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         case VideoBarItem:
760                 _drags->set (new VideoTimeLineDrag (this, item), event);
761                 return true;
762                 break;
763
764         case MarkerBarItem:
765         case TempoBarItem:
766         case MeterBarItem:
767                 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
768                         _drags->set (new CursorDrag (this, &playhead_cursor->track_canvas_item (), false), event);
769                 }
770                 return true;
771                 break;
772
773
774         case RangeMarkerBarItem:
775                 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
776                         _drags->set (new CursorDrag (this, &playhead_cursor->track_canvas_item (), false), event);
777                 } else {
778                         _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateRangeMarker), event);
779                 }
780                 return true;
781                 break;
782
783         case CdMarkerBarItem:
784                 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
785                         _drags->set (new CursorDrag (this, &playhead_cursor->track_canvas_item (), false), event);
786                 } else {
787                         _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateCDMarker), event);
788                 }
789                 return true;
790                 break;
791
792         case TransportMarkerBarItem:
793                 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
794                         _drags->set (new CursorDrag (this, &playhead_cursor->track_canvas_item (), false), event);
795                 } else {
796                         _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateTransportMarker), event);
797                 }
798                 return true;
799                 break;
800
801         default:
802                 break;
803         }
804
805         if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
806                 /* special case: allow trim of range selections in joined object mode;
807                    in theory eff should equal MouseRange in this case, but it doesn't
808                    because entering the range selection canvas item results in entered_regionview
809                    being set to 0, so update_join_object_range_location acts as if we aren't
810                    over a region.
811                 */
812                 if (item_type == StartSelectionTrimItem) {
813                         _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
814                 } else if (item_type == EndSelectionTrimItem) {
815                         _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
816                 }
817         }
818
819         Editing::MouseMode eff = effective_mouse_mode ();
820
821         /* special case: allow drag of region fade in/out in object mode with join object/range enabled */
822         if (item_type == FadeInHandleItem || item_type == FadeOutHandleItem) {
823                 eff = MouseObject;
824         }
825
826         /* there is no Range mode when in internal edit mode */
827         if (eff == MouseRange && internal_editing()) {
828                 eff = MouseObject;
829         }
830
831         switch (eff) {
832         case MouseRange:
833                 switch (item_type) {
834                 case StartSelectionTrimItem:
835                         _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
836                         break;
837
838                 case EndSelectionTrimItem:
839                         _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
840                         break;
841
842                 case SelectionItem:
843                         if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
844                                 start_selection_grab (item, event);
845                                 return true;
846                         } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
847                                 /* grab selection for moving */
848                                 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionMove), event);
849                         } else {
850                                 double const y = event->button.y + vertical_adjustment.get_value();
851                                 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
852                                 if (tvp.first) {
853                                         AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
854                                         if ( get_smart_mode() && atv) {
855                                                 /* smart "join" mode: drag automation */
856                                                 _drags->set (new AutomationRangeDrag (this, atv, selection->time), event, _cursors->up_down);
857                                         } else {
858                                                 /* this was debated, but decided the more common action was to
859                                                    make a new selection */
860                                                 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
861                                         }
862                                 }
863                         }
864                         break;
865
866                 case StreamItem:
867                         if (internal_editing()) {
868                                 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
869                                         _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
870                                         return true;
871                                 } 
872                         } else {
873                                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::RangeSelectModifier)) {
874                                         _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionExtend), event);
875                                 } else {
876                                         _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
877                                 }
878                                 return true;
879                         }
880                         break;
881
882                 case RegionViewNameHighlight:
883                         if (!clicked_regionview->region()->locked()) {
884                                 RegionSelection s = get_equivalent_regions (selection->regions, Properties::select.property_id);
885                                 _drags->set (new TrimDrag (this, item, clicked_regionview, s.by_layer()), event);
886                                 return true;
887                         }
888                         break;
889
890                 default:
891                         if (!internal_editing()) {
892                                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::RangeSelectModifier)) {
893                                         _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionExtend), event);
894                                 } else {
895                                         _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
896                                 }
897                         }
898                 }
899                 return true;
900                 break;
901
902         case MouseDraw:
903                 switch (item_type) {
904                 case NoteItem:
905                         if (internal_editing()) {
906                                 /* trim notes if we're in internal edit mode and near the ends of the note */
907                                 NoteBase* cn = reinterpret_cast<NoteBase*>(item->get_data ("notebase"));
908                                 assert (cn);
909                                 if (cn && cn->big_enough_to_trim() && cn->mouse_near_ends()) {
910                                         _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
911                                 } else {
912                                         _drags->set (new NoteDrag (this, item), event);
913                                 }
914                                 return true;
915                         }
916                         break;
917                 case StreamItem:
918                         if (internal_editing()) {
919                                 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
920                                         _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
921                                 }
922                                 return true;
923                         }
924                         break;
925
926                 default:
927                         break;
928                 }
929                 break;
930
931         case MouseObject:
932                 switch (item_type) {
933                 case NoteItem:
934                         if (internal_editing()) {
935                                 NoteBase* cn = reinterpret_cast<NoteBase*> (item->get_data ("notebase"));
936                                 assert (cn);
937                                 if (cn->mouse_near_ends()) {
938                                         _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
939                                 } else {
940                                         _drags->set (new NoteDrag (this, item), event);
941                                 }
942                                 return true;
943                         }
944                         break;
945
946                 default:
947                         break;
948                 }
949
950                 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)) &&
951                     event->type == GDK_BUTTON_PRESS) {
952
953                         _drags->set (new EditorRubberbandSelectDrag (this, item), event);
954
955                 } else if (event->type == GDK_BUTTON_PRESS) {
956
957                         switch (item_type) {
958                         case FadeInHandleItem:
959                         {
960                                 RegionSelection s = get_equivalent_regions (selection->regions, Properties::select.property_id);
961                                 _drags->set (new FadeInDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), s), event, _cursors->fade_in);
962                                 return true;
963                         }
964
965                         case FadeOutHandleItem:
966                         {
967                                 RegionSelection s = get_equivalent_regions (selection->regions, Properties::select.property_id);
968                                 _drags->set (new FadeOutDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), s), event, _cursors->fade_out);
969                                 return true;
970                         }
971
972                         case StartCrossFadeItem:
973                         case EndCrossFadeItem:
974                                 /* we might allow user to grab inside the fade to trim a region with preserve_fade_anchor.  for not this is not fully implemented */ 
975 //                              if (!clicked_regionview->region()->locked()) {
976 //                                      RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
977 //                                      _drags->set (new TrimDrag (this, item, clicked_regionview, s.by_layer(), true), event);
978 //                                      return true;
979 //                              }
980                                 break;
981
982                         case FeatureLineItem:
983                         {
984                                 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier)) {
985                                         remove_transient(item);
986                                         return true;
987                                 }
988
989                                 _drags->set (new FeatureLineDrag (this, item), event);
990                                 return true;
991                                 break;
992                         }
993
994                         case RegionItem:
995                                 if (dynamic_cast<AutomationRegionView*> (clicked_regionview)) {
996                                         /* click on an automation region view; do nothing here and let the ARV's signal handler
997                                            sort it out.
998                                         */
999                                         break;
1000                                 }
1001
1002                                 if (internal_editing ()) {
1003                                         if (event->type == GDK_2BUTTON_PRESS && event->button.button == 1) {
1004                                                 Glib::RefPtr<Action> act = ActionManager::get_action (X_("MouseMode"), X_("toggle-internal-edit"));
1005                                                 act->activate ();
1006                                         }
1007                                         break;
1008                                 }
1009
1010                                 /* click on a normal region view */
1011                                 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
1012                                         add_region_copy_drag (item, event, clicked_regionview);
1013                                 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
1014                                         add_region_brush_drag (item, event, clicked_regionview);
1015                                 } else {
1016                                         add_region_drag (item, event, clicked_regionview);
1017                                 }
1018
1019
1020 //                              if (!internal_editing() && (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE && !selection->regions.empty())) {
1021 //                                      _drags->add (new SelectionDrag (this, clicked_axisview->get_selection_rect (clicked_selection)->rect, SelectionDrag::SelectionMove));
1022 //                              }
1023
1024                                 _drags->start_grab (event);
1025                                 return true;
1026                                 break;
1027
1028                         case RegionViewNameHighlight:
1029                         case LeftFrameHandle:
1030                         case RightFrameHandle:
1031                                 if (!clicked_regionview->region()->locked()) {
1032                                         RegionSelection s = get_equivalent_regions (selection->regions, Properties::select.property_id);
1033                                         _drags->set (new TrimDrag (this, item, clicked_regionview, s.by_layer()), event);
1034                                         return true;
1035                                 }
1036                                 break;
1037
1038                         case RegionViewName:
1039                         {
1040                                 /* rename happens on edit clicks */
1041                                 RegionSelection s = get_equivalent_regions (selection->regions, Properties::select.property_id);
1042                                 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, s.by_layer()), event);
1043                                 return true;
1044                                 break;
1045                         }
1046
1047                         case ControlPointItem:
1048                                 _drags->set (new ControlPointDrag (this, item), event);
1049                                 return true;
1050                                 break;
1051
1052                         case AutomationLineItem:
1053                                 _drags->set (new LineDrag (this, item), event);
1054                                 return true;
1055                                 break;
1056
1057                         case StreamItem:
1058                                 if (internal_editing()) {
1059                                         if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
1060                                                 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
1061                                         }
1062                                         return true;
1063                                 } else {
1064                                         _drags->set (new EditorRubberbandSelectDrag (this, item), event);
1065                                 }
1066                                 break;
1067
1068                         case AutomationTrackItem:
1069                         {
1070                                 TimeAxisView* parent = clicked_axisview->get_parent ();
1071                                 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (clicked_axisview);
1072                                 assert (atv);
1073                                 if (parent && dynamic_cast<MidiTimeAxisView*> (parent) && atv->show_regions ()) {
1074
1075                                         RouteTimeAxisView* p = dynamic_cast<RouteTimeAxisView*> (parent);
1076                                         assert (p);
1077                                         boost::shared_ptr<Playlist> pl = p->track()->playlist ();
1078                                         if (pl->n_regions() == 0) {
1079                                                 /* Parent has no regions; create one so that we have somewhere to put automation */
1080                                                 _drags->set (new RegionCreateDrag (this, item, parent), event);
1081                                         } else {
1082                                                 /* See if there's a region before the click that we can extend, and extend it if so */
1083                                                 framepos_t const t = event_frame (event);
1084                                                 boost::shared_ptr<Region> prev = pl->find_next_region (t, End, -1);
1085                                                 if (!prev) {
1086                                                         _drags->set (new RegionCreateDrag (this, item, parent), event);
1087                                                 } else {
1088                                                         prev->set_length (t - prev->position ());
1089                                                 }
1090                                         }
1091                                 } else {
1092                                         /* rubberband drag to select automation points */
1093                                         _drags->set (new EditorRubberbandSelectDrag (this, item), event);
1094                                 }
1095                                 break;
1096                         }
1097
1098                         case SelectionItem:
1099                         {
1100                                 if ( get_smart_mode() ) {
1101                                         /* we're in "smart" joined mode, and we've clicked on a Selection */
1102                                         double const y = event->button.y + vertical_adjustment.get_value();
1103                                         pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
1104                                         if (tvp.first) {
1105                                                 /* if we're over an automation track, start a drag of its data */
1106                                                 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
1107                                                 if (atv) {
1108                                                         _drags->set (new AutomationRangeDrag (this, atv, selection->time), event, _cursors->up_down);
1109                                                 }
1110
1111                                                 /* if we're over a track and a region, and in the `object' part of a region,
1112                                                    put a selection around the region and drag both
1113                                                 */
1114 /*                                              RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1115                                                 if (rtv && _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
1116                                                         boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track> (rtv->route ());
1117                                                         if (t) {
1118                                                                 boost::shared_ptr<Playlist> pl = t->playlist ();
1119                                                                 if (pl) {
1120
1121                                                                         boost::shared_ptr<Region> r = pl->top_region_at (event_frame (event));
1122                                                                         if (r) {
1123                                                                                 RegionView* rv = rtv->view()->find_view (r);
1124                                                                                 clicked_selection = select_range (rv->region()->position(), 
1125                                                                                                                   rv->region()->last_frame()+1);
1126                                                                                 _drags->add (new SelectionDrag (this, item, SelectionDrag::SelectionMove));
1127                                                                                 list<RegionView*> rvs;
1128                                                                                 rvs.push_back (rv);
1129                                                                                 _drags->add (new RegionMoveDrag (this, item, rv, rvs, false, false));
1130                                                                                 _drags->start_grab (event);
1131                                                                                 return true;
1132                                                                         }
1133                                                                 }
1134                                                         }
1135                                                 }
1136 */
1137                                         }
1138                                 }
1139                                 break;
1140                         }
1141
1142 #ifdef WITH_CMT
1143                         case ImageFrameHandleStartItem:
1144                                 imageframe_start_handle_op(item, event) ;
1145                                 return(true) ;
1146                                 break ;
1147                         case ImageFrameHandleEndItem:
1148                                 imageframe_end_handle_op(item, event) ;
1149                                 return(true) ;
1150                                 break ;
1151                         case MarkerViewHandleStartItem:
1152                                 markerview_item_start_handle_op(item, event) ;
1153                                 return(true) ;
1154                                 break ;
1155                         case MarkerViewHandleEndItem:
1156                                 markerview_item_end_handle_op(item, event) ;
1157                                 return(true) ;
1158                                 break ;
1159                         case MarkerViewItem:
1160                                 start_markerview_grab(item, event) ;
1161                                 break ;
1162                         case ImageFrameItem:
1163                                 start_imageframe_grab(item, event) ;
1164                                 break ;
1165 #endif
1166
1167                         case MarkerBarItem:
1168
1169                                 break;
1170
1171                         default:
1172                                 break;
1173                         }
1174                 }
1175                 return true;
1176                 break;
1177
1178         case MouseGain:
1179                 switch (item_type) {
1180                 case GainLineItem:
1181                         _drags->set (new LineDrag (this, item), event);
1182                         return true;
1183
1184                 case ControlPointItem:
1185                         _drags->set (new ControlPointDrag (this, item), event);
1186                         return true;
1187                         break;
1188
1189                 case SelectionItem:
1190                 {
1191                         AudioRegionView* arv = dynamic_cast<AudioRegionView *> (clicked_regionview);
1192                         if (arv) {
1193                                 _drags->set (new AutomationRangeDrag (this, arv, selection->time), event, _cursors->up_down);
1194                                 _drags->start_grab (event);
1195                         }
1196                         return true;
1197                         break;
1198                 }
1199
1200                 case AutomationLineItem:
1201                         _drags->set (new LineDrag (this, item), event);
1202                         break;
1203                         
1204                 default:
1205                         break;
1206                 }
1207                 return true;
1208                 break;
1209
1210         case MouseZoom:
1211                 if (event->type == GDK_BUTTON_PRESS) {
1212                         _drags->set (new MouseZoomDrag (this, item), event);
1213                 }
1214
1215                 return true;
1216                 break;
1217
1218         case MouseTimeFX:
1219                 if (internal_editing() && item_type == NoteItem) {
1220                         /* drag notes if we're in internal edit mode */
1221                         _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
1222                         return true;
1223                 } else if (clicked_regionview) {
1224                         /* do time-FX  */
1225                         _drags->set (new TimeFXDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1226                         return true;
1227                 }
1228                 break;
1229
1230         case MouseAudition:
1231                 _drags->set (new ScrubDrag (this, item), event);
1232                 scrub_reversals = 0;
1233                 scrub_reverse_distance = 0;
1234                 last_scrub_x = event->button.x;
1235                 scrubbing_direction = 0;
1236                 set_canvas_cursor (_cursors->transparent);
1237                 return true;
1238                 break;
1239
1240         default:
1241                 break;
1242         }
1243
1244         return false;
1245 }
1246
1247 bool
1248 Editor::button_press_handler_2 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1249 {
1250         Editing::MouseMode const eff = effective_mouse_mode ();
1251         switch (eff) {
1252         case MouseObject:
1253                 switch (item_type) {
1254                 case RegionItem:
1255                         if (internal_editing ()) {
1256                                 /* no region drags in internal edit mode */
1257                                 return false;
1258                         }
1259
1260                         if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
1261                                 add_region_copy_drag (item, event, clicked_regionview);
1262                         } else {
1263                                 add_region_drag (item, event, clicked_regionview);
1264                         }
1265                         _drags->start_grab (event);
1266                         return true;
1267                         break;
1268                 case ControlPointItem:
1269                         _drags->set (new ControlPointDrag (this, item), event);
1270                         return true;
1271                         break;
1272
1273                 default:
1274                         break;
1275                 }
1276
1277                 switch (item_type) {
1278                 case RegionViewNameHighlight:
1279                         _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1280                         return true;
1281                         break;
1282
1283                 case LeftFrameHandle:
1284                 case RightFrameHandle:
1285                         if (!internal_editing ()) {
1286                                 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1287                         }
1288                         return true;
1289                         break;
1290
1291                 case RegionViewName:
1292                         _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer()), event);
1293                         return true;
1294                         break;
1295
1296                 default:
1297                         break;
1298                 }
1299
1300                 break;
1301
1302         case MouseDraw:
1303                 return false;
1304
1305         case MouseRange:
1306                 /* relax till release */
1307                 return true;
1308                 break;
1309
1310
1311         case MouseZoom:
1312                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1313                         temporal_zoom_to_frame (false, event_frame (event));
1314                 } else {
1315                         temporal_zoom_to_frame (true, event_frame(event));
1316                 }
1317                 return true;
1318                 break;
1319
1320         default:
1321                 break;
1322         }
1323
1324         return false;
1325 }
1326    
1327 bool
1328 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1329 {
1330         if (event->type != GDK_BUTTON_PRESS) {
1331                 return false;
1332         }
1333
1334         Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->_track_canvas_viewport->get_window();
1335
1336         if (canvas_window) {
1337                 Glib::RefPtr<const Gdk::Window> pointer_window;
1338                 int x, y;
1339                 double wx, wy;
1340                 Gdk::ModifierType mask;
1341
1342                 pointer_window = canvas_window->get_pointer (x, y, mask);
1343
1344                 if (pointer_window == _track_canvas->get_window()) {
1345                         _track_canvas_viewport->window_to_canvas (x, y, wx, wy);
1346                 }
1347         }
1348
1349         pre_press_cursor = current_canvas_cursor;
1350         
1351         _track_canvas->grab_focus();
1352
1353         if (_session && _session->actively_recording()) {
1354                 return true;
1355         }
1356
1357         if (internal_editing()) {
1358                 bool leave_internal_edit_mode = false;
1359
1360                 switch (item_type) {
1361                 case NoteItem:
1362                         break;
1363
1364                 case RegionItem:
1365                         if (!dynamic_cast<MidiRegionView*> (clicked_regionview) && !dynamic_cast<AutomationRegionView*> (clicked_regionview)) {
1366                                 leave_internal_edit_mode = true;
1367                         }
1368                         break;
1369
1370                 case PlayheadCursorItem:
1371                 case MarkerItem:
1372                 case TempoMarkerItem:
1373                 case MeterMarkerItem:
1374                 case MarkerBarItem:
1375                 case TempoBarItem:
1376                 case MeterBarItem:
1377                 case RangeMarkerBarItem:
1378                 case CdMarkerBarItem:
1379                 case TransportMarkerBarItem:
1380                 case StreamItem:
1381                         /* button press on these events never does anything to
1382                            change the editing mode.
1383                         */
1384                         break;
1385
1386                 default:
1387                         break;
1388                 }
1389                 
1390                 if (leave_internal_edit_mode) {
1391                         ActionManager::do_action ("MouseMode", "toggle-internal-edit");
1392                 }
1393         }
1394
1395         button_selection (item, event, item_type);
1396
1397         if (!_drags->active () &&
1398             (Keyboard::is_delete_event (&event->button) ||
1399              Keyboard::is_context_menu_event (&event->button) ||
1400              Keyboard::is_edit_event (&event->button))) {
1401
1402                 /* handled by button release */
1403                 return true;
1404         }
1405
1406         //not rolling, range mode click + join_play_range :  locate the PH here
1407         if ( !_drags->active () && !_session->transport_rolling() && ( effective_mouse_mode() == MouseRange ) && Config->get_always_play_range() ) {
1408                 framepos_t where = event_frame (event, 0, 0);
1409                 snap_to(where);
1410                 _session->request_locate (where, false);
1411         }
1412
1413         switch (event->button.button) {
1414         case 1:
1415                 return button_press_handler_1 (item, event, item_type);
1416                 break;
1417
1418         case 2:
1419                 return button_press_handler_2 (item, event, item_type);
1420                 break;
1421
1422         case 3:
1423                 break;
1424
1425         default:
1426                 return button_press_dispatch (&event->button);
1427                 break;
1428
1429         }
1430
1431         return false;
1432 }
1433
1434 bool
1435 Editor::button_press_dispatch (GdkEventButton* ev)
1436 {
1437         /* this function is intended only for buttons 4 and above.
1438          */
1439
1440         Gtkmm2ext::MouseButton b (ev->state, ev->button);
1441         return button_bindings->activate (b, Gtkmm2ext::Bindings::Press);
1442 }
1443
1444 bool
1445 Editor::button_release_dispatch (GdkEventButton* ev)
1446 {
1447         /* this function is intended only for buttons 4 and above.
1448          */
1449
1450         Gtkmm2ext::MouseButton b (ev->state, ev->button);
1451         return button_bindings->activate (b, Gtkmm2ext::Bindings::Release);
1452 }
1453
1454 bool
1455 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1456 {
1457         framepos_t where = event_frame (event, 0, 0);
1458         AutomationTimeAxisView* atv = 0;
1459
1460         if (pre_press_cursor) {
1461                 set_canvas_cursor (pre_press_cursor);
1462                 pre_press_cursor = 0;
1463         }
1464
1465         /* no action if we're recording */
1466
1467         if (_session && _session->actively_recording()) {
1468                 return true;
1469         }
1470
1471         /* see if we're finishing a drag */
1472
1473         bool were_dragging = false;
1474         if (_drags->active ()) {
1475                 bool const r = _drags->end_grab (event);
1476                 if (r) {
1477                         /* grab dragged, so do nothing else */
1478                         return true;
1479                 }
1480
1481                 were_dragging = true;
1482         }
1483
1484     update_region_layering_order_editor ();
1485
1486         /* edit events get handled here */
1487
1488         if (!_drags->active () && Keyboard::is_edit_event (&event->button)) {
1489                 switch (item_type) {
1490                 case RegionItem:
1491                         show_region_properties ();
1492                         break;
1493
1494                 case TempoMarkerItem:
1495                         edit_tempo_marker (item);
1496                         break;
1497
1498                 case MeterMarkerItem:
1499                         edit_meter_marker (item);
1500                         break;
1501
1502                 case RegionViewName:
1503                         if (clicked_regionview->name_active()) {
1504                                 return mouse_rename_region (item, event);
1505                         }
1506                         break;
1507
1508                 case ControlPointItem:
1509                         edit_control_point (item);
1510                         break;
1511
1512                 case NoteItem:
1513                 {
1514                         NoteBase* e = reinterpret_cast<NoteBase*> (item->get_data ("notebase"));
1515                         assert (e);
1516                         edit_notes (e->region_view().selection ());
1517                         break;
1518                 }
1519
1520                 default:
1521                         break;
1522                 }
1523                 return true;
1524         }
1525
1526         /* context menu events get handled here */
1527         if (Keyboard::is_context_menu_event (&event->button)) {
1528
1529                 context_click_event = *event;
1530
1531                 if (!_drags->active ()) {
1532
1533                         /* no matter which button pops up the context menu, tell the menu
1534                            widget to use button 1 to drive menu selection.
1535                         */
1536
1537                         switch (item_type) {
1538                         case FadeInItem:
1539                         case FadeInHandleItem:
1540                         case FadeOutItem:
1541                         case FadeOutHandleItem:
1542                                 popup_fade_context_menu (1, event->button.time, item, item_type);
1543                                 break;
1544
1545                         case StartCrossFadeItem:
1546                                 popup_xfade_in_context_menu (1, event->button.time, item, item_type);
1547                                 break;
1548
1549                         case EndCrossFadeItem:
1550                                 popup_xfade_out_context_menu (1, event->button.time, item, item_type);
1551                                 break;
1552
1553                         case StreamItem:
1554                                 popup_track_context_menu (1, event->button.time, item_type, false);
1555                                 break;
1556
1557                         case RegionItem:
1558                         case RegionViewNameHighlight:
1559                         case LeftFrameHandle:
1560                         case RightFrameHandle:
1561                         case RegionViewName:
1562                                 popup_track_context_menu (1, event->button.time, item_type, false);
1563                                 break;
1564
1565                         case SelectionItem:
1566                                 popup_track_context_menu (1, event->button.time, item_type, true);
1567                                 break;
1568                                 
1569                         case AutomationTrackItem:
1570                                 popup_track_context_menu (1, event->button.time, item_type, false);
1571                                 break;
1572
1573                         case MarkerBarItem:
1574                         case RangeMarkerBarItem:
1575                         case TransportMarkerBarItem:
1576                         case CdMarkerBarItem:
1577                         case TempoBarItem:
1578                         case MeterBarItem:
1579                         case VideoBarItem:
1580                                 popup_ruler_menu (where, item_type);
1581                                 break;
1582
1583                         case MarkerItem:
1584                                 marker_context_menu (&event->button, item);
1585                                 break;
1586
1587                         case TempoMarkerItem:
1588                                 tempo_or_meter_marker_context_menu (&event->button, item);
1589                                 break;
1590
1591                         case MeterMarkerItem:
1592                                 tempo_or_meter_marker_context_menu (&event->button, item);
1593                                 break;
1594
1595                         case CrossfadeViewItem:
1596                                 popup_track_context_menu (1, event->button.time, item_type, false);
1597                                 break;
1598
1599                         case ControlPointItem:
1600                                 popup_control_point_context_menu (item, event);
1601                                 break;
1602
1603 #ifdef WITH_CMT
1604                         case ImageFrameItem:
1605                                 popup_imageframe_edit_menu(1, event->button.time, item, true) ;
1606                                 break ;
1607                         case ImageFrameTimeAxisItem:
1608                                 popup_imageframe_edit_menu(1, event->button.time, item, false) ;
1609                                 break ;
1610                         case MarkerViewItem:
1611                                 popup_marker_time_axis_edit_menu(1, event->button.time, item, true) ;
1612                                 break ;
1613                         case MarkerTimeAxisItem:
1614                                 popup_marker_time_axis_edit_menu(1, event->button.time, item, false) ;
1615                                 break ;
1616 #endif
1617
1618                         default:
1619                                 break;
1620                         }
1621
1622                         return true;
1623                 }
1624         }
1625
1626         /* delete events get handled here */
1627
1628         Editing::MouseMode const eff = effective_mouse_mode ();
1629
1630         if (!_drags->active () && Keyboard::is_delete_event (&event->button)) {
1631
1632                 switch (item_type) {
1633                 case TempoMarkerItem:
1634                         remove_tempo_marker (item);
1635                         break;
1636
1637                 case MeterMarkerItem:
1638                         remove_meter_marker (item);
1639                         break;
1640
1641                 case MarkerItem:
1642                         remove_marker (*item, event);
1643                         break;
1644
1645                 case RegionItem:
1646                         if (eff == MouseObject) {
1647                                 remove_clicked_region ();
1648                         }
1649                         break;
1650
1651                 case ControlPointItem:
1652                         remove_control_point (item);
1653                         break;
1654
1655                 case NoteItem:
1656                         remove_midi_note (item, event);
1657                         break;
1658
1659                 default:
1660                         break;
1661                 }
1662                 return true;
1663         }
1664
1665         switch (event->button.button) {
1666         case 1:
1667
1668                 switch (item_type) {
1669                 /* see comments in button_press_handler */
1670                 case PlayheadCursorItem:
1671                 case MarkerItem:
1672                 case GainLineItem:
1673                 case AutomationLineItem:
1674                 case StartSelectionTrimItem:
1675                 case EndSelectionTrimItem:
1676                         return true;
1677
1678                 case MarkerBarItem:
1679                         if (!_dragging_playhead) {
1680                                 snap_to_with_modifier (where, event, 0, true);
1681                                 mouse_add_new_marker (where);
1682                         }
1683                         return true;
1684
1685                 case CdMarkerBarItem:
1686                         if (!_dragging_playhead) {
1687                                 // if we get here then a dragged range wasn't done
1688                                 snap_to_with_modifier (where, event, 0, true);
1689                                 mouse_add_new_marker (where, true);
1690                         }
1691                         return true;
1692
1693                 case TempoBarItem:
1694                         if (!_dragging_playhead) {
1695                                 snap_to_with_modifier (where, event);
1696                                 mouse_add_new_tempo_event (where);
1697                         }
1698                         return true;
1699
1700                 case MeterBarItem:
1701                         if (!_dragging_playhead) {
1702                                 mouse_add_new_meter_event (pixel_to_frame (event->button.x));
1703                         }
1704                         return true;
1705                         break;
1706
1707                 default:
1708                         break;
1709                 }
1710
1711                 switch (eff) {
1712                 case MouseObject:
1713                         switch (item_type) {
1714                         case AutomationTrackItem:
1715                                 atv = dynamic_cast<AutomationTimeAxisView*>(clicked_axisview);
1716                                 if (atv) {
1717                                         atv->add_automation_event (event, where, event->button.y);
1718                                 }
1719                                 return true;
1720                                 break;
1721                         default:
1722                                 break;
1723                         }
1724                         break;
1725
1726                 case MouseGain:
1727                         switch (item_type) {
1728                         case RegionItem:
1729                         {
1730                                 /* check that we didn't drag before releasing, since
1731                                    its really annoying to create new control
1732                                    points when doing this.
1733                                 */
1734                                 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (clicked_regionview);
1735                                 if (!were_dragging && arv) {
1736                                         arv->add_gain_point_event (item, event);
1737                                 }
1738                                 return true;
1739                                 break;
1740                         }
1741
1742                         case AutomationTrackItem:
1743                                 dynamic_cast<AutomationTimeAxisView*>(clicked_axisview)->
1744                                         add_automation_event (event, where, event->button.y);
1745                                 return true;
1746                                 break;
1747                         default:
1748                                 break;
1749                         }
1750                         break;
1751
1752                 case MouseAudition:
1753                         set_canvas_cursor (current_canvas_cursor);
1754                         if (scrubbing_direction == 0) {
1755                                 /* no drag, just a click */
1756                                 switch (item_type) {
1757                                 case RegionItem:
1758                                         play_selected_region ();
1759                                         break;
1760                                 default:
1761                                         break;
1762                                 }
1763                         } else {
1764                                 /* make sure we stop */
1765                                 _session->request_transport_speed (0.0);
1766                         }
1767                         break;
1768
1769                 default:
1770                         break;
1771
1772                 }
1773
1774                 /* do any (de)selection operations that should occur on button release */
1775                 button_selection (item, event, item_type);
1776                 return true;
1777                 break;
1778
1779
1780         case 2:
1781                 switch (eff) {
1782
1783                 case MouseObject:
1784                         switch (item_type) {
1785                         case RegionItem:
1786                                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1787                                         raise_region ();
1788                                 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier|Keyboard::SecondaryModifier))) {
1789                                         lower_region ();
1790                                 } else {
1791                                         // Button2 click is unused
1792                                 }
1793                                 return true;
1794
1795                                 break;
1796
1797                         default:
1798                                 break;
1799                         }
1800                         break;
1801
1802                 case MouseDraw:
1803                         return true;
1804                         
1805                 case MouseRange:
1806                         // x_style_paste (where, 1.0);
1807                         return true;
1808                         break;
1809
1810                 default:
1811                         break;
1812                 }
1813
1814                 break;
1815
1816         case 3:
1817                 break;
1818
1819         default:
1820                 break;
1821         }
1822
1823         return false;
1824 }
1825
1826 bool
1827 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1828 {
1829         ControlPoint* cp;
1830         Marker * marker;
1831         double fraction;
1832         bool ret = true;
1833
1834         switch (item_type) {
1835         case ControlPointItem:
1836                 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1837                         cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1838                         cp->set_visible (true);
1839
1840                         double at_x, at_y;
1841                         at_x = cp->get_x();
1842                         at_y = cp->get_y ();
1843                         cp->i2w (at_x, at_y);
1844                         at_x += 10.0;
1845                         at_y += 10.0;
1846
1847                         fraction = 1.0 - (cp->get_y() / cp->line().height());
1848
1849                         if (is_drawable() && !_drags->active ()) {
1850                                 set_canvas_cursor (_cursors->fader);
1851                         }
1852
1853                         _verbose_cursor->set (cp->line().get_verbose_cursor_string (fraction), at_x, at_y);
1854                         _verbose_cursor->show ();
1855                 }
1856                 break;
1857
1858         case GainLineItem:
1859                 if (mouse_mode == MouseGain) {
1860                         ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1861                         if (line) {
1862                                 line->set_outline_color (ARDOUR_UI::config()->canvasvar_EnteredGainLine.get());
1863                         }
1864                         if (is_drawable()) {
1865                                 set_canvas_cursor (_cursors->fader);
1866                         }
1867                 }
1868                 break;
1869
1870         case AutomationLineItem:
1871                 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1872                         ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1873                         if (line) {
1874                                 line->set_outline_color (ARDOUR_UI::config()->canvasvar_EnteredAutomationLine.get());
1875                         }
1876                         if (is_drawable()) {
1877                                 set_canvas_cursor (_cursors->fader);
1878                         }
1879                 }
1880                 break;
1881
1882         case RegionViewNameHighlight:
1883                 if (is_drawable() && effective_mouse_mode() == MouseObject && entered_regionview) {
1884                         set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1885                         _over_region_trim_target = true;
1886                 }
1887                 break;
1888
1889         case LeftFrameHandle:
1890         case RightFrameHandle:
1891                 if (is_drawable() && effective_mouse_mode() == MouseObject && !internal_editing() && entered_regionview) {
1892                         set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1893                 }
1894                 break;
1895
1896         case StartSelectionTrimItem:
1897 #ifdef WITH_CMT
1898         case ImageFrameHandleStartItem:
1899         case MarkerViewHandleStartItem:
1900 #endif
1901                 if (is_drawable()) {
1902                         set_canvas_cursor (_cursors->left_side_trim);
1903                 }
1904                 break;
1905         case EndSelectionTrimItem:
1906 #ifdef WITH_CMT
1907         case ImageFrameHandleEndItem:
1908         case MarkerViewHandleEndItem:
1909 #endif
1910                 if (is_drawable()) {
1911                         set_canvas_cursor (_cursors->right_side_trim);
1912                 }
1913                 break;
1914
1915         case PlayheadCursorItem:
1916                 if (is_drawable()) {
1917                         switch (_edit_point) {
1918                         case EditAtMouse:
1919                                 set_canvas_cursor (_cursors->grabber_edit_point);
1920                                 break;
1921                         default:
1922                                 set_canvas_cursor (_cursors->grabber);
1923                                 break;
1924                         }
1925                 }
1926                 break;
1927
1928         case RegionViewName:
1929
1930                 /* when the name is not an active item, the entire name highlight is for trimming */
1931
1932                 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1933                         if (mouse_mode == MouseObject && is_drawable()) {
1934                                 set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1935                                 _over_region_trim_target = true;
1936                         }
1937                 }
1938                 break;
1939
1940
1941         case AutomationTrackItem:
1942                 if (is_drawable()) {
1943                         Gdk::Cursor *cursor;
1944                         switch (mouse_mode) {
1945                         case MouseRange:
1946                                 cursor = _cursors->selector;
1947                                 break;
1948                         case MouseZoom:
1949                                 cursor = _cursors->zoom_in;
1950                                 break;
1951                         default:
1952                                 cursor = _cursors->cross_hair;
1953                                 break;
1954                         }
1955
1956                         set_canvas_cursor (cursor);
1957
1958                         AutomationTimeAxisView* atv;
1959                         if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1960                                 clear_entered_track = false;
1961                                 set_entered_track (atv);
1962                         }
1963                 }
1964                 break;
1965
1966         case MarkerBarItem:
1967         case RangeMarkerBarItem:
1968         case TransportMarkerBarItem:
1969         case CdMarkerBarItem:
1970         case MeterBarItem:
1971         case TempoBarItem:
1972                 if (is_drawable()) {
1973                         set_canvas_cursor (_cursors->timebar);
1974                 }
1975                 break;
1976
1977         case MarkerItem:
1978                 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1979                         break;
1980                 }
1981                 entered_marker = marker;
1982                 marker->set_color_rgba (ARDOUR_UI::config()->canvasvar_EnteredMarker.get());
1983                 // fall through
1984         case MeterMarkerItem:
1985         case TempoMarkerItem:
1986                 if (is_drawable()) {
1987                         set_canvas_cursor (_cursors->timebar);
1988                 }
1989                 break;
1990
1991         case FadeInHandleItem:
1992                 if (mouse_mode == MouseObject && !internal_editing()) {
1993                         ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
1994                         if (rect) {
1995                                 rect->set_fill_color (0xBBBBBBAA);
1996                         }
1997                         set_canvas_cursor (_cursors->fade_in);
1998                 }
1999                 break;
2000
2001         case FadeOutHandleItem:
2002                 if (mouse_mode == MouseObject && !internal_editing()) {
2003                         ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
2004                         if (rect) {
2005                                 rect->set_fill_color (0xBBBBBBAA);
2006                         }
2007                         set_canvas_cursor (_cursors->fade_out);
2008                 }
2009                 break;
2010         case FeatureLineItem:
2011         {
2012                 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
2013                 line->set_outline_color (0xFF0000FF);
2014         }
2015         break;
2016         
2017         case SelectionItem:
2018                 if ( get_smart_mode() ) {
2019                         set_canvas_cursor ();
2020                 }
2021                 break;
2022
2023         default:
2024                 break;
2025         }
2026
2027         /* second pass to handle entered track status in a comprehensible way.
2028          */
2029
2030         switch (item_type) {
2031         case GainLineItem:
2032         case AutomationLineItem:
2033         case ControlPointItem:
2034                 /* these do not affect the current entered track state */
2035                 clear_entered_track = false;
2036                 break;
2037
2038         case AutomationTrackItem:
2039                 /* handled above already */
2040                 break;
2041
2042         default:
2043                 set_entered_track (0);
2044                 break;
2045         }
2046
2047         return ret;
2048 }
2049
2050 bool
2051 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent*, ItemType item_type)
2052 {
2053         AutomationLine* al;
2054         ControlPoint* cp;
2055         Marker *marker;
2056         Location *loc;
2057         RegionView* rv;
2058         bool is_start;
2059         bool ret = true;
2060
2061         switch (item_type) {
2062         case ControlPointItem:
2063                 cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
2064                 if (cp->line().the_list()->interpolation() != AutomationList::Discrete) {
2065                         if (cp->line().npoints() > 1 && !cp->get_selected()) {
2066                                 cp->set_visible (false);
2067                         }
2068                 }
2069
2070                 if (is_drawable()) {
2071                         set_canvas_cursor (current_canvas_cursor);
2072                 }
2073
2074                 _verbose_cursor->hide ();
2075                 break;
2076
2077         case RegionViewNameHighlight:
2078         case LeftFrameHandle:
2079         case RightFrameHandle:
2080         case StartSelectionTrimItem:
2081         case EndSelectionTrimItem:
2082         case PlayheadCursorItem:
2083
2084 #ifdef WITH_CMT
2085         case ImageFrameHandleStartItem:
2086         case ImageFrameHandleEndItem:
2087         case MarkerViewHandleStartItem:
2088         case MarkerViewHandleEndItem:
2089 #endif
2090
2091                 _over_region_trim_target = false;
2092
2093                 if (is_drawable()) {
2094                         set_canvas_cursor (current_canvas_cursor);
2095                 }
2096                 break;
2097
2098         case GainLineItem:
2099         case AutomationLineItem:
2100                 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
2101                 {
2102                         ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
2103                         if (line) {
2104                                 line->set_outline_color (al->get_line_color());
2105                         }
2106                 }
2107                 if (is_drawable()) {
2108                         set_canvas_cursor (current_canvas_cursor);
2109                 }
2110                 break;
2111
2112         case RegionViewName:
2113                 /* see enter_handler() for notes */
2114                 _over_region_trim_target = false;
2115
2116                 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
2117                         if (is_drawable() && mouse_mode == MouseObject) {
2118                                 set_canvas_cursor (current_canvas_cursor);
2119                         }
2120                 }
2121                 break;
2122
2123         case RangeMarkerBarItem:
2124         case TransportMarkerBarItem:
2125         case CdMarkerBarItem:
2126         case MeterBarItem:
2127         case TempoBarItem:
2128         case MarkerBarItem:
2129                 if (is_drawable()) {
2130                         set_canvas_cursor (current_canvas_cursor);
2131                 }
2132                 break;
2133
2134         case MarkerItem:
2135                 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2136                         break;
2137                 }
2138                 entered_marker = 0;
2139                 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
2140                         location_flags_changed (loc, this);
2141                 }
2142                 // fall through
2143         case MeterMarkerItem:
2144         case TempoMarkerItem:
2145
2146                 if (is_drawable()) {
2147                         set_canvas_cursor (current_canvas_cursor);
2148                 }
2149
2150                 break;
2151
2152         case FadeInHandleItem:
2153         case FadeOutHandleItem:
2154                 rv = static_cast<RegionView*>(item->get_data ("regionview"));
2155                 {
2156                         ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
2157                         if (rect) {
2158                                 rect->set_fill_color (rv->get_fill_color());
2159                         }
2160                 }
2161                 set_canvas_cursor (current_canvas_cursor);
2162                 break;
2163
2164         case AutomationTrackItem:
2165                 if (is_drawable()) {
2166                         set_canvas_cursor (current_canvas_cursor);
2167                         clear_entered_track = true;
2168                         Glib::signal_idle().connect (sigc::mem_fun(*this, &Editor::left_automation_track));
2169                 }
2170                 break;
2171         case FeatureLineItem:
2172                 {
2173                         ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
2174                         line->set_outline_color (ARDOUR_UI::config()->canvasvar_ZeroLine.get());
2175                 }
2176                 break;
2177
2178         default:
2179                 break;
2180         }
2181
2182         return ret;
2183 }
2184
2185 gint
2186 Editor::left_automation_track ()
2187 {
2188         if (clear_entered_track) {
2189                 set_entered_track (0);
2190                 clear_entered_track = false;
2191         }
2192         return false;
2193 }
2194
2195 void
2196 Editor::scrub (framepos_t frame, double current_x)
2197 {
2198         double delta;
2199
2200         if (scrubbing_direction == 0) {
2201                 /* first move */
2202                 _session->request_locate (frame, false);
2203                 _session->request_transport_speed (0.1);
2204                 scrubbing_direction = 1;
2205
2206         } else {
2207
2208                 if (last_scrub_x > current_x) {
2209
2210                         /* pointer moved to the left */
2211
2212                         if (scrubbing_direction > 0) {
2213
2214                                 /* we reversed direction to go backwards */
2215
2216                                 scrub_reversals++;
2217                                 scrub_reverse_distance += (int) (last_scrub_x - current_x);
2218
2219                         } else {
2220
2221                                 /* still moving to the left (backwards) */
2222
2223                                 scrub_reversals = 0;
2224                                 scrub_reverse_distance = 0;
2225
2226                                 delta = 0.01 * (last_scrub_x - current_x);
2227                                 _session->request_transport_speed_nonzero (_session->transport_speed() - delta);
2228                         }
2229
2230                 } else {
2231                         /* pointer moved to the right */
2232
2233                         if (scrubbing_direction < 0) {
2234                                 /* we reversed direction to go forward */
2235
2236                                 scrub_reversals++;
2237                                 scrub_reverse_distance += (int) (current_x - last_scrub_x);
2238
2239                         } else {
2240                                 /* still moving to the right */
2241
2242                                 scrub_reversals = 0;
2243                                 scrub_reverse_distance = 0;
2244
2245                                 delta = 0.01 * (current_x - last_scrub_x);
2246                                 _session->request_transport_speed_nonzero (_session->transport_speed() + delta);
2247                         }
2248                 }
2249
2250                 /* if there have been more than 2 opposite motion moves detected, or one that moves
2251                    back more than 10 pixels, reverse direction
2252                 */
2253
2254                 if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
2255
2256                         if (scrubbing_direction > 0) {
2257                                 /* was forwards, go backwards */
2258                                 _session->request_transport_speed (-0.1);
2259                                 scrubbing_direction = -1;
2260                         } else {
2261                                 /* was backwards, go forwards */
2262                                 _session->request_transport_speed (0.1);
2263                                 scrubbing_direction = 1;
2264                         }
2265
2266                         scrub_reverse_distance = 0;
2267                         scrub_reversals = 0;
2268                 }
2269         }
2270
2271         last_scrub_x = current_x;
2272 }
2273
2274 bool
2275 Editor::motion_handler (ArdourCanvas::Item* /*item*/, GdkEvent* event, bool from_autoscroll)
2276 {
2277         _last_motion_y = event->motion.y;
2278
2279         if (event->motion.is_hint) {
2280                 gint x, y;
2281
2282                 /* We call this so that MOTION_NOTIFY events continue to be
2283                    delivered to the canvas. We need to do this because we set
2284                    Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
2285                    the density of the events, at the expense of a round-trip
2286                    to the server. Given that this will mostly occur on cases
2287                    where DISPLAY = :0.0, and given the cost of what the motion
2288                    event might do, its a good tradeoff.
2289                 */
2290
2291                 _track_canvas->get_pointer (x, y);
2292         }
2293
2294         if (current_stepping_trackview) {
2295                 /* don't keep the persistent stepped trackview if the mouse moves */
2296                 current_stepping_trackview = 0;
2297                 step_timeout.disconnect ();
2298         }
2299
2300         if (_session && _session->actively_recording()) {
2301                 /* Sorry. no dragging stuff around while we record */
2302                 return true;
2303         }
2304
2305         JoinObjectRangeState const old = _join_object_range_state;
2306         update_join_object_range_location (event->motion.x, event->motion.y);
2307
2308         if (!_internal_editing && _join_object_range_state != old) {
2309                 set_canvas_cursor ();
2310         }
2311
2312         if (!_internal_editing && _over_region_trim_target) {
2313                 set_canvas_cursor_for_region_view (event->motion.x, entered_regionview);
2314         }
2315
2316         bool handled = false;
2317         if (_drags->active ()) {
2318                 handled = _drags->motion_handler (event, from_autoscroll);
2319         }
2320
2321         if (!handled) {
2322                 return false;
2323         }
2324
2325         track_canvas_motion (event);
2326         return true;
2327 }
2328
2329 bool
2330 Editor::can_remove_control_point (ArdourCanvas::Item* item)
2331 {
2332         ControlPoint* control_point;
2333
2334         if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2335                 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2336                 /*NOTREACHED*/
2337         }
2338
2339         AutomationLine& line = control_point->line ();
2340         if (dynamic_cast<AudioRegionGainLine*> (&line)) {
2341                 /* we shouldn't remove the first or last gain point in region gain lines */
2342                 if (line.is_last_point(*control_point) || line.is_first_point(*control_point)) {
2343                         return false;
2344                 }
2345         }
2346
2347         return true;
2348 }
2349
2350 void
2351 Editor::remove_control_point (ArdourCanvas::Item* item)
2352 {
2353         if (!can_remove_control_point (item)) {
2354                 return;
2355         }
2356
2357         ControlPoint* control_point;
2358
2359         if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2360                 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2361                 /*NOTREACHED*/
2362         }
2363
2364         control_point->line().remove_point (*control_point);
2365 }
2366
2367 void
2368 Editor::edit_control_point (ArdourCanvas::Item* item)
2369 {
2370         ControlPoint* p = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"));
2371
2372         if (p == 0) {
2373                 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2374                 /*NOTREACHED*/
2375         }
2376
2377         ControlPointDialog d (p);
2378         d.set_position (Gtk::WIN_POS_MOUSE);
2379         ensure_float (d);
2380
2381         if (d.run () != RESPONSE_ACCEPT) {
2382                 return;
2383         }
2384
2385         p->line().modify_point_y (*p, d.get_y_fraction ());
2386 }
2387
2388 void
2389 Editor::edit_notes (MidiRegionView::Selection const & s)
2390 {
2391         if (s.empty ()) {
2392                 return;
2393         }
2394         
2395         EditNoteDialog d (&(*s.begin())->region_view(), s);
2396         d.set_position (Gtk::WIN_POS_MOUSE);
2397         ensure_float (d);
2398
2399         d.run ();
2400 }
2401
2402
2403 void
2404 Editor::visible_order_range (int* low, int* high) const
2405 {
2406         *low = TimeAxisView::max_order ();
2407         *high = 0;
2408
2409         for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
2410
2411                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
2412
2413                 if (!rtv->hidden()) {
2414
2415                         if (*high < rtv->order()) {
2416                                 *high = rtv->order ();
2417                         }
2418
2419                         if (*low > rtv->order()) {
2420                                 *low = rtv->order ();
2421                         }
2422                 }
2423         }
2424 }
2425
2426 void
2427 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
2428 {
2429         /* Either add to or set the set the region selection, unless
2430            this is an alignment click (control used)
2431         */
2432
2433         if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
2434                 TimeAxisView* tv = &rv.get_time_axis_view();
2435                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
2436                 double speed = 1.0;
2437                 if (rtv && rtv->is_track()) {
2438                         speed = rtv->track()->speed();
2439                 }
2440
2441                 framepos_t where = get_preferred_edit_position();
2442
2443                 if (where >= 0) {
2444
2445                         if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
2446
2447                                 align_region (rv.region(), SyncPoint, (framepos_t) (where * speed));
2448
2449                         } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
2450
2451                                 align_region (rv.region(), End, (framepos_t) (where * speed));
2452
2453                         } else {
2454
2455                                 align_region (rv.region(), Start, (framepos_t) (where * speed));
2456                         }
2457                 }
2458         }
2459 }
2460
2461 void
2462 Editor::collect_new_region_view (RegionView* rv)
2463 {
2464         latest_regionviews.push_back (rv);
2465 }
2466
2467 void
2468 Editor::collect_and_select_new_region_view (RegionView* rv)
2469 {
2470         selection->add(rv);
2471         latest_regionviews.push_back (rv);
2472 }
2473
2474 void
2475 Editor::cancel_selection ()
2476 {
2477         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2478                 (*i)->hide_selection ();
2479         }
2480
2481         selection->clear ();
2482         clicked_selection = 0;
2483 }
2484
2485 void
2486 Editor::cancel_time_selection ()
2487 {
2488     for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2489                 (*i)->hide_selection ();
2490         }
2491         selection->time.clear ();
2492         clicked_selection = 0;
2493 }       
2494
2495 void
2496 Editor::point_trim (GdkEvent* event, framepos_t new_bound)
2497 {
2498         RegionView* rv = clicked_regionview;
2499
2500         /* Choose action dependant on which button was pressed */
2501         switch (event->button.button) {
2502         case 1:
2503                 begin_reversible_command (_("start point trim"));
2504
2505                 if (selection->selected (rv)) {
2506                         for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
2507                              i != selection->regions.by_layer().end(); ++i)
2508                         {
2509                                 if (!(*i)->region()->locked()) {
2510                                         (*i)->region()->clear_changes ();
2511                                         (*i)->region()->trim_front (new_bound);
2512                                         _session->add_command(new StatefulDiffCommand ((*i)->region()));
2513                                 }
2514                         }
2515
2516                 } else {
2517                         if (!rv->region()->locked()) {
2518                                 rv->region()->clear_changes ();
2519                                 rv->region()->trim_front (new_bound);
2520                                 _session->add_command(new StatefulDiffCommand (rv->region()));
2521                         }
2522                 }
2523
2524                 commit_reversible_command();
2525
2526                 break;
2527         case 2:
2528                 begin_reversible_command (_("End point trim"));
2529
2530                 if (selection->selected (rv)) {
2531
2532                         for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
2533                         {
2534                                 if (!(*i)->region()->locked()) {
2535                                         (*i)->region()->clear_changes();
2536                                         (*i)->region()->trim_end (new_bound);
2537                                         _session->add_command(new StatefulDiffCommand ((*i)->region()));
2538                                 }
2539                         }
2540
2541                 } else {
2542
2543                         if (!rv->region()->locked()) {
2544                                 rv->region()->clear_changes ();
2545                                 rv->region()->trim_end (new_bound);
2546                                 _session->add_command (new StatefulDiffCommand (rv->region()));
2547                         }
2548                 }
2549
2550                 commit_reversible_command();
2551
2552                 break;
2553         default:
2554                 break;
2555         }
2556 }
2557
2558 void
2559 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* /*event*/)
2560 {
2561         Marker* marker;
2562         bool is_start;
2563
2564         if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2565                 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2566                 /*NOTREACHED*/
2567         }
2568
2569         Location* location = find_location_from_marker (marker, is_start);
2570         location->set_hidden (true, this);
2571 }
2572
2573
2574 void
2575 Editor::reposition_zoom_rect (framepos_t start, framepos_t end)
2576 {
2577         double x1 = frame_to_pixel (start);
2578         double x2 = frame_to_pixel (end);
2579         double y2 = _full_canvas_height - 1.0;
2580
2581         zoom_rect->set (ArdourCanvas::Rect (x1, 1.0, x2, y2));
2582 }
2583
2584
2585 gint
2586 Editor::mouse_rename_region (ArdourCanvas::Item* /*item*/, GdkEvent* /*event*/)
2587 {
2588         using namespace Gtkmm2ext;
2589
2590         ArdourPrompter prompter (false);
2591
2592         prompter.set_prompt (_("Name for region:"));
2593         prompter.set_initial_text (clicked_regionview->region()->name());
2594         prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
2595         prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
2596         prompter.show_all ();
2597         switch (prompter.run ()) {
2598         case Gtk::RESPONSE_ACCEPT:
2599                 string str;
2600                 prompter.get_result(str);
2601                 if (str.length()) {
2602                         clicked_regionview->region()->set_name (str);
2603                 }
2604                 break;
2605         }
2606         return true;
2607 }
2608
2609
2610 void
2611 Editor::mouse_brush_insert_region (RegionView* rv, framepos_t pos)
2612 {
2613         /* no brushing without a useful snap setting */
2614
2615         switch (_snap_mode) {
2616         case SnapMagnetic:
2617                 return; /* can't work because it allows region to be placed anywhere */
2618         default:
2619                 break; /* OK */
2620         }
2621
2622         switch (_snap_type) {
2623         case SnapToMark:
2624                 return;
2625
2626         default:
2627                 break;
2628         }
2629
2630         /* don't brush a copy over the original */
2631
2632         if (pos == rv->region()->position()) {
2633                 return;
2634         }
2635
2636         RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
2637
2638         if (rtv == 0 || !rtv->is_track()) {
2639                 return;
2640         }
2641
2642         boost::shared_ptr<Playlist> playlist = rtv->playlist();
2643         double speed = rtv->track()->speed();
2644
2645         playlist->clear_changes ();
2646         boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region(), true));
2647         playlist->add_region (new_region, (framepos_t) (pos * speed));
2648         _session->add_command (new StatefulDiffCommand (playlist));
2649
2650         // playlist is frozen, so we have to update manually XXX this is disgusting
2651
2652         playlist->RegionAdded (new_region); /* EMIT SIGNAL */
2653 }
2654
2655 gint
2656 Editor::track_height_step_timeout ()
2657 {
2658         if (get_microseconds() - last_track_height_step_timestamp < 250000) {
2659                 current_stepping_trackview = 0;
2660                 return false;
2661         }
2662         return true;
2663 }
2664
2665 void
2666 Editor::add_region_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2667 {
2668         assert (region_view);
2669
2670         if (!region_view->region()->playlist()) {
2671                 return;
2672         }
2673
2674         _region_motion_group->raise_to_top ();
2675
2676         if (Config->get_edit_mode() == Splice) {
2677                 _drags->add (new RegionSpliceDrag (this, item, region_view, selection->regions.by_layer()));
2678         } else {
2679                 RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::select.property_id);
2680                 _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), false, false));
2681         }
2682
2683         /* sync the canvas to what we think is its current state */
2684         update_canvas_now();
2685 }
2686
2687 void
2688 Editor::add_region_copy_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2689 {
2690         assert (region_view);
2691
2692         if (!region_view->region()->playlist()) {
2693                 return;
2694         }
2695
2696         _region_motion_group->raise_to_top ();
2697
2698         RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::select.property_id);
2699         _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), false, true));
2700 }
2701
2702 void
2703 Editor::add_region_brush_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2704 {
2705         assert (region_view);
2706
2707         if (!region_view->region()->playlist()) {
2708                 return;
2709         }
2710
2711         if (Config->get_edit_mode() == Splice) {
2712                 return;
2713         }
2714
2715         RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::select.property_id);
2716         _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), true, false));
2717
2718         begin_reversible_command (Operations::drag_region_brush);
2719 }
2720
2721 /** Start a grab where a time range is selected, track(s) are selected, and the
2722  *  user clicks and drags a region with a modifier in order to create a new region containing
2723  *  the section of the clicked region that lies within the time range.
2724  */
2725 void
2726 Editor::start_selection_grab (ArdourCanvas::Item* /*item*/, GdkEvent* event)
2727 {
2728         if (clicked_regionview == 0) {
2729                 return;
2730         }
2731
2732         /* lets try to create new Region for the selection */
2733
2734         vector<boost::shared_ptr<Region> > new_regions;
2735         create_region_from_selection (new_regions);
2736
2737         if (new_regions.empty()) {
2738                 return;
2739         }
2740
2741         /* XXX fix me one day to use all new regions */
2742
2743         boost::shared_ptr<Region> region (new_regions.front());
2744
2745         /* add it to the current stream/playlist.
2746
2747            tricky: the streamview for the track will add a new regionview. we will
2748            catch the signal it sends when it creates the regionview to
2749            set the regionview we want to then drag.
2750         */
2751
2752         latest_regionviews.clear();
2753         sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
2754
2755         /* A selection grab currently creates two undo/redo operations, one for
2756            creating the new region and another for moving it.
2757         */
2758
2759         begin_reversible_command (Operations::selection_grab);
2760
2761         boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
2762
2763         playlist->clear_changes ();
2764         clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
2765         _session->add_command(new StatefulDiffCommand (playlist));
2766
2767         commit_reversible_command ();
2768
2769         c.disconnect ();
2770
2771         if (latest_regionviews.empty()) {
2772                 /* something went wrong */
2773                 return;
2774         }
2775
2776         /* we need to deselect all other regionviews, and select this one
2777            i'm ignoring undo stuff, because the region creation will take care of it
2778         */
2779         selection->set (latest_regionviews);
2780
2781         _drags->set (new RegionMoveDrag (this, latest_regionviews.front()->get_canvas_group(), latest_regionviews.front(), latest_regionviews, false, false), event);
2782 }
2783
2784 void
2785 Editor::escape ()
2786 {
2787         if (_drags->active ()) {
2788                 _drags->abort ();
2789         } else {
2790                 selection->clear ();
2791         }
2792 }
2793
2794 void
2795 Editor::set_internal_edit (bool yn)
2796 {
2797         if (_internal_editing == yn) {
2798                 return;
2799         }
2800
2801         _internal_editing = yn;
2802         
2803         if (yn) {
2804                 pre_internal_mouse_mode = mouse_mode;
2805                 pre_internal_snap_type = _snap_type;
2806                 pre_internal_snap_mode = _snap_mode;
2807
2808                 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2809                         (*i)->enter_internal_edit_mode ();
2810                 }
2811
2812                 set_snap_to (internal_snap_type);
2813                 set_snap_mode (internal_snap_mode);
2814
2815         } else {
2816
2817                 internal_snap_mode = _snap_mode;
2818                 internal_snap_type = _snap_type;
2819
2820                 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2821                         (*i)->leave_internal_edit_mode ();
2822                 }
2823
2824                 if (mouse_mode == MouseDraw && pre_internal_mouse_mode != MouseDraw) {
2825                         /* we were drawing .. flip back to something sensible */
2826                         set_mouse_mode (pre_internal_mouse_mode);
2827                 }
2828
2829                 set_snap_to (pre_internal_snap_type);
2830                 set_snap_mode (pre_internal_snap_mode);
2831         }
2832         
2833         set_canvas_cursor ();
2834 }
2835
2836 /** Update _join_object_range_state which indicate whether we are over the top or bottom half of a region view,
2837  *  used by the `join object/range' tool mode.
2838  */
2839 void
2840 Editor::update_join_object_range_location (double /*x*/, double y)
2841 {
2842         /* XXX: actually, this decides based on whether the mouse is in the top
2843            or bottom half of a the waveform part RouteTimeAxisView;
2844
2845            Note that entered_{track,regionview} is not always setup (e.g. if
2846            the mouse is over a TimeSelection), and to get a Region
2847            that we're over requires searching the playlist.
2848         */
2849
2850         if ( !get_smart_mode() ) {
2851                 _join_object_range_state = JOIN_OBJECT_RANGE_NONE;
2852                 return;
2853         }
2854
2855         if (mouse_mode == MouseObject) {
2856                 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2857         } else if (mouse_mode == MouseRange) {
2858                 _join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
2859         }
2860
2861         /* XXX: maybe we should make entered_track work in all cases, rather than resorting to this */
2862         pair<TimeAxisView*, int> tvp = trackview_by_y_position (y + vertical_adjustment.get_value());
2863
2864         if (tvp.first) {
2865
2866                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
2867                 if (rtv) {
2868
2869                         double cx = 0;
2870                         double cy = y;
2871                         rtv->canvas_display()->canvas_to_item (cx, cy);
2872
2873                         double const c = cy / (rtv->view()->child_height() - TimeAxisViewItem::NAME_HIGHLIGHT_SIZE);
2874
2875                         _join_object_range_state = c <= 0.5 ? JOIN_OBJECT_RANGE_RANGE : JOIN_OBJECT_RANGE_OBJECT;
2876                 }
2877         }
2878 }
2879
2880 Editing::MouseMode
2881 Editor::effective_mouse_mode () const
2882 {
2883         if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
2884                 return MouseObject;
2885         } else if (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE) {
2886                 return MouseRange;
2887         }
2888
2889         return mouse_mode;
2890 }
2891
2892 void
2893 Editor::remove_midi_note (ArdourCanvas::Item* item, GdkEvent *)
2894 {
2895         NoteBase* e = reinterpret_cast<NoteBase*> (item->get_data ("notebase"));
2896         assert (e);
2897
2898         e->region_view().delete_note (e->note ());
2899 }
2900
2901 void
2902 Editor::set_canvas_cursor_for_region_view (double x, RegionView* rv)
2903 {
2904         /* XXX: this check should not be necessary */
2905         if (rv == 0) {
2906                 return;
2907         }
2908
2909         assert (rv);
2910
2911         ArdourCanvas::Group* g = rv->get_canvas_group ();
2912         ArdourCanvas::Group* p = g->parent ();
2913
2914         /* Compute x in region view parent coordinates */
2915         double dy = 0;
2916         p->canvas_to_item (x, dy);
2917
2918         boost::optional<ArdourCanvas::Rect> item_bbox = g->bounding_box ();
2919         assert (item_bbox);
2920         ArdourCanvas::Rect parent_bbox = g->item_to_parent (item_bbox.get ());
2921
2922         /* Halfway across the region */
2923         double const h = (parent_bbox.x0 + parent_bbox.x1) / 2;
2924
2925         Trimmable::CanTrim ct = rv->region()->can_trim ();
2926         if (x <= h) {
2927                 if (ct & Trimmable::FrontTrimEarlier) {
2928                         set_canvas_cursor (_cursors->left_side_trim);
2929                 } else {
2930                         set_canvas_cursor (_cursors->left_side_trim_right_only);
2931                 }
2932         } else {
2933                 if (ct & Trimmable::EndTrimLater) {
2934                         set_canvas_cursor (_cursors->right_side_trim);
2935                 } else {
2936                         set_canvas_cursor (_cursors->right_side_trim_left_only);
2937                 }
2938         }
2939 }
2940
2941 /** Obtain the pointer position in canvas coordinates */
2942 void
2943 Editor::get_pointer_position (double& x, double& y) const
2944 {
2945         int px, py;
2946         _track_canvas->get_pointer (px, py);
2947         _track_canvas_viewport->window_to_canvas (px, py, x, y);
2948 }