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