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