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