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