remove utterly useless unused code
[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 "utils.h"
64 #include "selection.h"
65 #include "keyboard.h"
66 #include "editing.h"
67 #include "rgb_macros.h"
68 #include "control_point_dialog.h"
69 #include "editor_drag.h"
70 #include "automation_region_view.h"
71 #include "edit_note_dialog.h"
72 #include "mouse_cursors.h"
73 #include "editor_cursors.h"
74 #include "verbose_cursor.h"
75 #include "note.h"
76
77 #include "i18n.h"
78
79 using namespace std;
80 using namespace ARDOUR;
81 using namespace PBD;
82 using namespace Gtk;
83 using namespace Editing;
84 using Gtkmm2ext::Keyboard;
85
86 bool
87 Editor::mouse_frame (framepos_t& where, bool& in_track_canvas) const
88 {
89         /* gdk_window_get_pointer() has X11's XQueryPointer semantics in that it only
90            pays attentions to subwindows. this means that menu windows are ignored, and 
91            if the pointer is in a menu, the return window from the call will be the
92            the regular subwindow *under* the menu.
93
94            this matters quite a lot if the pointer is moving around in a menu that overlaps
95            the track canvas because we will believe that we are within the track canvas
96            when we are not. therefore, we track enter/leave events for the track canvas
97            and allow that to override the result of gdk_window_get_pointer().
98         */
99
100         if (!within_track_canvas) {
101                 return false;
102         }
103
104         int x, y;
105         Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->_track_canvas->get_window();
106
107         if (!canvas_window) {
108                 return false;
109         }
110
111         Glib::RefPtr<const Gdk::Window> pointer_window = Gdk::Display::get_default()->get_window_at_pointer (x, y);
112
113         if (!pointer_window) {
114                 return false;
115         }
116
117         if (pointer_window != canvas_window) {
118                 in_track_canvas = false;
119                 return false;
120         }
121
122         in_track_canvas = true;
123
124         GdkEvent event;
125         event.type = GDK_BUTTON_RELEASE;
126         event.button.x = x;
127         event.button.y = y;
128
129         where = window_event_sample (&event, 0, 0);
130
131         return true;
132 }
133
134 framepos_t
135 Editor::window_event_sample (GdkEvent const * event, double* pcx, double* pcy) const
136 {
137         ArdourCanvas::Duple d;
138
139         if (!gdk_event_get_coords (event, &d.x, &d.y)) {
140                 return 0;
141         }
142
143         /* adjust for scrolling (the canvas used by Ardour has global scroll
144          * disabled, so we have to do the adjustment explicitly).
145          */
146
147         d.translate (ArdourCanvas::Duple (horizontal_adjustment.get_value(), vertical_adjustment.get_value()));
148
149         /* event coordinates are in window units, so convert to canvas
150          */
151
152         d = _track_canvas->window_to_canvas (d);
153
154         if (pcx) {
155                 *pcx = d.x;
156         }
157
158         if (pcy) {
159                 *pcy = d.y;
160         }
161
162         return pixel_to_sample (d.x);
163 }
164
165 framepos_t
166 Editor::canvas_event_sample (GdkEvent const * event, double* pcx, double* pcy) const
167 {
168         double x;
169         double y;
170
171         /* event coordinates are already in canvas units */
172
173         if (!gdk_event_get_coords (event, &x, &y)) {
174                 cerr << "!NO c COORDS for event type " << event->type << endl;
175                 return 0;
176         }
177
178         if (pcx) {
179                 *pcx = x;
180         }
181
182         if (pcy) {
183                 *pcy = y;
184         }
185
186         /* note that pixel_to_sample_from_event() never returns less than zero, so even if the pixel
187            position is negative (as can be the case with motion events in particular),
188            the frame location is always positive.
189         */
190
191         return pixel_to_sample_from_event (x);
192 }
193
194 Gdk::Cursor*
195 Editor::which_grabber_cursor ()
196 {
197         Gdk::Cursor* c = _cursors->grabber;
198
199         if (_internal_editing) {
200                 switch (mouse_mode) {
201                 case MouseDraw:
202                         c = _cursors->midi_pencil;
203                         break;
204
205                 case MouseObject:
206                         c = _cursors->grabber_note;
207                         break;
208
209                 case MouseTimeFX:
210                         c = _cursors->midi_resize;
211                         break;
212                         
213                 case MouseRange:
214                         c = _cursors->grabber_note;
215                         break;
216
217                 default:
218                         break;
219                 }
220
221         } else {
222
223                 switch (_edit_point) {
224                 case EditAtMouse:
225                         c = _cursors->grabber_edit_point;
226                         break;
227                 default:
228                         boost::shared_ptr<Movable> m = _movable.lock();
229                         if (m && m->locked()) {
230                                 c = _cursors->speaker;
231                         }
232                         break;
233                 }
234         }
235
236         return c;
237 }
238
239 void
240 Editor::set_current_trimmable (boost::shared_ptr<Trimmable> t)
241 {
242         boost::shared_ptr<Trimmable> st = _trimmable.lock();
243
244         if (!st || st == t) {
245                 _trimmable = t;
246                 set_canvas_cursor ();
247         }
248 }
249
250 void
251 Editor::set_current_movable (boost::shared_ptr<Movable> m)
252 {
253         boost::shared_ptr<Movable> sm = _movable.lock();
254
255         if (!sm || sm != m) {
256                 _movable = m;
257                 set_canvas_cursor ();
258         }
259 }
260
261 void
262 Editor::set_canvas_cursor ()
263 {
264         switch (mouse_mode) {
265         case MouseRange:
266                 current_canvas_cursor = _cursors->selector;
267                 if (_internal_editing) {
268                         current_canvas_cursor = which_grabber_cursor();
269                 }
270                 break;
271
272         case MouseObject:
273                 current_canvas_cursor = which_grabber_cursor();
274                 break;
275
276         case MouseDraw:
277                 current_canvas_cursor = _cursors->midi_pencil;
278                 break;
279
280         case MouseGain:
281                 current_canvas_cursor = _cursors->cross_hair;
282                 break;
283
284         case MouseZoom:
285                 if (Keyboard::the_keyboard().key_is_down (GDK_Control_L)) {
286                         current_canvas_cursor = _cursors->zoom_out;
287                 } else {
288                         current_canvas_cursor = _cursors->zoom_in;
289                 }
290                 break;
291
292         case MouseTimeFX:
293                 current_canvas_cursor = _cursors->time_fx; // just use playhead
294                 break;
295
296         case MouseAudition:
297                 current_canvas_cursor = _cursors->speaker;
298                 break;
299         }
300
301         if (!_internal_editing) {
302                 switch (_join_object_range_state) {
303                 case JOIN_OBJECT_RANGE_NONE:
304                         break;
305                 case JOIN_OBJECT_RANGE_OBJECT:
306                         current_canvas_cursor = which_grabber_cursor ();
307                         break;
308                 case JOIN_OBJECT_RANGE_RANGE:
309                         current_canvas_cursor = _cursors->selector;
310                         break;
311                 }
312         }
313
314         /* up-down cursor as a cue that automation can be dragged up and down when in join object/range mode */
315         if (!_internal_editing && get_smart_mode() ) {
316
317                 double x, y;
318                 get_pointer_position (x, y);
319
320                 if (x >= 0 && y >= 0) {
321                         
322                         vector<ArdourCanvas::Item const *> items;
323
324                         /* Note how we choose a specific scroll group to get
325                          * items from. This could be problematic.
326                          */
327                         
328                         hv_scroll_group->add_items_at_point (ArdourCanvas::Duple (x,y), items);
329                         
330                         // first item will be the upper most 
331                         
332                         if (!items.empty()) {
333                                 const ArdourCanvas::Item* i = items.front();
334                                 
335                                 if (i && i->parent() && i->parent()->get_data (X_("timeselection"))) {
336                                         pair<TimeAxisView*, int> tvp = trackview_by_y_position (_last_motion_y);
337                                         if (dynamic_cast<AutomationTimeAxisView*> (tvp.first)) {
338                                                 current_canvas_cursor = _cursors->up_down;
339                                         }
340                                 }
341                         }
342                 }
343         }
344
345         set_canvas_cursor (current_canvas_cursor, true);
346 }
347
348 void
349 Editor::mouse_mode_object_range_toggled()
350 {
351         MouseMode m = mouse_mode;
352         
353         Glib::RefPtr<Action> act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-range"));
354         assert (act);
355         Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
356         assert (tact);
357
358         if (tact->get_active()) {
359                 m = MouseObject;  //Smart mode turned to ON, force editing to Object mode
360         }
361
362         set_mouse_mode(m, true);  //call this so the button styles can get updated
363 }
364
365 void
366 Editor::set_mouse_mode (MouseMode m, bool force)
367 {
368         if (_drags->active ()) {
369                 return;
370         }
371
372         if (!force && m == mouse_mode) {
373                 return;
374         }
375
376         Glib::RefPtr<Action> act;
377
378         switch (m) {
379         case MouseRange:
380                 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-range"));
381                 break;
382
383         case MouseObject:
384                 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-object"));
385                 break;
386
387         case MouseDraw:
388                 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-draw"));
389                 break;
390
391         case MouseGain:
392                 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-gain"));
393                 break;
394
395         case MouseZoom:
396                 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-zoom"));
397                 break;
398
399         case MouseTimeFX:
400                 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-timefx"));
401                 break;
402
403         case MouseAudition:
404                 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-audition"));
405                 break;
406         }
407
408         assert (act);
409
410         Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
411         assert (tact);
412
413         /* go there and back to ensure that the toggled handler is called to set up mouse_mode */
414         tact->set_active (false);
415         tact->set_active (true);
416
417         //NOTE:  this will result in a call to mouse_mode_toggled which does the heavy lifting
418 }
419
420 void
421 Editor::mouse_mode_toggled (MouseMode m)
422 {
423         Glib::RefPtr<Action> act;
424         Glib::RefPtr<ToggleAction> tact;
425
426         switch (m) {
427         case MouseRange:
428                 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-range"));
429                 break;
430
431         case MouseObject:
432                 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-object"));
433                 break;
434
435         case MouseDraw:
436                 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-draw"));
437                 break;
438
439         case MouseGain:
440                 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-gain"));
441                 break;
442
443         case MouseZoom:
444                 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-zoom"));
445                 break;
446
447         case MouseTimeFX:
448                 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-timefx"));
449                 break;
450
451         case MouseAudition:
452                 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-audition"));
453                 break;
454         }
455
456         assert (act);
457
458         tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
459         assert (tact);
460
461         if (!tact->get_active()) {
462                 /* this was just the notification that the old mode has been
463                  * left. we'll get called again with the new mode active in a
464                  * jiffy.
465                  */
466                 return;
467         }
468
469         switch (m) {
470         case MouseDraw:
471                 act = ActionManager::get_action (X_("MouseMode"), X_("toggle-internal-edit"));
472                 tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
473                 tact->set_active (true);
474                 break;
475         default:
476                 break;
477         }
478         
479         if (_session && mouse_mode == MouseAudition) {
480                 /* stop transport and reset default speed to avoid oddness with
481                    auditioning */
482                 _session->request_transport_speed (0.0, true);
483         }
484
485         mouse_mode = m;
486
487         instant_save ();
488
489         //TODO:  set button styles for smart buttons
490 /*
491         if ( smart_mode_action->get_active() ) {
492                 if( mouse_mode == MouseObject ) { //smart active and object active
493                         smart_mode_button.set_active(1);
494                         smart_mode_button.set_name("smart mode button");
495                         mouse_move_button.set_name("smart mode button");
496                 } else {  //smart active but object inactive
497                         smart_mode_button.set_active(0);
498                         smart_mode_button.set_name("smart mode button");
499                         mouse_move_button.set_name("mouse mode button");
500                 }
501         } else {
502                 smart_mode_button.set_active(0);
503                 smart_mode_button.set_name("mouse mode button");
504                 mouse_move_button.set_name("mouse mode button");
505         }
506 */
507         
508         set_canvas_cursor ();
509         set_gain_envelope_visibility ();
510         
511         update_time_selection_display ();
512
513         MouseModeChanged (); /* EMIT SIGNAL */
514 }
515
516 void
517 Editor::update_time_selection_display ()
518 {
519         if (smart_mode_action->get_active()) {
520                 /* not sure what to do here */
521                 if (mouse_mode == MouseObject) {
522                 } else {
523                 }
524         } else {
525                 switch (mouse_mode) {
526                 case MouseRange:
527                         selection->clear_objects ();
528                         break;
529                 default:
530                         selection->clear_time ();
531                         break;
532                 }
533         }
534 }
535
536 void
537 Editor::step_mouse_mode (bool next)
538 {
539         switch (current_mouse_mode()) {
540         case MouseObject:
541                 if (next) {
542                         if (Profile->get_sae()) {
543                                 set_mouse_mode (MouseZoom);
544                         } else {
545                                 set_mouse_mode (MouseRange);
546                         }
547                 } else {
548                         set_mouse_mode (MouseTimeFX);
549                 }
550                 break;
551
552         case MouseRange:
553                 if (next) set_mouse_mode (MouseDraw);
554                 else set_mouse_mode (MouseObject);
555                 break;
556
557         case MouseDraw:
558                 if (next) set_mouse_mode (MouseZoom);
559                 else set_mouse_mode (MouseRange);
560                 break;
561
562         case MouseZoom:
563                 if (next) {
564                         if (Profile->get_sae()) {
565                                 set_mouse_mode (MouseTimeFX);
566                         } else {
567                                 set_mouse_mode (MouseGain);
568                         }
569                 } else {
570                         if (Profile->get_sae()) {
571                                 set_mouse_mode (MouseObject);
572                         } else {
573                                 set_mouse_mode (MouseDraw);
574                         }
575                 }
576                 break;
577
578         case MouseGain:
579                 if (next) set_mouse_mode (MouseTimeFX);
580                 else set_mouse_mode (MouseZoom);
581                 break;
582
583         case MouseTimeFX:
584                 if (next) {
585                         set_mouse_mode (MouseAudition);
586                 } else {
587                         if (Profile->get_sae()) {
588                                 set_mouse_mode (MouseZoom);
589                         } else {
590                                 set_mouse_mode (MouseGain);
591                         }
592                 }
593                 break;
594
595         case MouseAudition:
596                 if (next) set_mouse_mode (MouseObject);
597                 else set_mouse_mode (MouseTimeFX);
598                 break;
599         }
600 }
601
602 bool
603 Editor::toggle_internal_editing_from_double_click (GdkEvent* event)
604 {
605         if (_drags->active()) {
606                 _drags->end_grab (event);
607         } 
608
609         ActionManager::do_action ("MouseMode", "toggle-internal-edit");
610
611         /* prevent reversion of edit cursor on button release */
612         
613         pre_press_cursor = 0;
614
615         return true;
616 }
617
618 void
619 Editor::button_selection (ArdourCanvas::Item* /*item*/, GdkEvent* event, ItemType item_type)
620 {
621         /* in object/audition/timefx/gain-automation mode,
622            any button press sets the selection if the object
623            can be selected. this is a bit of hack, because
624            we want to avoid this if the mouse operation is a
625            region alignment.
626
627            note: not dbl-click or triple-click
628
629            Also note that there is no region selection in internal edit mode, otherwise
630            for operations operating on the selection (e.g. cut) it is not obvious whether
631            to cut notes or regions.
632         */
633
634         MouseMode eff_mouse_mode = mouse_mode;
635
636         if (get_smart_mode() && eff_mouse_mode == MouseRange && event->button.button == 3 && item_type == RegionItem) {
637                 /* context clicks are always about object properties, even if
638                    we're in range mode within smart mode.
639                 */
640                 eff_mouse_mode = MouseObject;
641         }
642
643         if (((mouse_mode != MouseObject) &&
644              (mouse_mode != MouseAudition || item_type != RegionItem) &&
645              (mouse_mode != MouseTimeFX || item_type != RegionItem) &&
646              (mouse_mode != MouseGain) &&
647              (mouse_mode != MouseDraw)) ||
648             ((event->type != GDK_BUTTON_PRESS && event->type != GDK_BUTTON_RELEASE) || event->button.button > 3) ||
649             (internal_editing() && mouse_mode != MouseTimeFX)) {
650
651                 return;
652         }
653
654         if (event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) {
655
656                 if ((event->button.state & Keyboard::RelevantModifierKeyMask) && event->button.button != 1) {
657
658                         /* almost no selection action on modified button-2 or button-3 events */
659
660                         if (item_type != RegionItem && event->button.button != 2) {
661                                 return;
662                         }
663                 }
664         }
665
666         Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
667         bool press = (event->type == GDK_BUTTON_PRESS);
668
669         switch (item_type) {
670         case RegionItem:
671                 if (press) {
672                         if (eff_mouse_mode != MouseRange) {
673                                 set_selected_regionview_from_click (press, op);
674                         } else {
675                                 /* don't change the selection unless the
676                                    clicked track is not currently selected. if
677                                    so, "collapse" the selection to just this
678                                    track
679                                 */
680                                 if (!selection->selected (clicked_axisview)) {
681                                         set_selected_track_as_side_effect (Selection::Set);
682                                 }
683                         }
684                 } else {
685                         if (eff_mouse_mode != MouseRange) {
686                                 set_selected_regionview_from_click (press, op);
687                         }
688                 }
689                 break;
690
691         case RegionViewNameHighlight:
692         case RegionViewName:
693         case LeftFrameHandle:
694         case RightFrameHandle:
695                 if (eff_mouse_mode != MouseRange) {
696                         set_selected_regionview_from_click (press, op);
697                 } else if (event->type == GDK_BUTTON_PRESS) {
698                         set_selected_track_as_side_effect (op);
699                 }
700                 break;
701
702         case FadeInHandleItem:
703         case FadeInTrimHandleItem:
704         case FadeInItem:
705         case FadeOutHandleItem:
706         case FadeOutTrimHandleItem:
707         case FadeOutItem:
708         case StartCrossFadeItem:
709         case EndCrossFadeItem:
710                 if (eff_mouse_mode != MouseRange) {
711                         cerr << "Should be setting selected regionview\n";
712                         set_selected_regionview_from_click (press, op);
713                 } else if (event->type == GDK_BUTTON_PRESS) {
714                         set_selected_track_as_side_effect (op);
715                 }
716                 break;
717
718         case ControlPointItem:
719                 set_selected_track_as_side_effect (op);
720                 if (eff_mouse_mode != MouseRange) {
721                         set_selected_control_point_from_click (press, op);
722                 }
723                 break;
724
725         case StreamItem:
726                 /* for context click, select track */
727                 if (event->button.button == 3) {
728                         selection->clear_tracks ();
729                         set_selected_track_as_side_effect (op);
730                 }
731                 break;
732
733         case AutomationTrackItem:
734                 set_selected_track_as_side_effect (op);
735                 break;
736
737         default:
738                 break;
739         }
740 }
741
742 bool
743 Editor::button_press_handler_1 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
744 {
745         /* single mouse clicks on any of these item types operate
746            independent of mouse mode, mostly because they are
747            not on the main track canvas or because we want
748            them to be modeless.
749         */
750
751         switch (item_type) {
752         case PlayheadCursorItem:
753                 _drags->set (new CursorDrag (this, *playhead_cursor, true), event);
754                 return true;
755
756         case MarkerItem:
757                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
758                         hide_marker (item, event);
759                 } else {
760                         _drags->set (new MarkerDrag (this, item), event);
761                 }
762                 return true;
763
764         case TempoMarkerItem:
765         {
766                 TempoMarker* m = reinterpret_cast<TempoMarker*> (item->get_data ("marker"));
767                 assert (m);
768                 _drags->set (
769                         new TempoMarkerDrag (
770                                 this,
771                                 item,
772                                 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
773                                 ),
774                         event
775                         );
776                 return true;
777         }
778
779         case MeterMarkerItem:
780         {
781                 MeterMarker* m = reinterpret_cast<MeterMarker*> (item->get_data ("marker"));
782                 assert (m);
783                 _drags->set (
784                         new MeterMarkerDrag (
785                                 this,
786                                 item,
787                                 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
788                                 ),
789                         event
790                         );
791                 return true;
792         }
793
794         case VideoBarItem:
795                 _drags->set (new VideoTimeLineDrag (this, item), event);
796                 return true;
797                 break;
798
799         case MarkerBarItem:
800         case TempoBarItem:
801         case MeterBarItem:
802                 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
803                         _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
804                 }
805                 return true;
806                 break;
807
808
809         case RangeMarkerBarItem:
810                 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
811                         _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
812                 } else {
813                         _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateRangeMarker), event);
814                 }
815                 return true;
816                 break;
817
818         case CdMarkerBarItem:
819                 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
820                         _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
821                 } else {
822                         _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateCDMarker), event);
823                 }
824                 return true;
825                 break;
826
827         case TransportMarkerBarItem:
828                 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
829                         _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
830                 } else {
831                         _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateTransportMarker), event);
832                 }
833                 return true;
834                 break;
835
836         default:
837                 break;
838         }
839
840         if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
841                 /* special case: allow trim of range selections in joined object mode;
842                    in theory eff should equal MouseRange in this case, but it doesn't
843                    because entering the range selection canvas item results in entered_regionview
844                    being set to 0, so update_join_object_range_location acts as if we aren't
845                    over a region.
846                 */
847                 if (item_type == StartSelectionTrimItem) {
848                         _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
849                 } else if (item_type == EndSelectionTrimItem) {
850                         _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
851                 }
852         }
853
854         Editing::MouseMode eff = effective_mouse_mode ();
855
856         /* special case: allow drag of region fade in/out in object mode with join object/range enabled */
857         if (item_type == FadeInHandleItem || item_type == FadeOutHandleItem) {
858                 eff = MouseObject;
859         }
860
861         /* there is no Range mode when in internal edit mode */
862         if (eff == MouseRange && internal_editing()) {
863                 eff = MouseObject;
864         }
865
866         switch (eff) {
867         case MouseRange:
868                 switch (item_type) {
869                 case StartSelectionTrimItem:
870                         _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
871                         break;
872
873                 case EndSelectionTrimItem:
874                         _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
875                         break;
876
877                 case SelectionItem:
878                         if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
879                                 start_selection_grab (item, event);
880                                 return true;
881                         } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
882                                 /* grab selection for moving */
883                                 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionMove), event);
884                         } else {
885                                 double const y = event->button.y;
886                                 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
887                                 if (tvp.first) {
888                                         AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
889                                         if ( get_smart_mode() && atv) {
890                                                 /* smart "join" mode: drag automation */
891                                                 _drags->set (new AutomationRangeDrag (this, atv, selection->time), event, _cursors->up_down);
892                                         } else {
893                                                 /* this was debated, but decided the more common action was to
894                                                    make a new selection */
895                                                 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
896                                         }
897                                 }
898                         }
899                         break;
900
901                 case StreamItem:
902                         if (internal_editing()) {
903                                 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
904                                         _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
905                                         return true;
906                                 } 
907                         } else {
908                                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::RangeSelectModifier)) {
909                                         _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionExtend), event);
910                                 } else {
911                                         _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
912                                 }
913                                 return true;
914                         }
915                         break;
916
917                 case RegionViewNameHighlight:
918                         if (!clicked_regionview->region()->locked()) {
919                                 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
920                                 return true;
921                         }
922                         break;
923
924                 default:
925                         if (!internal_editing()) {
926                                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::RangeSelectModifier)) {
927                                         _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionExtend), event);
928                                 } else {
929                                         _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
930                                 }
931                         }
932                 }
933                 return true;
934                 break;
935
936         case MouseDraw:
937                 switch (item_type) {
938                 case NoteItem:
939                         /* Existing note: allow trimming/motion */
940                         if (internal_editing()) {
941                                 /* trim notes if we're in internal edit mode and near the ends of the note */
942                                 NoteBase* cn = reinterpret_cast<NoteBase*>(item->get_data ("notebase"));
943                                 assert (cn);
944                                 if (cn->big_enough_to_trim() && cn->mouse_near_ends()) {
945                                         _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
946                                 } else {
947                                         _drags->set (new NoteDrag (this, item), event);
948                                 }
949                                 return true;
950                         } 
951                         break;
952                 case StreamItem:
953                         if (internal_editing()) {
954                                 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
955                                         _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
956                                 }
957                                 return true;
958                         }
959                         break;
960
961                 default:
962                         break;
963                 }
964                 break;
965
966         case MouseObject:
967                 switch (item_type) {
968                 case NoteItem:
969                         /* Existing note: allow trimming/motion */
970                         if (internal_editing()) {
971                                 NoteBase* cn = reinterpret_cast<NoteBase*> (item->get_data ("notebase"));
972                                 assert (cn);
973                                 if (cn->big_enough_to_trim() && cn->mouse_near_ends()) {
974                                         _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
975                                 } else {
976                                         _drags->set (new NoteDrag (this, item), event);
977                                 }
978                                 return true;
979                         }
980                         break;
981
982                 default:
983                         break;
984                 }
985
986                 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)) &&
987                     event->type == GDK_BUTTON_PRESS) {
988
989                         _drags->set (new EditorRubberbandSelectDrag (this, item), event);
990
991                 } else if (event->type == GDK_BUTTON_PRESS) {
992
993                         switch (item_type) {
994                         case FadeInHandleItem:
995                         {
996                                 _drags->set (new FadeInDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), selection->regions), event, _cursors->fade_in);
997                                 return true;
998                         }
999
1000                         case FadeOutHandleItem:
1001                         {
1002                                 _drags->set (new FadeOutDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), selection->regions), event, _cursors->fade_out);
1003                                 return true;
1004                         }
1005
1006                         case StartCrossFadeItem:
1007                         case EndCrossFadeItem:
1008                                 /* we might allow user to grab inside the fade to trim a region with preserve_fade_anchor.  for not this is not fully implemented */ 
1009 //                              if (!clicked_regionview->region()->locked()) {
1010 //                                      _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer(), true), event);
1011 //                                      return true;
1012 //                              }
1013                                 break;
1014
1015                         case FeatureLineItem:
1016                         {
1017                                 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier)) {
1018                                         remove_transient(item);
1019                                         return true;
1020                                 }
1021
1022                                 _drags->set (new FeatureLineDrag (this, item), event);
1023                                 return true;
1024                                 break;
1025                         }
1026
1027                         case RegionItem:
1028                                 if (dynamic_cast<AutomationRegionView*> (clicked_regionview)) {
1029                                         /* click on an automation region view; do nothing here and let the ARV's signal handler
1030                                            sort it out.
1031                                         */
1032                                         break;
1033                                 }
1034
1035                                 if (internal_editing ()) {
1036                                         break;
1037                                 }
1038
1039                                 /* click on a normal region view */
1040                                 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
1041                                         add_region_copy_drag (item, event, clicked_regionview);
1042                                 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
1043                                         add_region_brush_drag (item, event, clicked_regionview);
1044                                 } else {
1045                                         add_region_drag (item, event, clicked_regionview);
1046                                 }
1047
1048
1049 //                              if (!internal_editing() && (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE && !selection->regions.empty())) {
1050 //                                      _drags->add (new SelectionDrag (this, clicked_axisview->get_selection_rect (clicked_selection)->rect, SelectionDrag::SelectionMove));
1051 //                              }
1052
1053                                 _drags->start_grab (event);
1054                                 return true;
1055                                 break;
1056
1057                         case RegionViewNameHighlight:
1058                         case LeftFrameHandle:
1059                         case RightFrameHandle:
1060                                 if (!clicked_regionview->region()->locked()) {
1061                                         _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1062                                         return true;
1063                                 }
1064                                 break;
1065
1066                         case FadeInTrimHandleItem:
1067                         case FadeOutTrimHandleItem:
1068                                 if (!clicked_regionview->region()->locked()) {
1069                                         _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer(), true), event);
1070                                         return true;
1071                                 }
1072                                 break;
1073
1074                         case RegionViewName:
1075                         {
1076                                 /* rename happens on edit clicks */
1077                                 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer()), event);
1078                                 return true;
1079                                 break;
1080                         }
1081
1082                         case ControlPointItem:
1083                                 _drags->set (new ControlPointDrag (this, item), event);
1084                                 return true;
1085                                 break;
1086
1087                         case AutomationLineItem:
1088                                 _drags->set (new LineDrag (this, item), event);
1089                                 return true;
1090                                 break;
1091
1092                         case StreamItem:
1093                                 if (internal_editing()) {
1094                                         if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
1095                                                 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
1096                                         }
1097                                         return true;
1098                                 } else {
1099                                         _drags->set (new EditorRubberbandSelectDrag (this, item), event);
1100                                 }
1101                                 break;
1102
1103                         case AutomationTrackItem:
1104                         {
1105                                 TimeAxisView* parent = clicked_axisview->get_parent ();
1106                                 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (clicked_axisview);
1107                                 assert (atv);
1108                                 if (parent && dynamic_cast<MidiTimeAxisView*> (parent) && atv->show_regions ()) {
1109
1110                                         RouteTimeAxisView* p = dynamic_cast<RouteTimeAxisView*> (parent);
1111                                         assert (p);
1112                                         boost::shared_ptr<Playlist> pl = p->track()->playlist ();
1113                                         if (pl->n_regions() == 0) {
1114                                                 /* Parent has no regions; create one so that we have somewhere to put automation */
1115                                                 _drags->set (new RegionCreateDrag (this, item, parent), event);
1116                                         } else {
1117                                                 /* See if there's a region before the click that we can extend, and extend it if so */
1118                                                 framepos_t const t = canvas_event_sample (event);
1119                                                 boost::shared_ptr<Region> prev = pl->find_next_region (t, End, -1);
1120                                                 if (!prev) {
1121                                                         _drags->set (new RegionCreateDrag (this, item, parent), event);
1122                                                 } else {
1123                                                         prev->set_length (t - prev->position ());
1124                                                 }
1125                                         }
1126                                 } else {
1127                                         /* rubberband drag to select automation points */
1128                                         _drags->set (new EditorRubberbandSelectDrag (this, item), event);
1129                                 }
1130                                 break;
1131                         }
1132
1133                         case SelectionItem:
1134                         {
1135                                 if ( get_smart_mode() ) {
1136                                         /* we're in "smart" joined mode, and we've clicked on a Selection */
1137                                         double const y = event->button.y;
1138                                         pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
1139                                         if (tvp.first) {
1140                                                 /* if we're over an automation track, start a drag of its data */
1141                                                 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
1142                                                 if (atv) {
1143                                                         _drags->set (new AutomationRangeDrag (this, atv, selection->time), event, _cursors->up_down);
1144                                                 }
1145
1146                                                 /* if we're over a track and a region, and in the `object' part of a region,
1147                                                    put a selection around the region and drag both
1148                                                 */
1149 /*                                              RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1150                                                 if (rtv && _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
1151                                                         boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track> (rtv->route ());
1152                                                         if (t) {
1153                                                                 boost::shared_ptr<Playlist> pl = t->playlist ();
1154                                                                 if (pl) {
1155
1156                                                                         boost::shared_ptr<Region> r = pl->top_region_at (canvas_event_sample (event));
1157                                                                         if (r) {
1158                                                                                 RegionView* rv = rtv->view()->find_view (r);
1159                                                                                 clicked_selection = select_range (rv->region()->position(), 
1160                                                                                                                   rv->region()->last_frame()+1);
1161                                                                                 _drags->add (new SelectionDrag (this, item, SelectionDrag::SelectionMove));
1162                                                                                 list<RegionView*> rvs;
1163                                                                                 rvs.push_back (rv);
1164                                                                                 _drags->add (new RegionMoveDrag (this, item, rv, rvs, false, false));
1165                                                                                 _drags->start_grab (event);
1166                                                                                 return true;
1167                                                                         }
1168                                                                 }
1169                                                         }
1170                                                 }
1171 */
1172                                         }
1173                                 }
1174                                 break;
1175                         }
1176
1177                         case MarkerBarItem:
1178
1179                                 break;
1180
1181                         default:
1182                                 break;
1183                         }
1184                 }
1185                 return true;
1186                 break;
1187
1188         case MouseGain:
1189                 switch (item_type) {
1190                 case GainLineItem:
1191                         _drags->set (new LineDrag (this, item), event);
1192                         return true;
1193
1194                 case ControlPointItem:
1195                         _drags->set (new ControlPointDrag (this, item), event);
1196                         return true;
1197                         break;
1198
1199                 case SelectionItem:
1200                 {
1201                         AudioRegionView* arv = dynamic_cast<AudioRegionView *> (clicked_regionview);
1202                         if (arv) {
1203                                 _drags->set (new AutomationRangeDrag (this, arv, selection->time), event, _cursors->up_down);
1204                                 _drags->start_grab (event);
1205                         }
1206                         return true;
1207                         break;
1208                 }
1209
1210                 case AutomationLineItem:
1211                         _drags->set (new LineDrag (this, item), event);
1212                         break;
1213                         
1214                 default:
1215                         break;
1216                 }
1217                 return true;
1218                 break;
1219
1220         case MouseZoom:
1221                 if (event->type == GDK_BUTTON_PRESS) {
1222                         _drags->set (new MouseZoomDrag (this, item), event);
1223                 }
1224
1225                 return true;
1226                 break;
1227
1228         case MouseTimeFX:
1229                 if (internal_editing() && item_type == NoteItem ) {
1230                         /* drag notes if we're in internal edit mode */
1231                         NoteBase* cn = reinterpret_cast<NoteBase*>(item->get_data ("notebase"));
1232                         assert (cn);
1233                         if (cn->big_enough_to_trim()) {
1234                                 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
1235                         }
1236                         return true;
1237                 } else if (clicked_regionview) {
1238                         /* do time-FX  */
1239                         _drags->set (new TimeFXDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1240                         return true;
1241                 }
1242                 break;
1243
1244         case MouseAudition:
1245                 _drags->set (new ScrubDrag (this, item), event);
1246                 scrub_reversals = 0;
1247                 scrub_reverse_distance = 0;
1248                 last_scrub_x = event->button.x;
1249                 scrubbing_direction = 0;
1250                 set_canvas_cursor (_cursors->transparent);
1251                 return true;
1252                 break;
1253
1254         default:
1255                 break;
1256         }
1257
1258         return false;
1259 }
1260
1261 bool
1262 Editor::button_press_handler_2 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1263 {
1264         Editing::MouseMode const eff = effective_mouse_mode ();
1265         switch (eff) {
1266         case MouseObject:
1267                 switch (item_type) {
1268                 case RegionItem:
1269                         if (internal_editing ()) {
1270                                 /* no region drags in internal edit mode */
1271                                 return false;
1272                         }
1273
1274                         if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
1275                                 add_region_copy_drag (item, event, clicked_regionview);
1276                         } else {
1277                                 add_region_drag (item, event, clicked_regionview);
1278                         }
1279                         _drags->start_grab (event);
1280                         return true;
1281                         break;
1282                 case ControlPointItem:
1283                         _drags->set (new ControlPointDrag (this, item), event);
1284                         return true;
1285                         break;
1286
1287                 default:
1288                         break;
1289                 }
1290
1291                 switch (item_type) {
1292                 case RegionViewNameHighlight:
1293                         _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1294                         return true;
1295                         break;
1296
1297                 case LeftFrameHandle:
1298                 case RightFrameHandle:
1299                         if (!internal_editing ()) {
1300                                 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1301                         }
1302                         return true;
1303                         break;
1304
1305                 case RegionViewName:
1306                         _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer()), event);
1307                         return true;
1308                         break;
1309
1310                 default:
1311                         break;
1312                 }
1313
1314                 break;
1315
1316         case MouseDraw:
1317                 return false;
1318
1319         case MouseRange:
1320                 /* relax till release */
1321                 return true;
1322                 break;
1323
1324
1325         case MouseZoom:
1326                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1327                         temporal_zoom_to_frame (false, canvas_event_sample (event));
1328                 } else {
1329                         temporal_zoom_to_frame (true, canvas_event_sample(event));
1330                 }
1331                 return true;
1332                 break;
1333
1334         default:
1335                 break;
1336         }
1337
1338         return false;
1339 }
1340    
1341 bool
1342 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1343 {
1344         if (event->type == GDK_2BUTTON_PRESS) {
1345                 _drags->mark_double_click ();
1346                 gdk_pointer_ungrab (GDK_CURRENT_TIME);
1347                 return true;
1348         }
1349
1350         if (event->type != GDK_BUTTON_PRESS) {
1351                 return false;
1352         }
1353
1354         pre_press_cursor = current_canvas_cursor;
1355
1356         _track_canvas->grab_focus();
1357
1358         if (_session && _session->actively_recording()) {
1359                 return true;
1360         }
1361
1362         if (internal_editing()) {
1363                 bool leave_internal_edit_mode = false;
1364
1365                 switch (item_type) {
1366                 case NoteItem:
1367                         break;
1368
1369                 case RegionItem:
1370                         if (!dynamic_cast<MidiRegionView*> (clicked_regionview) && !dynamic_cast<AutomationRegionView*> (clicked_regionview)) {
1371                                 leave_internal_edit_mode = true;
1372                         }
1373                         break;
1374
1375                 case PlayheadCursorItem:
1376                 case MarkerItem:
1377                 case TempoMarkerItem:
1378                 case MeterMarkerItem:
1379                 case MarkerBarItem:
1380                 case TempoBarItem:
1381                 case MeterBarItem:
1382                 case RangeMarkerBarItem:
1383                 case CdMarkerBarItem:
1384                 case TransportMarkerBarItem:
1385                 case StreamItem:
1386                         /* button press on these events never does anything to
1387                            change the editing mode.
1388                         */
1389                         break;
1390
1391                 default:
1392                         break;
1393                 }
1394                 
1395                 if (leave_internal_edit_mode) {
1396                         ActionManager::do_action ("MouseMode", "toggle-internal-edit");
1397                 }
1398         }
1399
1400         button_selection (item, event, item_type);
1401
1402         if (!_drags->active () &&
1403             (Keyboard::is_delete_event (&event->button) ||
1404              Keyboard::is_context_menu_event (&event->button) ||
1405              Keyboard::is_edit_event (&event->button))) {
1406
1407                 /* handled by button release */
1408                 return true;
1409         }
1410
1411         //not rolling, range mode click + join_play_range :  locate the PH here
1412         if ( !_drags->active () && !_session->transport_rolling() && ( effective_mouse_mode() == MouseRange ) && Config->get_always_play_range() ) {
1413                 framepos_t where = canvas_event_sample (event);
1414                 snap_to(where);
1415                 _session->request_locate (where, false);
1416         }
1417
1418         switch (event->button.button) {
1419         case 1:
1420                 return button_press_handler_1 (item, event, item_type);
1421                 break;
1422
1423         case 2:
1424                 return button_press_handler_2 (item, event, item_type);
1425                 break;
1426
1427         case 3:
1428                 break;
1429
1430         default:
1431                 return button_press_dispatch (&event->button);
1432                 break;
1433
1434         }
1435
1436         return false;
1437 }
1438
1439 bool
1440 Editor::button_press_dispatch (GdkEventButton* ev)
1441 {
1442         /* this function is intended only for buttons 4 and above.
1443          */
1444
1445         Gtkmm2ext::MouseButton b (ev->state, ev->button);
1446         return button_bindings->activate (b, Gtkmm2ext::Bindings::Press);
1447 }
1448
1449 bool
1450 Editor::button_release_dispatch (GdkEventButton* ev)
1451 {
1452         /* this function is intended only for buttons 4 and above.
1453          */
1454
1455         Gtkmm2ext::MouseButton b (ev->state, ev->button);
1456         return button_bindings->activate (b, Gtkmm2ext::Bindings::Release);
1457 }
1458
1459 bool
1460 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1461 {
1462         framepos_t where = canvas_event_sample (event);
1463         AutomationTimeAxisView* atv = 0;
1464
1465         if (pre_press_cursor) {
1466                 set_canvas_cursor (pre_press_cursor);
1467                 pre_press_cursor = 0;
1468         }
1469
1470         /* no action if we're recording */
1471
1472         if (_session && _session->actively_recording()) {
1473                 return true;
1474         }
1475
1476         /* see if we're finishing a drag */
1477
1478         bool were_dragging = false;
1479         if (_drags->active ()) {
1480                 bool const r = _drags->end_grab (event);
1481                 if (r) {
1482                         /* grab dragged, so do nothing else */
1483                         return true;
1484                 }
1485
1486                 were_dragging = true;
1487         }
1488
1489         update_region_layering_order_editor ();
1490
1491         /* edit events get handled here */
1492
1493         if (!_drags->active () && Keyboard::is_edit_event (&event->button)) {
1494                 switch (item_type) {
1495                 case RegionItem:
1496                         show_region_properties ();
1497                         break;
1498
1499                 case TempoMarkerItem: {
1500                         Marker* marker;
1501                         TempoMarker* tempo_marker;
1502                         
1503                         if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
1504                                 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
1505                                 /*NOTREACHED*/
1506                         }
1507                         
1508                         if ((tempo_marker = dynamic_cast<TempoMarker*> (marker)) == 0) {
1509                                 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
1510                                 /*NOTREACHED*/
1511                         }
1512                         
1513                         edit_tempo_marker (*tempo_marker);
1514                         break;
1515                 }
1516
1517                 case MeterMarkerItem: {
1518                         Marker* marker;
1519                         MeterMarker* meter_marker;
1520                         
1521                         if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
1522                                 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
1523                                 /*NOTREACHED*/
1524                         }
1525                         
1526                         if ((meter_marker = dynamic_cast<MeterMarker*> (marker)) == 0) {
1527                                 fatal << _("programming error: marker for meter is not a meter marker!") << endmsg;
1528                                 /*NOTREACHED*/
1529                         }
1530                         edit_meter_marker (*meter_marker);
1531                         break;
1532                 }
1533
1534                 case RegionViewName:
1535                         if (clicked_regionview->name_active()) {
1536                                 return mouse_rename_region (item, event);
1537                         }
1538                         break;
1539
1540                 case ControlPointItem:
1541                         edit_control_point (item);
1542                         break;
1543
1544                 default:
1545                         break;
1546                 }
1547                 return true;
1548         }
1549
1550         /* context menu events get handled here */
1551         if (Keyboard::is_context_menu_event (&event->button)) {
1552
1553                 context_click_event = *event;
1554
1555                 if (!_drags->active ()) {
1556
1557                         /* no matter which button pops up the context menu, tell the menu
1558                            widget to use button 1 to drive menu selection.
1559                         */
1560
1561                         switch (item_type) {
1562                         case FadeInItem:
1563                         case FadeInHandleItem:
1564                         case FadeInTrimHandleItem:
1565                         case StartCrossFadeItem:
1566                                 popup_xfade_in_context_menu (1, event->button.time, item, item_type);
1567                                 break;
1568
1569                         case FadeOutItem:
1570                         case FadeOutHandleItem:
1571                         case FadeOutTrimHandleItem:
1572                         case EndCrossFadeItem:
1573                                 popup_xfade_out_context_menu (1, event->button.time, item, item_type);
1574                                 break;
1575
1576                         case StreamItem:
1577                                 popup_track_context_menu (1, event->button.time, item_type, false);
1578                                 break;
1579
1580                         case RegionItem:
1581                         case RegionViewNameHighlight:
1582                         case LeftFrameHandle:
1583                         case RightFrameHandle:
1584                         case RegionViewName:
1585                                 popup_track_context_menu (1, event->button.time, item_type, false);
1586                                 break;
1587
1588                         case SelectionItem:
1589                                 popup_track_context_menu (1, event->button.time, item_type, true);
1590                                 break;
1591                                 
1592                         case AutomationTrackItem:
1593                                 popup_track_context_menu (1, event->button.time, item_type, false);
1594                                 break;
1595
1596                         case MarkerBarItem:
1597                         case RangeMarkerBarItem:
1598                         case TransportMarkerBarItem:
1599                         case CdMarkerBarItem:
1600                         case TempoBarItem:
1601                         case MeterBarItem:
1602                         case VideoBarItem:
1603                                 popup_ruler_menu (where, item_type);
1604                                 break;
1605
1606                         case MarkerItem:
1607                                 marker_context_menu (&event->button, item);
1608                                 break;
1609
1610                         case TempoMarkerItem:
1611                                 tempo_or_meter_marker_context_menu (&event->button, item);
1612                                 break;
1613
1614                         case MeterMarkerItem:
1615                                 tempo_or_meter_marker_context_menu (&event->button, item);
1616                                 break;
1617
1618                         case CrossfadeViewItem:
1619                                 popup_track_context_menu (1, event->button.time, item_type, false);
1620                                 break;
1621
1622                         case ControlPointItem:
1623                                 popup_control_point_context_menu (item, event);
1624                                 break;
1625
1626                         default:
1627                                 break;
1628                         }
1629
1630                         return true;
1631                 }
1632         }
1633
1634         /* delete events get handled here */
1635
1636         Editing::MouseMode const eff = effective_mouse_mode ();
1637
1638         if (!_drags->active () && Keyboard::is_delete_event (&event->button)) {
1639
1640                 switch (item_type) {
1641                 case TempoMarkerItem:
1642                         remove_tempo_marker (item);
1643                         break;
1644
1645                 case MeterMarkerItem:
1646                         remove_meter_marker (item);
1647                         break;
1648
1649                 case MarkerItem:
1650                         remove_marker (*item, event);
1651                         break;
1652
1653                 case RegionItem:
1654                         if (eff == MouseObject) {
1655                                 remove_clicked_region ();
1656                         }
1657                         break;
1658
1659                 case ControlPointItem:
1660                         remove_control_point (item);
1661                         break;
1662
1663                 case NoteItem:
1664                         remove_midi_note (item, event);
1665                         break;
1666
1667                 default:
1668                         break;
1669                 }
1670                 return true;
1671         }
1672
1673         switch (event->button.button) {
1674         case 1:
1675
1676                 switch (item_type) {
1677                 /* see comments in button_press_handler */
1678                 case PlayheadCursorItem:
1679                 case MarkerItem:
1680                 case GainLineItem:
1681                 case AutomationLineItem:
1682                 case StartSelectionTrimItem:
1683                 case EndSelectionTrimItem:
1684                         return true;
1685
1686                 case MarkerBarItem:
1687                         if (!_dragging_playhead) {
1688                                 snap_to_with_modifier (where, event, 0, true);
1689                                 mouse_add_new_marker (where);
1690                         }
1691                         return true;
1692
1693                 case CdMarkerBarItem:
1694                         if (!_dragging_playhead) {
1695                                 // if we get here then a dragged range wasn't done
1696                                 snap_to_with_modifier (where, event, 0, true);
1697                                 mouse_add_new_marker (where, true);
1698                         }
1699                         return true;
1700
1701                 case TempoBarItem:
1702                         if (!_dragging_playhead) {
1703                                 snap_to_with_modifier (where, event);
1704                                 mouse_add_new_tempo_event (where);
1705                         }
1706                         return true;
1707
1708                 case MeterBarItem:
1709                         if (!_dragging_playhead) {
1710                                 mouse_add_new_meter_event (pixel_to_sample (event->button.x));
1711                         }
1712                         return true;
1713                         break;
1714
1715                 default:
1716                         break;
1717                 }
1718
1719                 switch (eff) {
1720                 case MouseObject:
1721                         switch (item_type) {
1722                         case AutomationTrackItem:
1723                                 atv = dynamic_cast<AutomationTimeAxisView*>(clicked_axisview);
1724                                 if (atv) {
1725                                         bool with_guard_points = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
1726                                         atv->add_automation_event (event, where, event->button.y, with_guard_points);
1727                                 }
1728                                 return true;
1729                                 break;
1730                         default:
1731                                 break;
1732                         }
1733                         break;
1734
1735                 case MouseGain:
1736                         switch (item_type) {
1737                         case RegionItem:
1738                         {
1739                                 /* check that we didn't drag before releasing, since
1740                                    its really annoying to create new control
1741                                    points when doing this.
1742                                 */
1743                                 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (clicked_regionview);
1744                                 if (!were_dragging && arv) {
1745                                         bool with_guard_points = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
1746                                         arv->add_gain_point_event (item, event, with_guard_points);
1747                                 }
1748                                 return true;
1749                                 break;
1750                         }
1751
1752                         case AutomationTrackItem: {
1753                                 bool with_guard_points = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
1754                                 dynamic_cast<AutomationTimeAxisView*>(clicked_axisview)->
1755                                         add_automation_event (event, where, event->button.y, with_guard_points);
1756                                 return true;
1757                                 break;
1758                         }
1759                         default:
1760                                 break;
1761                         }
1762                         break;
1763
1764                 case MouseAudition:
1765                         set_canvas_cursor (current_canvas_cursor);
1766                         if (scrubbing_direction == 0) {
1767                                 /* no drag, just a click */
1768                                 switch (item_type) {
1769                                 case RegionItem:
1770                                         play_selected_region ();
1771                                         break;
1772                                 default:
1773                                         break;
1774                                 }
1775                         } else {
1776                                 /* make sure we stop */
1777                                 _session->request_transport_speed (0.0);
1778                         }
1779                         break;
1780
1781                 default:
1782                         break;
1783
1784                 }
1785
1786                 /* do any (de)selection operations that should occur on button release */
1787                 button_selection (item, event, item_type);
1788                 return true;
1789                 break;
1790
1791
1792         case 2:
1793                 switch (eff) {
1794
1795                 case MouseObject:
1796                         switch (item_type) {
1797                         case RegionItem:
1798                                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1799                                         raise_region ();
1800                                 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier|Keyboard::SecondaryModifier))) {
1801                                         lower_region ();
1802                                 } else {
1803                                         // Button2 click is unused
1804                                 }
1805                                 return true;
1806
1807                                 break;
1808
1809                         default:
1810                                 break;
1811                         }
1812                         break;
1813
1814                 case MouseDraw:
1815                         return true;
1816                         
1817                 case MouseRange:
1818                         // x_style_paste (where, 1.0);
1819                         return true;
1820                         break;
1821
1822                 default:
1823                         break;
1824                 }
1825
1826                 break;
1827
1828         case 3:
1829                 break;
1830
1831         default:
1832                 break;
1833         }
1834
1835         return false;
1836 }
1837
1838 bool
1839 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1840 {
1841         ControlPoint* cp;
1842         Marker * marker;
1843         double fraction;
1844         bool ret = true;
1845
1846         switch (item_type) {
1847         case ControlPointItem:
1848                 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1849                         cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1850                         cp->show ();
1851
1852                         double at_x, at_y;
1853                         at_x = cp->get_x();
1854                         at_y = cp->get_y ();
1855                         cp->i2w (at_x, at_y);
1856                         at_x += 10.0;
1857                         at_y += 10.0;
1858
1859                         fraction = 1.0 - (cp->get_y() / cp->line().height());
1860
1861                         if (is_drawable() && !_drags->active ()) {
1862                                 set_canvas_cursor (_cursors->fader);
1863                         }
1864
1865                         _verbose_cursor->set (cp->line().get_verbose_cursor_string (fraction), at_x, at_y);
1866                         _verbose_cursor->show ();
1867                 }
1868                 break;
1869
1870         case GainLineItem:
1871                 if (mouse_mode == MouseGain) {
1872                         ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1873                         if (line) {
1874                                 line->set_outline_color (ARDOUR_UI::config()->get_canvasvar_EnteredGainLine());
1875                         }
1876                         if (is_drawable()) {
1877                                 set_canvas_cursor (_cursors->fader);
1878                         }
1879                 }
1880                 break;
1881
1882         case AutomationLineItem:
1883                 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1884                         ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1885                         if (line) {
1886                                 line->set_outline_color (ARDOUR_UI::config()->get_canvasvar_EnteredAutomationLine());
1887                         }
1888                         if (is_drawable()) {
1889                                 set_canvas_cursor (_cursors->fader);
1890                         }
1891                 }
1892                 break;
1893
1894         case RegionViewNameHighlight:
1895                 if (is_drawable() && effective_mouse_mode() == MouseObject && entered_regionview) {
1896                         set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1897                         _over_region_trim_target = true;
1898                 }
1899                 break;
1900
1901         case LeftFrameHandle:
1902         case RightFrameHandle:
1903                 if (is_drawable() && effective_mouse_mode() == MouseObject && !internal_editing() && entered_regionview) {
1904                         set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1905                 }
1906                 break;
1907
1908         case RegionItem:
1909                 switch (effective_mouse_mode()) {
1910                 case MouseRange:
1911                         set_canvas_cursor (_cursors->selector);
1912                         break;
1913                 default:
1914                         set_canvas_cursor (which_grabber_cursor());
1915                         break;
1916                 }
1917                 break;  
1918
1919         case StartSelectionTrimItem:
1920                 if (is_drawable()) {
1921                         set_canvas_cursor (_cursors->left_side_trim);
1922                 }
1923                 break;
1924         case EndSelectionTrimItem:
1925                 if (is_drawable()) {
1926                         set_canvas_cursor (_cursors->right_side_trim);
1927                 }
1928                 break;
1929
1930         case PlayheadCursorItem:
1931                 if (is_drawable()) {
1932                         switch (_edit_point) {
1933                         case EditAtMouse:
1934                                 set_canvas_cursor (_cursors->grabber_edit_point);
1935                                 break;
1936                         default:
1937                                 set_canvas_cursor (_cursors->grabber);
1938                                 break;
1939                         }
1940                 }
1941                 break;
1942
1943
1944         case RegionViewName:
1945
1946                 /* when the name is not an active item, the entire name highlight is for trimming */
1947
1948                 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1949                         if (mouse_mode == MouseObject && is_drawable()) {
1950                                 set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1951                                 _over_region_trim_target = true;
1952                         }
1953                 }
1954                 break;
1955
1956
1957         case AutomationTrackItem:
1958                 if (is_drawable()) {
1959                         Gdk::Cursor *cursor;
1960                         switch (mouse_mode) {
1961                         case MouseRange:
1962                                 cursor = _cursors->selector;
1963                                 break;
1964                         case MouseZoom:
1965                                 cursor = _cursors->zoom_in;
1966                                 break;
1967                         default:
1968                                 cursor = _cursors->cross_hair;
1969                                 break;
1970                         }
1971
1972                         set_canvas_cursor (cursor);
1973
1974                         AutomationTimeAxisView* atv;
1975                         if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1976                                 clear_entered_track = false;
1977                                 set_entered_track (atv);
1978                         }
1979                 }
1980                 break;
1981
1982         case MarkerBarItem:
1983         case RangeMarkerBarItem:
1984         case TransportMarkerBarItem:
1985         case CdMarkerBarItem:
1986         case MeterBarItem:
1987         case TempoBarItem:
1988                 if (is_drawable()) {
1989                         set_canvas_cursor (_cursors->timebar);
1990                 }
1991                 break;
1992
1993         case MarkerItem:
1994                 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1995                         break;
1996                 }
1997                 entered_marker = marker;
1998                 marker->set_color_rgba (ARDOUR_UI::config()->get_canvasvar_EnteredMarker());
1999                 // fall through
2000         case MeterMarkerItem:
2001         case TempoMarkerItem:
2002                 if (is_drawable()) {
2003                         set_canvas_cursor (_cursors->timebar);
2004                 }
2005                 break;
2006
2007         case FadeInHandleItem:
2008                 if (mouse_mode == MouseObject && !internal_editing()) {
2009                         ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
2010                         if (rect) {
2011                                 RegionView* rv = static_cast<RegionView*>(item->get_data ("regionview"));
2012                                 rect->set_fill_color (rv->get_fill_color());
2013                                 set_canvas_cursor (_cursors->fade_in);
2014                         }
2015                 }
2016                 break;
2017
2018         case FadeInTrimHandleItem:
2019                 if (mouse_mode == MouseObject && !internal_editing()) {
2020                         ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
2021                         if (rect) {
2022                                 RegionView* rv = static_cast<RegionView*>(item->get_data ("regionview"));
2023                                 rect->set_fill_color (rv->get_fill_color());
2024                                 set_canvas_cursor (_cursors->fade_trim_in);
2025                         }
2026                 }
2027                 break;
2028
2029         case FadeOutHandleItem:
2030                 if (mouse_mode == MouseObject && !internal_editing()) {
2031                         ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
2032                         if (rect) {
2033                                 RegionView* rv = static_cast<RegionView*>(item->get_data ("regionview"));
2034                                 rect->set_fill_color (rv->get_fill_color ());
2035                                 set_canvas_cursor (_cursors->fade_out);
2036                         }
2037                 }
2038                 break;
2039
2040         case FadeOutTrimHandleItem:
2041                 if (mouse_mode == MouseObject && !internal_editing()) {
2042                         ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
2043                         if (rect) {
2044                                 RegionView* rv = static_cast<RegionView*>(item->get_data ("regionview"));
2045                                 rect->set_fill_color (rv->get_fill_color ());
2046                                 set_canvas_cursor (_cursors->fade_trim_out);
2047                         }
2048                 }
2049                 break;
2050
2051         case FeatureLineItem:
2052         {
2053                 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
2054                 line->set_outline_color (0xFF0000FF);
2055         }
2056         break;
2057
2058         case SelectionItem:
2059                 if ( get_smart_mode() ) {
2060                         set_canvas_cursor ();
2061                 }
2062                 break;
2063
2064         default:
2065                 break;
2066         }
2067
2068         /* second pass to handle entered track status in a comprehensible way.
2069          */
2070
2071         switch (item_type) {
2072         case GainLineItem:
2073         case AutomationLineItem:
2074         case ControlPointItem:
2075                 /* these do not affect the current entered track state */
2076                 clear_entered_track = false;
2077                 break;
2078
2079         case AutomationTrackItem:
2080                 /* handled above already */
2081                 break;
2082
2083         default:
2084                 set_entered_track (0);
2085                 break;
2086         }
2087
2088         return ret;
2089 }
2090
2091 bool
2092 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent*, ItemType item_type)
2093 {
2094         AutomationLine* al;
2095         Marker *marker;
2096         Location *loc;
2097         bool is_start;
2098         bool ret = true;
2099
2100         switch (item_type) {
2101         case ControlPointItem:
2102                 if (is_drawable()) {
2103                         set_canvas_cursor (current_canvas_cursor);
2104                 }
2105
2106                 _verbose_cursor->hide ();
2107                 break;
2108
2109         case RegionViewNameHighlight:
2110         case LeftFrameHandle:
2111         case RightFrameHandle:
2112         case StartSelectionTrimItem:
2113         case EndSelectionTrimItem:
2114         case PlayheadCursorItem:
2115
2116                 _over_region_trim_target = false;
2117
2118                 if (is_drawable()) {
2119                         set_canvas_cursor (current_canvas_cursor);
2120                 }
2121                 break;
2122
2123         case GainLineItem:
2124         case AutomationLineItem:
2125                 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
2126                 {
2127                         ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
2128                         if (line) {
2129                                 line->set_outline_color (al->get_line_color());
2130                         }
2131                 }
2132                 if (is_drawable()) {
2133                         set_canvas_cursor (current_canvas_cursor);
2134                 }
2135                 break;
2136
2137         case RegionViewName:
2138                 /* see enter_handler() for notes */
2139                 _over_region_trim_target = false;
2140
2141                 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
2142                         if (is_drawable() && mouse_mode == MouseObject) {
2143                                 set_canvas_cursor (current_canvas_cursor);
2144                         }
2145                 }
2146                 break;
2147
2148         case RangeMarkerBarItem:
2149         case TransportMarkerBarItem:
2150         case CdMarkerBarItem:
2151         case MeterBarItem:
2152         case TempoBarItem:
2153         case MarkerBarItem:
2154                 if (is_drawable()) {
2155                         set_canvas_cursor (current_canvas_cursor);
2156                 }
2157                 break;
2158
2159         case MarkerItem:
2160                 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2161                         break;
2162                 }
2163                 entered_marker = 0;
2164                 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
2165                         location_flags_changed (loc, this);
2166                 }
2167                 // fall through
2168         case MeterMarkerItem:
2169         case TempoMarkerItem:
2170
2171                 if (is_drawable()) {
2172                         set_canvas_cursor (current_canvas_cursor);
2173                 }
2174
2175                 break;
2176
2177         case FadeInTrimHandleItem:
2178         case FadeOutTrimHandleItem:
2179         case FadeInHandleItem:
2180         case FadeOutHandleItem:
2181                 {
2182                         ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
2183                         if (rect) {
2184                                 rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_InactiveFadeHandle());
2185                         }
2186                 }
2187                 set_canvas_cursor (current_canvas_cursor);
2188                 break;
2189
2190         case AutomationTrackItem:
2191                 if (is_drawable()) {
2192                         set_canvas_cursor (current_canvas_cursor);
2193                         clear_entered_track = true;
2194                         Glib::signal_idle().connect (sigc::mem_fun(*this, &Editor::left_automation_track));
2195                 }
2196                 break;
2197         case FeatureLineItem:
2198                 {
2199                         ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
2200                         line->set_outline_color (ARDOUR_UI::config()->get_canvasvar_ZeroLine());
2201                 }
2202                 break;
2203
2204         default:
2205                 break;
2206         }
2207
2208         return ret;
2209 }
2210
2211 gint
2212 Editor::left_automation_track ()
2213 {
2214         if (clear_entered_track) {
2215                 set_entered_track (0);
2216                 clear_entered_track = false;
2217         }
2218         return false;
2219 }
2220
2221 void
2222 Editor::scrub (framepos_t frame, double current_x)
2223 {
2224         double delta;
2225
2226         if (scrubbing_direction == 0) {
2227                 /* first move */
2228                 _session->request_locate (frame, false);
2229                 _session->request_transport_speed (0.1);
2230                 scrubbing_direction = 1;
2231
2232         } else {
2233
2234                 if (last_scrub_x > current_x) {
2235
2236                         /* pointer moved to the left */
2237
2238                         if (scrubbing_direction > 0) {
2239
2240                                 /* we reversed direction to go backwards */
2241
2242                                 scrub_reversals++;
2243                                 scrub_reverse_distance += (int) (last_scrub_x - current_x);
2244
2245                         } else {
2246
2247                                 /* still moving to the left (backwards) */
2248
2249                                 scrub_reversals = 0;
2250                                 scrub_reverse_distance = 0;
2251
2252                                 delta = 0.01 * (last_scrub_x - current_x);
2253                                 _session->request_transport_speed_nonzero (_session->transport_speed() - delta);
2254                         }
2255
2256                 } else {
2257                         /* pointer moved to the right */
2258
2259                         if (scrubbing_direction < 0) {
2260                                 /* we reversed direction to go forward */
2261
2262                                 scrub_reversals++;
2263                                 scrub_reverse_distance += (int) (current_x - last_scrub_x);
2264
2265                         } else {
2266                                 /* still moving to the right */
2267
2268                                 scrub_reversals = 0;
2269                                 scrub_reverse_distance = 0;
2270
2271                                 delta = 0.01 * (current_x - last_scrub_x);
2272                                 _session->request_transport_speed_nonzero (_session->transport_speed() + delta);
2273                         }
2274                 }
2275
2276                 /* if there have been more than 2 opposite motion moves detected, or one that moves
2277                    back more than 10 pixels, reverse direction
2278                 */
2279
2280                 if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
2281
2282                         if (scrubbing_direction > 0) {
2283                                 /* was forwards, go backwards */
2284                                 _session->request_transport_speed (-0.1);
2285                                 scrubbing_direction = -1;
2286                         } else {
2287                                 /* was backwards, go forwards */
2288                                 _session->request_transport_speed (0.1);
2289                                 scrubbing_direction = 1;
2290                         }
2291
2292                         scrub_reverse_distance = 0;
2293                         scrub_reversals = 0;
2294                 }
2295         }
2296
2297         last_scrub_x = current_x;
2298 }
2299
2300 bool
2301 Editor::motion_handler (ArdourCanvas::Item* /*item*/, GdkEvent* event, bool from_autoscroll)
2302 {
2303         _last_motion_y = event->motion.y;
2304
2305         if (event->motion.is_hint) {
2306                 gint x, y;
2307
2308                 /* We call this so that MOTION_NOTIFY events continue to be
2309                    delivered to the canvas. We need to do this because we set
2310                    Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
2311                    the density of the events, at the expense of a round-trip
2312                    to the server. Given that this will mostly occur on cases
2313                    where DISPLAY = :0.0, and given the cost of what the motion
2314                    event might do, its a good tradeoff.
2315                 */
2316
2317                 _track_canvas->get_pointer (x, y);
2318         }
2319
2320         if (current_stepping_trackview) {
2321                 /* don't keep the persistent stepped trackview if the mouse moves */
2322                 current_stepping_trackview = 0;
2323                 step_timeout.disconnect ();
2324         }
2325
2326         if (_session && _session->actively_recording()) {
2327                 /* Sorry. no dragging stuff around while we record */
2328                 return true;
2329         }
2330
2331         JoinObjectRangeState const old = _join_object_range_state;
2332         update_join_object_range_location (event->motion.x, event->motion.y);
2333
2334         if (!_internal_editing && _join_object_range_state != old) {
2335                 set_canvas_cursor ();
2336         }
2337
2338         if (!_internal_editing && _over_region_trim_target) {
2339                 set_canvas_cursor_for_region_view (event->motion.x, entered_regionview);
2340         }
2341
2342         bool handled = false;
2343         if (_drags->active ()) {
2344                 handled = _drags->motion_handler (event, from_autoscroll);
2345         }
2346
2347         if (!handled) {
2348                 return false;
2349         }
2350
2351         track_canvas_motion (event);
2352         return true;
2353 }
2354
2355 bool
2356 Editor::can_remove_control_point (ArdourCanvas::Item* item)
2357 {
2358         ControlPoint* control_point;
2359
2360         if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2361                 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2362                 /*NOTREACHED*/
2363         }
2364
2365         AutomationLine& line = control_point->line ();
2366         if (dynamic_cast<AudioRegionGainLine*> (&line)) {
2367                 /* we shouldn't remove the first or last gain point in region gain lines */
2368                 if (line.is_last_point(*control_point) || line.is_first_point(*control_point)) {
2369                         return false;
2370                 }
2371         }
2372
2373         return true;
2374 }
2375
2376 void
2377 Editor::remove_control_point (ArdourCanvas::Item* item)
2378 {
2379         if (!can_remove_control_point (item)) {
2380                 return;
2381         }
2382
2383         ControlPoint* control_point;
2384
2385         if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2386                 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2387                 /*NOTREACHED*/
2388         }
2389
2390         control_point->line().remove_point (*control_point);
2391 }
2392
2393 void
2394 Editor::edit_control_point (ArdourCanvas::Item* item)
2395 {
2396         ControlPoint* p = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"));
2397
2398         if (p == 0) {
2399                 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2400                 /*NOTREACHED*/
2401         }
2402
2403         ControlPointDialog d (p);
2404         ensure_float (d);
2405
2406         if (d.run () != RESPONSE_ACCEPT) {
2407                 return;
2408         }
2409
2410         p->line().modify_point_y (*p, d.get_y_fraction ());
2411 }
2412
2413 void
2414 Editor::edit_notes (TimeAxisViewItem& tavi)
2415 {
2416         MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(&tavi);
2417
2418         if (!mrv) {
2419                 return;
2420         }
2421
2422         MidiRegionView::Selection const & s = mrv->selection();
2423
2424         if (s.empty ()) {
2425                 return;
2426         }
2427         
2428         EditNoteDialog* d = new EditNoteDialog (&(*s.begin())->region_view(), s);
2429         d->show_all ();
2430         ensure_float (*d);
2431
2432         d->signal_response().connect (sigc::bind (sigc::mem_fun (*this, &Editor::note_edit_done), d));
2433 }
2434
2435 void
2436 Editor::note_edit_done (int r, EditNoteDialog* d)
2437 {
2438         d->done (r);
2439         delete d;
2440 }
2441
2442 void
2443 Editor::visible_order_range (int* low, int* high) const
2444 {
2445         *low = TimeAxisView::max_order ();
2446         *high = 0;
2447
2448         for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
2449
2450                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
2451
2452                 if (!rtv->hidden()) {
2453
2454                         if (*high < rtv->order()) {
2455                                 *high = rtv->order ();
2456                         }
2457
2458                         if (*low > rtv->order()) {
2459                                 *low = rtv->order ();
2460                         }
2461                 }
2462         }
2463 }
2464
2465 void
2466 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
2467 {
2468         /* Either add to or set the set the region selection, unless
2469            this is an alignment click (control used)
2470         */
2471
2472         if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
2473                 TimeAxisView* tv = &rv.get_time_axis_view();
2474                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
2475                 double speed = 1.0;
2476                 if (rtv && rtv->is_track()) {
2477                         speed = rtv->track()->speed();
2478                 }
2479
2480                 framepos_t where = get_preferred_edit_position();
2481
2482                 if (where >= 0) {
2483
2484                         if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
2485
2486                                 align_region (rv.region(), SyncPoint, (framepos_t) (where * speed));
2487
2488                         } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
2489
2490                                 align_region (rv.region(), End, (framepos_t) (where * speed));
2491
2492                         } else {
2493
2494                                 align_region (rv.region(), Start, (framepos_t) (where * speed));
2495                         }
2496                 }
2497         }
2498 }
2499
2500 void
2501 Editor::collect_new_region_view (RegionView* rv)
2502 {
2503         latest_regionviews.push_back (rv);
2504 }
2505
2506 void
2507 Editor::collect_and_select_new_region_view (RegionView* rv)
2508 {
2509         selection->add(rv);
2510         latest_regionviews.push_back (rv);
2511 }
2512
2513 void
2514 Editor::cancel_selection ()
2515 {
2516         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2517                 (*i)->hide_selection ();
2518         }
2519
2520         selection->clear ();
2521         clicked_selection = 0;
2522 }
2523
2524 void
2525 Editor::cancel_time_selection ()
2526 {
2527     for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2528                 (*i)->hide_selection ();
2529         }
2530         selection->time.clear ();
2531         clicked_selection = 0;
2532 }       
2533
2534 void
2535 Editor::point_trim (GdkEvent* event, framepos_t new_bound)
2536 {
2537         RegionView* rv = clicked_regionview;
2538
2539         /* Choose action dependant on which button was pressed */
2540         switch (event->button.button) {
2541         case 1:
2542                 begin_reversible_command (_("start point trim"));
2543
2544                 if (selection->selected (rv)) {
2545                         for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
2546                              i != selection->regions.by_layer().end(); ++i)
2547                         {
2548                                 if (!(*i)->region()->locked()) {
2549                                         (*i)->region()->clear_changes ();
2550                                         (*i)->region()->trim_front (new_bound);
2551                                         _session->add_command(new StatefulDiffCommand ((*i)->region()));
2552                                 }
2553                         }
2554
2555                 } else {
2556                         if (!rv->region()->locked()) {
2557                                 rv->region()->clear_changes ();
2558                                 rv->region()->trim_front (new_bound);
2559                                 _session->add_command(new StatefulDiffCommand (rv->region()));
2560                         }
2561                 }
2562
2563                 commit_reversible_command();
2564
2565                 break;
2566         case 2:
2567                 begin_reversible_command (_("End point trim"));
2568
2569                 if (selection->selected (rv)) {
2570
2571                         for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
2572                         {
2573                                 if (!(*i)->region()->locked()) {
2574                                         (*i)->region()->clear_changes();
2575                                         (*i)->region()->trim_end (new_bound);
2576                                         _session->add_command(new StatefulDiffCommand ((*i)->region()));
2577                                 }
2578                         }
2579
2580                 } else {
2581
2582                         if (!rv->region()->locked()) {
2583                                 rv->region()->clear_changes ();
2584                                 rv->region()->trim_end (new_bound);
2585                                 _session->add_command (new StatefulDiffCommand (rv->region()));
2586                         }
2587                 }
2588
2589                 commit_reversible_command();
2590
2591                 break;
2592         default:
2593                 break;
2594         }
2595 }
2596
2597 void
2598 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* /*event*/)
2599 {
2600         Marker* marker;
2601         bool is_start;
2602
2603         if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2604                 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2605                 /*NOTREACHED*/
2606         }
2607
2608         Location* location = find_location_from_marker (marker, is_start);
2609         location->set_hidden (true, this);
2610 }
2611
2612
2613 void
2614 Editor::reposition_zoom_rect (framepos_t start, framepos_t end)
2615 {
2616         double x1 = sample_to_pixel (start);
2617         double x2 = sample_to_pixel (end);
2618         double y2 = _full_canvas_height - 1.0;
2619
2620         zoom_rect->set (ArdourCanvas::Rect (x1, 1.0, x2, y2));
2621 }
2622
2623
2624 gint
2625 Editor::mouse_rename_region (ArdourCanvas::Item* /*item*/, GdkEvent* /*event*/)
2626 {
2627         using namespace Gtkmm2ext;
2628
2629         ArdourPrompter prompter (false);
2630
2631         prompter.set_prompt (_("Name for region:"));
2632         prompter.set_initial_text (clicked_regionview->region()->name());
2633         prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
2634         prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
2635         prompter.show_all ();
2636         switch (prompter.run ()) {
2637         case Gtk::RESPONSE_ACCEPT:
2638                 string str;
2639                 prompter.get_result(str);
2640                 if (str.length()) {
2641                         clicked_regionview->region()->set_name (str);
2642                 }
2643                 break;
2644         }
2645         return true;
2646 }
2647
2648
2649 void
2650 Editor::mouse_brush_insert_region (RegionView* rv, framepos_t pos)
2651 {
2652         /* no brushing without a useful snap setting */
2653
2654         switch (_snap_mode) {
2655         case SnapMagnetic:
2656                 return; /* can't work because it allows region to be placed anywhere */
2657         default:
2658                 break; /* OK */
2659         }
2660
2661         switch (_snap_type) {
2662         case SnapToMark:
2663                 return;
2664
2665         default:
2666                 break;
2667         }
2668
2669         /* don't brush a copy over the original */
2670
2671         if (pos == rv->region()->position()) {
2672                 return;
2673         }
2674
2675         RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
2676
2677         if (rtv == 0 || !rtv->is_track()) {
2678                 return;
2679         }
2680
2681         boost::shared_ptr<Playlist> playlist = rtv->playlist();
2682         double speed = rtv->track()->speed();
2683
2684         playlist->clear_changes ();
2685         boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region(), true));
2686         playlist->add_region (new_region, (framepos_t) (pos * speed));
2687         _session->add_command (new StatefulDiffCommand (playlist));
2688
2689         // playlist is frozen, so we have to update manually XXX this is disgusting
2690
2691         playlist->RegionAdded (new_region); /* EMIT SIGNAL */
2692 }
2693
2694 gint
2695 Editor::track_height_step_timeout ()
2696 {
2697         if (get_microseconds() - last_track_height_step_timestamp < 250000) {
2698                 current_stepping_trackview = 0;
2699                 return false;
2700         }
2701         return true;
2702 }
2703
2704 void
2705 Editor::add_region_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2706 {
2707         assert (region_view);
2708
2709         if (!region_view->region()->playlist()) {
2710                 return;
2711         }
2712
2713         _region_motion_group->raise_to_top ();
2714
2715         if (Config->get_edit_mode() == Splice) {
2716                 _drags->add (new RegionSpliceDrag (this, item, region_view, selection->regions.by_layer()));
2717         } else {
2718                 _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), false, false));
2719         }
2720 }
2721
2722 void
2723 Editor::add_region_copy_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2724 {
2725         assert (region_view);
2726
2727         if (!region_view->region()->playlist()) {
2728                 return;
2729         }
2730
2731         _region_motion_group->raise_to_top ();
2732
2733         _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), false, true));
2734 }
2735
2736 void
2737 Editor::add_region_brush_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2738 {
2739         assert (region_view);
2740
2741         if (!region_view->region()->playlist()) {
2742                 return;
2743         }
2744
2745         if (Config->get_edit_mode() == Splice) {
2746                 return;
2747         }
2748
2749         _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), true, false));
2750
2751         begin_reversible_command (Operations::drag_region_brush);
2752 }
2753
2754 /** Start a grab where a time range is selected, track(s) are selected, and the
2755  *  user clicks and drags a region with a modifier in order to create a new region containing
2756  *  the section of the clicked region that lies within the time range.
2757  */
2758 void
2759 Editor::start_selection_grab (ArdourCanvas::Item* /*item*/, GdkEvent* event)
2760 {
2761         if (clicked_regionview == 0) {
2762                 return;
2763         }
2764
2765         /* lets try to create new Region for the selection */
2766
2767         vector<boost::shared_ptr<Region> > new_regions;
2768         create_region_from_selection (new_regions);
2769
2770         if (new_regions.empty()) {
2771                 return;
2772         }
2773
2774         /* XXX fix me one day to use all new regions */
2775
2776         boost::shared_ptr<Region> region (new_regions.front());
2777
2778         /* add it to the current stream/playlist.
2779
2780            tricky: the streamview for the track will add a new regionview. we will
2781            catch the signal it sends when it creates the regionview to
2782            set the regionview we want to then drag.
2783         */
2784
2785         latest_regionviews.clear();
2786         sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
2787
2788         /* A selection grab currently creates two undo/redo operations, one for
2789            creating the new region and another for moving it.
2790         */
2791
2792         begin_reversible_command (Operations::selection_grab);
2793
2794         boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
2795
2796         playlist->clear_changes ();
2797         clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
2798         _session->add_command(new StatefulDiffCommand (playlist));
2799
2800         commit_reversible_command ();
2801
2802         c.disconnect ();
2803
2804         if (latest_regionviews.empty()) {
2805                 /* something went wrong */
2806                 return;
2807         }
2808
2809         /* we need to deselect all other regionviews, and select this one
2810            i'm ignoring undo stuff, because the region creation will take care of it
2811         */
2812         selection->set (latest_regionviews);
2813
2814         _drags->set (new RegionMoveDrag (this, latest_regionviews.front()->get_canvas_group(), latest_regionviews.front(), latest_regionviews, false, false), event);
2815 }
2816
2817 void
2818 Editor::escape ()
2819 {
2820         if (_drags->active ()) {
2821                 _drags->abort ();
2822         } else {
2823                 selection->clear ();
2824         }
2825 }
2826
2827 void
2828 Editor::set_internal_edit (bool yn)
2829 {
2830         if (_internal_editing == yn) {
2831                 return;
2832         }
2833
2834         _internal_editing = yn;
2835         
2836         if (yn) {
2837                 pre_internal_mouse_mode = mouse_mode;
2838                 pre_internal_snap_type = _snap_type;
2839                 pre_internal_snap_mode = _snap_mode;
2840
2841                 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2842                         (*i)->enter_internal_edit_mode ();
2843                 }
2844
2845                 set_snap_to (internal_snap_type);
2846                 set_snap_mode (internal_snap_mode);
2847
2848         } else {
2849
2850                 internal_snap_mode = _snap_mode;
2851                 internal_snap_type = _snap_type;
2852
2853                 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2854                         (*i)->leave_internal_edit_mode ();
2855                 }
2856
2857                 if (mouse_mode == MouseDraw && pre_internal_mouse_mode != MouseDraw) {
2858                         /* we were drawing .. flip back to something sensible */
2859                         set_mouse_mode (pre_internal_mouse_mode);
2860                 }
2861
2862                 set_snap_to (pre_internal_snap_type);
2863                 set_snap_mode (pre_internal_snap_mode);
2864         }
2865         
2866         set_canvas_cursor ();
2867 }
2868
2869 /** Update _join_object_range_state which indicate whether we are over the top or bottom half of a region view,
2870  *  used by the `join object/range' tool mode.
2871  */
2872 void
2873 Editor::update_join_object_range_location (double /*x*/, double y)
2874 {
2875         /* XXX: actually, this decides based on whether the mouse is in the top
2876            or bottom half of a the waveform part RouteTimeAxisView;
2877
2878            Note that entered_{track,regionview} is not always setup (e.g. if
2879            the mouse is over a TimeSelection), and to get a Region
2880            that we're over requires searching the playlist.
2881         */
2882
2883         if ( !get_smart_mode() ) {
2884                 _join_object_range_state = JOIN_OBJECT_RANGE_NONE;
2885                 return;
2886         }
2887
2888         if (mouse_mode == MouseObject) {
2889                 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2890         } else if (mouse_mode == MouseRange) {
2891                 _join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
2892         }
2893
2894         /* XXX: maybe we should make entered_track work in all cases, rather than resorting to this */
2895         pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
2896
2897         if (tvp.first) {
2898
2899                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
2900                 if (rtv) {
2901
2902                         double cx = 0;
2903                         double cy = y;
2904                         rtv->canvas_display()->canvas_to_item (cx, cy);
2905
2906                         double const c = cy / (rtv->view()->child_height() - TimeAxisViewItem::NAME_HIGHLIGHT_SIZE);
2907
2908                         _join_object_range_state = c <= 0.5 ? JOIN_OBJECT_RANGE_RANGE : JOIN_OBJECT_RANGE_OBJECT;
2909                 }
2910         }
2911 }
2912
2913 Editing::MouseMode
2914 Editor::effective_mouse_mode () const
2915 {
2916         if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
2917                 return MouseObject;
2918         } else if (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE) {
2919                 return MouseRange;
2920         }
2921
2922         return mouse_mode;
2923 }
2924
2925 void
2926 Editor::remove_midi_note (ArdourCanvas::Item* item, GdkEvent *)
2927 {
2928         NoteBase* e = reinterpret_cast<NoteBase*> (item->get_data ("notebase"));
2929         assert (e);
2930
2931         e->region_view().delete_note (e->note ());
2932 }
2933
2934 void
2935 Editor::set_canvas_cursor_for_region_view (double x, RegionView* rv)
2936 {
2937         /* XXX: this check should not be necessary */
2938         if (rv == 0) {
2939                 return;
2940         }
2941
2942         ArdourCanvas::Group* g = rv->get_canvas_group ();
2943         ArdourCanvas::Group* p = g->parent ();
2944
2945         /* Compute x in region view parent coordinates */
2946         double dy = 0;
2947         p->canvas_to_item (x, dy);
2948
2949         boost::optional<ArdourCanvas::Rect> item_bbox = g->bounding_box ();
2950         assert (item_bbox);
2951         ArdourCanvas::Rect parent_bbox = g->item_to_parent (item_bbox.get ());
2952
2953         /* First or last 10% of region is used for trimming, if the whole
2954            region is wider than 20 pixels at the current zoom level.
2955         */
2956
2957         double const w = parent_bbox.width();
2958
2959         if (w > 20.0 && x >= parent_bbox.x0 && x < parent_bbox.x1) {
2960                 
2961                 Trimmable::CanTrim ct = rv->region()->can_trim ();
2962
2963                 if (((x - parent_bbox.x0) / w) < 0.10) {
2964                         if (ct & Trimmable::FrontTrimEarlier) {
2965                                 set_canvas_cursor (_cursors->left_side_trim, true);
2966                         } else {
2967                                 set_canvas_cursor (_cursors->left_side_trim_right_only, true);
2968                         }
2969                 } else if (((parent_bbox.x1 - x) / w) < 0.10) {
2970                         if (ct & Trimmable::EndTrimLater) {
2971                                 set_canvas_cursor (_cursors->right_side_trim, true);
2972                         } else {
2973                                 set_canvas_cursor (_cursors->right_side_trim_left_only, true);
2974                         }
2975                 }
2976         }
2977 }
2978
2979 /** Obtain the pointer position in canvas coordinates */
2980 void
2981 Editor::get_pointer_position (double& x, double& y) const
2982 {
2983         int px, py;
2984         _track_canvas->get_pointer (px, py);
2985         _track_canvas->window_to_canvas (px, py, x, y);
2986 }