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