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