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