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