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