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