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