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