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