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