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