2 Copyright (C) 2000-2001 Paul Davis
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.
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.
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.
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"
35 #include "gtkmm2ext/bindings.h"
36 #include "gtkmm2ext/utils.h"
37 #include "gtkmm2ext/tearoff.h"
39 #include "canvas/canvas.h"
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"
50 #include "ardour_ui.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"
58 #include "streamview.h"
59 #include "region_gain_line.h"
60 #include "automation_time_axis.h"
61 #include "control_point.h"
64 #include "selection.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"
80 using namespace ARDOUR;
83 using namespace Editing;
84 using Gtkmm2ext::Keyboard;
87 Editor::mouse_frame (framepos_t& where, bool& in_track_canvas) const
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.
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().
100 if (!within_track_canvas) {
105 Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->_track_canvas->get_window();
107 if (!canvas_window) {
111 Glib::RefPtr<const Gdk::Window> pointer_window = Gdk::Display::get_default()->get_window_at_pointer (x, y);
113 if (!pointer_window) {
117 if (pointer_window != canvas_window && pointer_window != _time_bars_canvas->get_window()) {
118 in_track_canvas = false;
122 in_track_canvas = true;
125 event.type = GDK_BUTTON_RELEASE;
129 where = window_event_sample (&event, 0, 0);
135 Editor::window_event_sample (GdkEvent const * event, double* pcx, double* pcy) const
140 if (!gdk_event_get_coords (event, &x, &y)) {
144 /* event coordinates are in window units, so convert to canvas
145 * (i.e. account for scrolling)
148 ArdourCanvas::Duple d = _track_canvas->window_to_canvas (ArdourCanvas::Duple (x, y));
158 return pixel_to_sample (d.x);
162 Editor::canvas_event_sample (GdkEvent const * event, double* pcx, double* pcy) const
167 /* event coordinates are already in canvas units */
169 if (!gdk_event_get_coords (event, &x, &y)) {
170 cerr << "!NO c COORDS for event type " << event->type << endl;
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.
187 return pixel_to_sample_from_event (x);
191 Editor::which_grabber_cursor ()
193 Gdk::Cursor* c = _cursors->grabber;
195 if (_internal_editing) {
196 switch (mouse_mode) {
198 c = _cursors->midi_pencil;
202 c = _cursors->grabber_note;
206 c = _cursors->midi_resize;
210 c = _cursors->grabber_note;
219 switch (_edit_point) {
221 c = _cursors->grabber_edit_point;
224 boost::shared_ptr<Movable> m = _movable.lock();
225 if (m && m->locked()) {
226 c = _cursors->speaker;
236 Editor::set_current_trimmable (boost::shared_ptr<Trimmable> t)
238 boost::shared_ptr<Trimmable> st = _trimmable.lock();
240 if (!st || st == t) {
242 set_canvas_cursor ();
247 Editor::set_current_movable (boost::shared_ptr<Movable> m)
249 boost::shared_ptr<Movable> sm = _movable.lock();
251 if (!sm || sm != m) {
253 set_canvas_cursor ();
258 Editor::set_canvas_cursor ()
260 switch (mouse_mode) {
262 current_canvas_cursor = _cursors->selector;
263 if (_internal_editing) {
264 current_canvas_cursor = which_grabber_cursor();
269 current_canvas_cursor = which_grabber_cursor();
273 current_canvas_cursor = _cursors->midi_pencil;
277 current_canvas_cursor = _cursors->cross_hair;
281 if (Keyboard::the_keyboard().key_is_down (GDK_Control_L)) {
282 current_canvas_cursor = _cursors->zoom_out;
284 current_canvas_cursor = _cursors->zoom_in;
289 current_canvas_cursor = _cursors->time_fx; // just use playhead
293 current_canvas_cursor = _cursors->speaker;
297 if (!_internal_editing) {
298 switch (_join_object_range_state) {
299 case JOIN_OBJECT_RANGE_NONE:
301 case JOIN_OBJECT_RANGE_OBJECT:
302 current_canvas_cursor = which_grabber_cursor ();
304 case JOIN_OBJECT_RANGE_RANGE:
305 current_canvas_cursor = _cursors->selector;
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() ) {
314 get_pointer_position (x, y);
316 if (x >= 0 && y >= 0) {
318 vector<ArdourCanvas::Item const *> items;
320 _track_canvas->root()->add_items_at_point (ArdourCanvas::Duple (x,y), items);
322 // first item will be the upper most
324 if (!items.empty()) {
325 const ArdourCanvas::Item* i = items.front();
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;
337 set_canvas_cursor (current_canvas_cursor, true);
341 Editor::mouse_mode_object_range_toggled()
343 MouseMode m = mouse_mode;
345 Glib::RefPtr<Action> act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-range"));
347 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
350 if (tact->get_active()) {
351 m = MouseObject; //Smart mode turned to ON, force editing to Object mode
354 set_mouse_mode(m, true); //call this so the button styles can get updated
358 Editor::set_mouse_mode (MouseMode m, bool force)
360 if (_drags->active ()) {
364 if (!force && m == mouse_mode) {
368 Glib::RefPtr<Action> act;
372 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-range"));
376 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-object"));
380 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-draw"));
384 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-gain"));
388 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-zoom"));
392 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-timefx"));
396 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-audition"));
402 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
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);
409 //NOTE: this will result in a call to mouse_mode_toggled which does the heavy lifting
413 Editor::mouse_mode_toggled (MouseMode m)
415 Glib::RefPtr<Action> act;
416 Glib::RefPtr<ToggleAction> tact;
420 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-range"));
424 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-object"));
428 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-draw"));
432 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-gain"));
436 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-zoom"));
440 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-timefx"));
444 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-audition"));
450 tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
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
463 act = ActionManager::get_action (X_("MouseMode"), X_("toggle-internal-edit"));
464 tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
465 tact->set_active (true);
471 if (_session && mouse_mode == MouseAudition) {
472 /* stop transport and reset default speed to avoid oddness with
474 _session->request_transport_speed (0.0, true);
481 //TODO: set button styles for smart buttons
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");
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");
500 set_canvas_cursor ();
501 set_gain_envelope_visibility ();
503 update_time_selection_display ();
505 MouseModeChanged (); /* EMIT SIGNAL */
509 Editor::update_time_selection_display ()
511 if (smart_mode_action->get_active()) {
512 /* not sure what to do here */
513 if (mouse_mode == MouseObject) {
517 switch (mouse_mode) {
519 selection->clear_objects ();
522 selection->clear_time ();
529 Editor::step_mouse_mode (bool next)
531 switch (current_mouse_mode()) {
534 if (Profile->get_sae()) {
535 set_mouse_mode (MouseZoom);
537 set_mouse_mode (MouseRange);
540 set_mouse_mode (MouseTimeFX);
545 if (next) set_mouse_mode (MouseDraw);
546 else set_mouse_mode (MouseObject);
550 if (next) set_mouse_mode (MouseZoom);
551 else set_mouse_mode (MouseRange);
556 if (Profile->get_sae()) {
557 set_mouse_mode (MouseTimeFX);
559 set_mouse_mode (MouseGain);
562 if (Profile->get_sae()) {
563 set_mouse_mode (MouseObject);
565 set_mouse_mode (MouseDraw);
571 if (next) set_mouse_mode (MouseTimeFX);
572 else set_mouse_mode (MouseZoom);
577 set_mouse_mode (MouseAudition);
579 if (Profile->get_sae()) {
580 set_mouse_mode (MouseZoom);
582 set_mouse_mode (MouseGain);
588 if (next) set_mouse_mode (MouseObject);
589 else set_mouse_mode (MouseTimeFX);
595 Editor::toggle_internal_editing_from_double_click (GdkEvent* event)
597 if (_drags->active()) {
598 _drags->end_grab (event);
601 ActionManager::do_action ("MouseMode", "toggle-internal-edit");
603 /* prevent reversion of edit cursor on button release */
605 pre_press_cursor = 0;
611 Editor::button_selection (ArdourCanvas::Item* /*item*/, GdkEvent* event, ItemType item_type)
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
619 note: not dbl-click or triple-click
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.
626 MouseMode eff_mouse_mode = mouse_mode;
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.
632 eff_mouse_mode = MouseObject;
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)) {
646 if (event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) {
648 if ((event->button.state & Keyboard::RelevantModifierKeyMask) && event->button.button != 1) {
650 /* almost no selection action on modified button-2 or button-3 events */
652 if (item_type != RegionItem && event->button.button != 2) {
658 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
659 bool press = (event->type == GDK_BUTTON_PRESS);
664 if (eff_mouse_mode != MouseRange) {
665 set_selected_regionview_from_click (press, op);
667 /* don't change the selection unless the
668 clicked track is not currently selected. if
669 so, "collapse" the selection to just this
672 if (!selection->selected (clicked_axisview)) {
673 set_selected_track_as_side_effect (Selection::Set);
677 if (eff_mouse_mode != MouseRange) {
678 set_selected_regionview_from_click (press, op);
683 case RegionViewNameHighlight:
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);
694 case FadeInHandleItem:
695 case FadeInTrimHandleItem:
697 case FadeOutHandleItem:
698 case FadeOutTrimHandleItem:
700 case StartCrossFadeItem:
701 case EndCrossFadeItem:
702 if (eff_mouse_mode != MouseRange) {
703 cerr << "Should be setting selected regionview\n";
704 set_selected_regionview_from_click (press, op);
705 } else if (event->type == GDK_BUTTON_PRESS) {
706 set_selected_track_as_side_effect (op);
710 case ControlPointItem:
711 set_selected_track_as_side_effect (op);
712 if (eff_mouse_mode != MouseRange) {
713 set_selected_control_point_from_click (press, op);
718 /* for context click, select track */
719 if (event->button.button == 3) {
720 selection->clear_tracks ();
721 set_selected_track_as_side_effect (op);
725 case AutomationTrackItem:
726 set_selected_track_as_side_effect (op);
735 Editor::button_press_handler_1 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
737 /* single mouse clicks on any of these item types operate
738 independent of mouse mode, mostly because they are
739 not on the main track canvas or because we want
744 case PlayheadCursorItem:
745 _drags->set (new CursorDrag (this, *playhead_cursor, true), event);
749 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
750 hide_marker (item, event);
752 _drags->set (new MarkerDrag (this, item), event);
756 case TempoMarkerItem:
758 TempoMarker* m = reinterpret_cast<TempoMarker*> (item->get_data ("marker"));
761 new TempoMarkerDrag (
764 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
771 case MeterMarkerItem:
773 MeterMarker* m = reinterpret_cast<MeterMarker*> (item->get_data ("marker"));
776 new MeterMarkerDrag (
779 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
787 _drags->set (new VideoTimeLineDrag (this, item), event);
794 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
795 _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
801 case RangeMarkerBarItem:
802 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
803 _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
805 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateRangeMarker), event);
810 case CdMarkerBarItem:
811 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
812 _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
814 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateCDMarker), event);
819 case TransportMarkerBarItem:
820 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
821 _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
823 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateTransportMarker), event);
832 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
833 /* special case: allow trim of range selections in joined object mode;
834 in theory eff should equal MouseRange in this case, but it doesn't
835 because entering the range selection canvas item results in entered_regionview
836 being set to 0, so update_join_object_range_location acts as if we aren't
839 if (item_type == StartSelectionTrimItem) {
840 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
841 } else if (item_type == EndSelectionTrimItem) {
842 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
846 Editing::MouseMode eff = effective_mouse_mode ();
848 /* special case: allow drag of region fade in/out in object mode with join object/range enabled */
849 if (item_type == FadeInHandleItem || item_type == FadeOutHandleItem) {
853 /* there is no Range mode when in internal edit mode */
854 if (eff == MouseRange && internal_editing()) {
861 case StartSelectionTrimItem:
862 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
865 case EndSelectionTrimItem:
866 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
870 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
871 start_selection_grab (item, event);
873 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
874 /* grab selection for moving */
875 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionMove), event);
877 double const y = event->button.y;
878 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
880 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
881 if ( get_smart_mode() && atv) {
882 /* smart "join" mode: drag automation */
883 _drags->set (new AutomationRangeDrag (this, atv, selection->time), event, _cursors->up_down);
885 /* this was debated, but decided the more common action was to
886 make a new selection */
887 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
894 if (internal_editing()) {
895 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
896 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
900 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::RangeSelectModifier)) {
901 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionExtend), event);
903 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
909 case RegionViewNameHighlight:
910 if (!clicked_regionview->region()->locked()) {
911 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
917 if (!internal_editing()) {
918 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::RangeSelectModifier)) {
919 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionExtend), event);
921 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
931 /* Existing note: allow trimming/motion */
932 if (internal_editing()) {
933 /* trim notes if we're in internal edit mode and near the ends of the note */
934 NoteBase* cn = reinterpret_cast<NoteBase*>(item->get_data ("notebase"));
936 if (cn->big_enough_to_trim() && cn->mouse_near_ends()) {
937 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
939 _drags->set (new NoteDrag (this, item), event);
945 if (internal_editing()) {
946 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
947 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
961 /* Existing note: allow trimming/motion */
962 if (internal_editing()) {
963 NoteBase* cn = reinterpret_cast<NoteBase*> (item->get_data ("notebase"));
965 if (cn->big_enough_to_trim() && cn->mouse_near_ends()) {
966 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
968 _drags->set (new NoteDrag (this, item), event);
978 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)) &&
979 event->type == GDK_BUTTON_PRESS) {
981 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
983 } else if (event->type == GDK_BUTTON_PRESS) {
986 case FadeInHandleItem:
988 _drags->set (new FadeInDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), selection->regions), event, _cursors->fade_in);
992 case FadeOutHandleItem:
994 _drags->set (new FadeOutDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), selection->regions), event, _cursors->fade_out);
998 case StartCrossFadeItem:
999 case EndCrossFadeItem:
1000 /* we might allow user to grab inside the fade to trim a region with preserve_fade_anchor. for not this is not fully implemented */
1001 // if (!clicked_regionview->region()->locked()) {
1002 // _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer(), true), event);
1007 case FeatureLineItem:
1009 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier)) {
1010 remove_transient(item);
1014 _drags->set (new FeatureLineDrag (this, item), event);
1020 if (dynamic_cast<AutomationRegionView*> (clicked_regionview)) {
1021 /* click on an automation region view; do nothing here and let the ARV's signal handler
1027 if (internal_editing ()) {
1031 /* click on a normal region view */
1032 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
1033 add_region_copy_drag (item, event, clicked_regionview);
1034 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
1035 add_region_brush_drag (item, event, clicked_regionview);
1037 add_region_drag (item, event, clicked_regionview);
1041 // if (!internal_editing() && (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE && !selection->regions.empty())) {
1042 // _drags->add (new SelectionDrag (this, clicked_axisview->get_selection_rect (clicked_selection)->rect, SelectionDrag::SelectionMove));
1045 _drags->start_grab (event);
1049 case RegionViewNameHighlight:
1050 case LeftFrameHandle:
1051 case RightFrameHandle:
1052 if (!clicked_regionview->region()->locked()) {
1053 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1058 case FadeInTrimHandleItem:
1059 case FadeOutTrimHandleItem:
1060 if (!clicked_regionview->region()->locked()) {
1061 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer(), true), event);
1066 case RegionViewName:
1068 /* rename happens on edit clicks */
1069 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer()), event);
1074 case ControlPointItem:
1075 _drags->set (new ControlPointDrag (this, item), event);
1079 case AutomationLineItem:
1080 _drags->set (new LineDrag (this, item), event);
1085 if (internal_editing()) {
1086 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
1087 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
1091 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
1095 case AutomationTrackItem:
1097 TimeAxisView* parent = clicked_axisview->get_parent ();
1098 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (clicked_axisview);
1100 if (parent && dynamic_cast<MidiTimeAxisView*> (parent) && atv->show_regions ()) {
1102 RouteTimeAxisView* p = dynamic_cast<RouteTimeAxisView*> (parent);
1104 boost::shared_ptr<Playlist> pl = p->track()->playlist ();
1105 if (pl->n_regions() == 0) {
1106 /* Parent has no regions; create one so that we have somewhere to put automation */
1107 _drags->set (new RegionCreateDrag (this, item, parent), event);
1109 /* See if there's a region before the click that we can extend, and extend it if so */
1110 framepos_t const t = canvas_event_sample (event);
1111 boost::shared_ptr<Region> prev = pl->find_next_region (t, End, -1);
1113 _drags->set (new RegionCreateDrag (this, item, parent), event);
1115 prev->set_length (t - prev->position ());
1119 /* rubberband drag to select automation points */
1120 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
1127 if ( get_smart_mode() ) {
1128 /* we're in "smart" joined mode, and we've clicked on a Selection */
1129 double const y = event->button.y;
1130 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
1132 /* if we're over an automation track, start a drag of its data */
1133 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
1135 _drags->set (new AutomationRangeDrag (this, atv, selection->time), event, _cursors->up_down);
1138 /* if we're over a track and a region, and in the `object' part of a region,
1139 put a selection around the region and drag both
1141 /* RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1142 if (rtv && _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
1143 boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track> (rtv->route ());
1145 boost::shared_ptr<Playlist> pl = t->playlist ();
1148 boost::shared_ptr<Region> r = pl->top_region_at (canvas_event_sample (event));
1150 RegionView* rv = rtv->view()->find_view (r);
1151 clicked_selection = select_range (rv->region()->position(),
1152 rv->region()->last_frame()+1);
1153 _drags->add (new SelectionDrag (this, item, SelectionDrag::SelectionMove));
1154 list<RegionView*> rvs;
1156 _drags->add (new RegionMoveDrag (this, item, rv, rvs, false, false));
1157 _drags->start_grab (event);
1181 switch (item_type) {
1183 _drags->set (new LineDrag (this, item), event);
1186 case ControlPointItem:
1187 _drags->set (new ControlPointDrag (this, item), event);
1193 AudioRegionView* arv = dynamic_cast<AudioRegionView *> (clicked_regionview);
1195 _drags->set (new AutomationRangeDrag (this, arv, selection->time), event, _cursors->up_down);
1196 _drags->start_grab (event);
1202 case AutomationLineItem:
1203 _drags->set (new LineDrag (this, item), event);
1213 if (event->type == GDK_BUTTON_PRESS) {
1214 _drags->set (new MouseZoomDrag (this, item), event);
1221 if (internal_editing() && item_type == NoteItem ) {
1222 /* drag notes if we're in internal edit mode */
1223 NoteBase* cn = reinterpret_cast<NoteBase*>(item->get_data ("notebase"));
1225 if (cn->big_enough_to_trim()) {
1226 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
1229 } else if (clicked_regionview) {
1231 _drags->set (new TimeFXDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1237 _drags->set (new ScrubDrag (this, item), event);
1238 scrub_reversals = 0;
1239 scrub_reverse_distance = 0;
1240 last_scrub_x = event->button.x;
1241 scrubbing_direction = 0;
1242 set_canvas_cursor (_cursors->transparent);
1254 Editor::button_press_handler_2 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1256 Editing::MouseMode const eff = effective_mouse_mode ();
1259 switch (item_type) {
1261 if (internal_editing ()) {
1262 /* no region drags in internal edit mode */
1266 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
1267 add_region_copy_drag (item, event, clicked_regionview);
1269 add_region_drag (item, event, clicked_regionview);
1271 _drags->start_grab (event);
1274 case ControlPointItem:
1275 _drags->set (new ControlPointDrag (this, item), event);
1283 switch (item_type) {
1284 case RegionViewNameHighlight:
1285 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1289 case LeftFrameHandle:
1290 case RightFrameHandle:
1291 if (!internal_editing ()) {
1292 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1297 case RegionViewName:
1298 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer()), event);
1312 /* relax till release */
1318 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1319 temporal_zoom_to_frame (false, canvas_event_sample (event));
1321 temporal_zoom_to_frame (true, canvas_event_sample(event));
1334 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1336 if (event->type == GDK_2BUTTON_PRESS) {
1337 _drags->mark_double_click ();
1338 gdk_pointer_ungrab (GDK_CURRENT_TIME);
1342 if (event->type != GDK_BUTTON_PRESS) {
1346 Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->_track_canvas_viewport->get_window();
1348 if (canvas_window) {
1349 Glib::RefPtr<const Gdk::Window> pointer_window;
1352 Gdk::ModifierType mask;
1354 pointer_window = canvas_window->get_pointer (x, y, mask);
1356 if (pointer_window == _track_canvas->get_window()) {
1357 _track_canvas->window_to_canvas (x, y, wx, wy);
1361 pre_press_cursor = current_canvas_cursor;
1363 _track_canvas->grab_focus();
1365 if (_session && _session->actively_recording()) {
1369 if (internal_editing()) {
1370 bool leave_internal_edit_mode = false;
1372 switch (item_type) {
1377 if (!dynamic_cast<MidiRegionView*> (clicked_regionview) && !dynamic_cast<AutomationRegionView*> (clicked_regionview)) {
1378 leave_internal_edit_mode = true;
1382 case PlayheadCursorItem:
1384 case TempoMarkerItem:
1385 case MeterMarkerItem:
1389 case RangeMarkerBarItem:
1390 case CdMarkerBarItem:
1391 case TransportMarkerBarItem:
1393 /* button press on these events never does anything to
1394 change the editing mode.
1402 if (leave_internal_edit_mode) {
1403 ActionManager::do_action ("MouseMode", "toggle-internal-edit");
1407 button_selection (item, event, item_type);
1409 if (!_drags->active () &&
1410 (Keyboard::is_delete_event (&event->button) ||
1411 Keyboard::is_context_menu_event (&event->button) ||
1412 Keyboard::is_edit_event (&event->button))) {
1414 /* handled by button release */
1418 //not rolling, range mode click + join_play_range : locate the PH here
1419 if ( !_drags->active () && !_session->transport_rolling() && ( effective_mouse_mode() == MouseRange ) && Config->get_always_play_range() ) {
1420 framepos_t where = canvas_event_sample (event);
1422 _session->request_locate (where, false);
1425 switch (event->button.button) {
1427 return button_press_handler_1 (item, event, item_type);
1431 return button_press_handler_2 (item, event, item_type);
1438 return button_press_dispatch (&event->button);
1447 Editor::button_press_dispatch (GdkEventButton* ev)
1449 /* this function is intended only for buttons 4 and above.
1452 Gtkmm2ext::MouseButton b (ev->state, ev->button);
1453 return button_bindings->activate (b, Gtkmm2ext::Bindings::Press);
1457 Editor::button_release_dispatch (GdkEventButton* ev)
1459 /* this function is intended only for buttons 4 and above.
1462 Gtkmm2ext::MouseButton b (ev->state, ev->button);
1463 return button_bindings->activate (b, Gtkmm2ext::Bindings::Release);
1467 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1469 framepos_t where = canvas_event_sample (event);
1470 AutomationTimeAxisView* atv = 0;
1472 if (pre_press_cursor) {
1473 set_canvas_cursor (pre_press_cursor);
1474 pre_press_cursor = 0;
1477 /* no action if we're recording */
1479 if (_session && _session->actively_recording()) {
1483 /* see if we're finishing a drag */
1485 bool were_dragging = false;
1486 if (_drags->active ()) {
1487 bool const r = _drags->end_grab (event);
1489 /* grab dragged, so do nothing else */
1493 were_dragging = true;
1496 update_region_layering_order_editor ();
1498 /* edit events get handled here */
1500 if (!_drags->active () && Keyboard::is_edit_event (&event->button)) {
1501 switch (item_type) {
1503 show_region_properties ();
1506 case TempoMarkerItem: {
1508 TempoMarker* tempo_marker;
1510 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
1511 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
1515 if ((tempo_marker = dynamic_cast<TempoMarker*> (marker)) == 0) {
1516 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
1520 edit_tempo_marker (*tempo_marker);
1524 case MeterMarkerItem: {
1526 MeterMarker* meter_marker;
1528 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
1529 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
1533 if ((meter_marker = dynamic_cast<MeterMarker*> (marker)) == 0) {
1534 fatal << _("programming error: marker for meter is not a meter marker!") << endmsg;
1537 edit_meter_marker (*meter_marker);
1541 case RegionViewName:
1542 if (clicked_regionview->name_active()) {
1543 return mouse_rename_region (item, event);
1547 case ControlPointItem:
1548 edit_control_point (item);
1557 /* context menu events get handled here */
1558 if (Keyboard::is_context_menu_event (&event->button)) {
1560 context_click_event = *event;
1562 if (!_drags->active ()) {
1564 /* no matter which button pops up the context menu, tell the menu
1565 widget to use button 1 to drive menu selection.
1568 switch (item_type) {
1570 case FadeInHandleItem:
1571 case FadeInTrimHandleItem:
1572 case StartCrossFadeItem:
1573 popup_xfade_in_context_menu (1, event->button.time, item, item_type);
1577 case FadeOutHandleItem:
1578 case FadeOutTrimHandleItem:
1579 case EndCrossFadeItem:
1580 popup_xfade_out_context_menu (1, event->button.time, item, item_type);
1584 popup_track_context_menu (1, event->button.time, item_type, false);
1588 case RegionViewNameHighlight:
1589 case LeftFrameHandle:
1590 case RightFrameHandle:
1591 case RegionViewName:
1592 popup_track_context_menu (1, event->button.time, item_type, false);
1596 popup_track_context_menu (1, event->button.time, item_type, true);
1599 case AutomationTrackItem:
1600 popup_track_context_menu (1, event->button.time, item_type, false);
1604 case RangeMarkerBarItem:
1605 case TransportMarkerBarItem:
1606 case CdMarkerBarItem:
1610 popup_ruler_menu (where, item_type);
1614 marker_context_menu (&event->button, item);
1617 case TempoMarkerItem:
1618 tempo_or_meter_marker_context_menu (&event->button, item);
1621 case MeterMarkerItem:
1622 tempo_or_meter_marker_context_menu (&event->button, item);
1625 case CrossfadeViewItem:
1626 popup_track_context_menu (1, event->button.time, item_type, false);
1629 case ControlPointItem:
1630 popup_control_point_context_menu (item, event);
1641 /* delete events get handled here */
1643 Editing::MouseMode const eff = effective_mouse_mode ();
1645 if (!_drags->active () && Keyboard::is_delete_event (&event->button)) {
1647 switch (item_type) {
1648 case TempoMarkerItem:
1649 remove_tempo_marker (item);
1652 case MeterMarkerItem:
1653 remove_meter_marker (item);
1657 remove_marker (*item, event);
1661 if (eff == MouseObject) {
1662 remove_clicked_region ();
1666 case ControlPointItem:
1667 remove_control_point (item);
1671 remove_midi_note (item, event);
1680 switch (event->button.button) {
1683 switch (item_type) {
1684 /* see comments in button_press_handler */
1685 case PlayheadCursorItem:
1688 case AutomationLineItem:
1689 case StartSelectionTrimItem:
1690 case EndSelectionTrimItem:
1694 if (!_dragging_playhead) {
1695 snap_to_with_modifier (where, event, 0, true);
1696 mouse_add_new_marker (where);
1700 case CdMarkerBarItem:
1701 if (!_dragging_playhead) {
1702 // if we get here then a dragged range wasn't done
1703 snap_to_with_modifier (where, event, 0, true);
1704 mouse_add_new_marker (where, true);
1709 if (!_dragging_playhead) {
1710 snap_to_with_modifier (where, event);
1711 mouse_add_new_tempo_event (where);
1716 if (!_dragging_playhead) {
1717 mouse_add_new_meter_event (pixel_to_sample (event->button.x));
1728 switch (item_type) {
1729 case AutomationTrackItem:
1730 atv = dynamic_cast<AutomationTimeAxisView*>(clicked_axisview);
1732 bool with_guard_points = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
1733 atv->add_automation_event (event, where, event->button.y, with_guard_points);
1743 switch (item_type) {
1746 /* check that we didn't drag before releasing, since
1747 its really annoying to create new control
1748 points when doing this.
1750 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (clicked_regionview);
1751 if (!were_dragging && arv) {
1752 bool with_guard_points = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
1753 arv->add_gain_point_event (item, event, with_guard_points);
1759 case AutomationTrackItem: {
1760 bool with_guard_points = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
1761 dynamic_cast<AutomationTimeAxisView*>(clicked_axisview)->
1762 add_automation_event (event, where, event->button.y, with_guard_points);
1772 set_canvas_cursor (current_canvas_cursor);
1773 if (scrubbing_direction == 0) {
1774 /* no drag, just a click */
1775 switch (item_type) {
1777 play_selected_region ();
1783 /* make sure we stop */
1784 _session->request_transport_speed (0.0);
1793 /* do any (de)selection operations that should occur on button release */
1794 button_selection (item, event, item_type);
1803 switch (item_type) {
1805 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1807 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier|Keyboard::SecondaryModifier))) {
1810 // Button2 click is unused
1825 // x_style_paste (where, 1.0);
1846 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1853 switch (item_type) {
1854 case ControlPointItem:
1855 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1856 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1861 at_y = cp->get_y ();
1862 cp->i2w (at_x, at_y);
1866 fraction = 1.0 - (cp->get_y() / cp->line().height());
1868 if (is_drawable() && !_drags->active ()) {
1869 set_canvas_cursor (_cursors->fader);
1872 _verbose_cursor->set (cp->line().get_verbose_cursor_string (fraction), at_x, at_y);
1873 _verbose_cursor->show ();
1878 if (mouse_mode == MouseGain) {
1879 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1881 line->set_outline_color (ARDOUR_UI::config()->get_canvasvar_EnteredGainLine());
1883 if (is_drawable()) {
1884 set_canvas_cursor (_cursors->fader);
1889 case AutomationLineItem:
1890 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1891 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1893 line->set_outline_color (ARDOUR_UI::config()->get_canvasvar_EnteredAutomationLine());
1895 if (is_drawable()) {
1896 set_canvas_cursor (_cursors->fader);
1901 case RegionViewNameHighlight:
1902 if (is_drawable() && effective_mouse_mode() == MouseObject && entered_regionview) {
1903 set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1904 _over_region_trim_target = true;
1908 case LeftFrameHandle:
1909 case RightFrameHandle:
1910 if (is_drawable() && effective_mouse_mode() == MouseObject && !internal_editing() && entered_regionview) {
1911 set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1916 switch (effective_mouse_mode()) {
1918 set_canvas_cursor (_cursors->selector);
1921 set_canvas_cursor (which_grabber_cursor());
1926 case StartSelectionTrimItem:
1927 if (is_drawable()) {
1928 set_canvas_cursor (_cursors->left_side_trim);
1931 case EndSelectionTrimItem:
1932 if (is_drawable()) {
1933 set_canvas_cursor (_cursors->right_side_trim);
1937 case PlayheadCursorItem:
1938 if (is_drawable()) {
1939 switch (_edit_point) {
1941 set_canvas_cursor (_cursors->grabber_edit_point);
1944 set_canvas_cursor (_cursors->grabber);
1951 case RegionViewName:
1953 /* when the name is not an active item, the entire name highlight is for trimming */
1955 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1956 if (mouse_mode == MouseObject && is_drawable()) {
1957 set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1958 _over_region_trim_target = true;
1964 case AutomationTrackItem:
1965 if (is_drawable()) {
1966 Gdk::Cursor *cursor;
1967 switch (mouse_mode) {
1969 cursor = _cursors->selector;
1972 cursor = _cursors->zoom_in;
1975 cursor = _cursors->cross_hair;
1979 set_canvas_cursor (cursor);
1981 AutomationTimeAxisView* atv;
1982 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1983 clear_entered_track = false;
1984 set_entered_track (atv);
1990 case RangeMarkerBarItem:
1991 case TransportMarkerBarItem:
1992 case CdMarkerBarItem:
1995 if (is_drawable()) {
1996 set_canvas_cursor (_cursors->timebar);
2001 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2004 entered_marker = marker;
2005 marker->set_color_rgba (ARDOUR_UI::config()->get_canvasvar_EnteredMarker());
2007 case MeterMarkerItem:
2008 case TempoMarkerItem:
2009 if (is_drawable()) {
2010 set_canvas_cursor (_cursors->timebar);
2014 case FadeInHandleItem:
2015 if (mouse_mode == MouseObject && !internal_editing()) {
2016 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
2018 RegionView* rv = static_cast<RegionView*>(item->get_data ("regionview"));
2019 rect->set_fill_color (rv->get_fill_color());
2020 set_canvas_cursor (_cursors->fade_in);
2025 case FadeInTrimHandleItem:
2026 if (mouse_mode == MouseObject && !internal_editing()) {
2027 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
2029 RegionView* rv = static_cast<RegionView*>(item->get_data ("regionview"));
2030 rect->set_fill_color (rv->get_fill_color());
2031 set_canvas_cursor (_cursors->fade_trim_in);
2036 case FadeOutHandleItem:
2037 if (mouse_mode == MouseObject && !internal_editing()) {
2038 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
2040 RegionView* rv = static_cast<RegionView*>(item->get_data ("regionview"));
2041 rect->set_fill_color (rv->get_fill_color ());
2042 set_canvas_cursor (_cursors->fade_out);
2047 case FadeOutTrimHandleItem:
2048 if (mouse_mode == MouseObject && !internal_editing()) {
2049 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
2051 RegionView* rv = static_cast<RegionView*>(item->get_data ("regionview"));
2052 rect->set_fill_color (rv->get_fill_color ());
2053 set_canvas_cursor (_cursors->fade_trim_out);
2058 case FeatureLineItem:
2060 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
2061 line->set_outline_color (0xFF0000FF);
2066 if ( get_smart_mode() ) {
2067 set_canvas_cursor ();
2075 /* second pass to handle entered track status in a comprehensible way.
2078 switch (item_type) {
2080 case AutomationLineItem:
2081 case ControlPointItem:
2082 /* these do not affect the current entered track state */
2083 clear_entered_track = false;
2086 case AutomationTrackItem:
2087 /* handled above already */
2091 set_entered_track (0);
2099 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent*, ItemType item_type)
2108 switch (item_type) {
2109 case ControlPointItem:
2110 if (is_drawable()) {
2111 set_canvas_cursor (current_canvas_cursor);
2114 _verbose_cursor->hide ();
2117 case RegionViewNameHighlight:
2118 case LeftFrameHandle:
2119 case RightFrameHandle:
2120 case StartSelectionTrimItem:
2121 case EndSelectionTrimItem:
2122 case PlayheadCursorItem:
2124 _over_region_trim_target = false;
2126 if (is_drawable()) {
2127 set_canvas_cursor (current_canvas_cursor);
2132 case AutomationLineItem:
2133 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
2135 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
2137 line->set_outline_color (al->get_line_color());
2140 if (is_drawable()) {
2141 set_canvas_cursor (current_canvas_cursor);
2145 case RegionViewName:
2146 /* see enter_handler() for notes */
2147 _over_region_trim_target = false;
2149 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
2150 if (is_drawable() && mouse_mode == MouseObject) {
2151 set_canvas_cursor (current_canvas_cursor);
2156 case RangeMarkerBarItem:
2157 case TransportMarkerBarItem:
2158 case CdMarkerBarItem:
2162 if (is_drawable()) {
2163 set_canvas_cursor (current_canvas_cursor);
2168 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2172 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
2173 location_flags_changed (loc, this);
2176 case MeterMarkerItem:
2177 case TempoMarkerItem:
2179 if (is_drawable()) {
2180 set_canvas_cursor (current_canvas_cursor);
2185 case FadeInTrimHandleItem:
2186 case FadeOutTrimHandleItem:
2187 case FadeInHandleItem:
2188 case FadeOutHandleItem:
2189 rv = static_cast<RegionView*>(item->get_data ("regionview"));
2191 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
2193 rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_InactiveFadeHandle());
2196 set_canvas_cursor (current_canvas_cursor);
2199 case AutomationTrackItem:
2200 if (is_drawable()) {
2201 set_canvas_cursor (current_canvas_cursor);
2202 clear_entered_track = true;
2203 Glib::signal_idle().connect (sigc::mem_fun(*this, &Editor::left_automation_track));
2206 case FeatureLineItem:
2208 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
2209 line->set_outline_color (ARDOUR_UI::config()->get_canvasvar_ZeroLine());
2221 Editor::left_automation_track ()
2223 if (clear_entered_track) {
2224 set_entered_track (0);
2225 clear_entered_track = false;
2231 Editor::scrub (framepos_t frame, double current_x)
2235 if (scrubbing_direction == 0) {
2237 _session->request_locate (frame, false);
2238 _session->request_transport_speed (0.1);
2239 scrubbing_direction = 1;
2243 if (last_scrub_x > current_x) {
2245 /* pointer moved to the left */
2247 if (scrubbing_direction > 0) {
2249 /* we reversed direction to go backwards */
2252 scrub_reverse_distance += (int) (last_scrub_x - current_x);
2256 /* still moving to the left (backwards) */
2258 scrub_reversals = 0;
2259 scrub_reverse_distance = 0;
2261 delta = 0.01 * (last_scrub_x - current_x);
2262 _session->request_transport_speed_nonzero (_session->transport_speed() - delta);
2266 /* pointer moved to the right */
2268 if (scrubbing_direction < 0) {
2269 /* we reversed direction to go forward */
2272 scrub_reverse_distance += (int) (current_x - last_scrub_x);
2275 /* still moving to the right */
2277 scrub_reversals = 0;
2278 scrub_reverse_distance = 0;
2280 delta = 0.01 * (current_x - last_scrub_x);
2281 _session->request_transport_speed_nonzero (_session->transport_speed() + delta);
2285 /* if there have been more than 2 opposite motion moves detected, or one that moves
2286 back more than 10 pixels, reverse direction
2289 if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
2291 if (scrubbing_direction > 0) {
2292 /* was forwards, go backwards */
2293 _session->request_transport_speed (-0.1);
2294 scrubbing_direction = -1;
2296 /* was backwards, go forwards */
2297 _session->request_transport_speed (0.1);
2298 scrubbing_direction = 1;
2301 scrub_reverse_distance = 0;
2302 scrub_reversals = 0;
2306 last_scrub_x = current_x;
2310 Editor::motion_handler (ArdourCanvas::Item* /*item*/, GdkEvent* event, bool from_autoscroll)
2312 _last_motion_y = event->motion.y;
2314 if (event->motion.is_hint) {
2317 /* We call this so that MOTION_NOTIFY events continue to be
2318 delivered to the canvas. We need to do this because we set
2319 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
2320 the density of the events, at the expense of a round-trip
2321 to the server. Given that this will mostly occur on cases
2322 where DISPLAY = :0.0, and given the cost of what the motion
2323 event might do, its a good tradeoff.
2326 _track_canvas->get_pointer (x, y);
2329 if (current_stepping_trackview) {
2330 /* don't keep the persistent stepped trackview if the mouse moves */
2331 current_stepping_trackview = 0;
2332 step_timeout.disconnect ();
2335 if (_session && _session->actively_recording()) {
2336 /* Sorry. no dragging stuff around while we record */
2340 JoinObjectRangeState const old = _join_object_range_state;
2341 update_join_object_range_location (event->motion.x, event->motion.y);
2343 if (!_internal_editing && _join_object_range_state != old) {
2344 set_canvas_cursor ();
2347 if (!_internal_editing && _over_region_trim_target) {
2348 set_canvas_cursor_for_region_view (event->motion.x, entered_regionview);
2351 bool handled = false;
2352 if (_drags->active ()) {
2353 handled = _drags->motion_handler (event, from_autoscroll);
2360 track_canvas_motion (event);
2365 Editor::can_remove_control_point (ArdourCanvas::Item* item)
2367 ControlPoint* control_point;
2369 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2370 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2374 AutomationLine& line = control_point->line ();
2375 if (dynamic_cast<AudioRegionGainLine*> (&line)) {
2376 /* we shouldn't remove the first or last gain point in region gain lines */
2377 if (line.is_last_point(*control_point) || line.is_first_point(*control_point)) {
2386 Editor::remove_control_point (ArdourCanvas::Item* item)
2388 if (!can_remove_control_point (item)) {
2392 ControlPoint* control_point;
2394 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2395 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2399 control_point->line().remove_point (*control_point);
2403 Editor::edit_control_point (ArdourCanvas::Item* item)
2405 ControlPoint* p = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"));
2408 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2412 ControlPointDialog d (p);
2415 if (d.run () != RESPONSE_ACCEPT) {
2419 p->line().modify_point_y (*p, d.get_y_fraction ());
2423 Editor::edit_notes (TimeAxisViewItem& tavi)
2425 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(&tavi);
2431 MidiRegionView::Selection const & s = mrv->selection();
2437 EditNoteDialog* d = new EditNoteDialog (&(*s.begin())->region_view(), s);
2441 d->signal_response().connect (sigc::bind (sigc::mem_fun (*this, &Editor::note_edit_done), d));
2445 Editor::note_edit_done (int r, EditNoteDialog* d)
2452 Editor::visible_order_range (int* low, int* high) const
2454 *low = TimeAxisView::max_order ();
2457 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
2459 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
2461 if (!rtv->hidden()) {
2463 if (*high < rtv->order()) {
2464 *high = rtv->order ();
2467 if (*low > rtv->order()) {
2468 *low = rtv->order ();
2475 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
2477 /* Either add to or set the set the region selection, unless
2478 this is an alignment click (control used)
2481 if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
2482 TimeAxisView* tv = &rv.get_time_axis_view();
2483 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
2485 if (rtv && rtv->is_track()) {
2486 speed = rtv->track()->speed();
2489 framepos_t where = get_preferred_edit_position();
2493 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
2495 align_region (rv.region(), SyncPoint, (framepos_t) (where * speed));
2497 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
2499 align_region (rv.region(), End, (framepos_t) (where * speed));
2503 align_region (rv.region(), Start, (framepos_t) (where * speed));
2510 Editor::collect_new_region_view (RegionView* rv)
2512 latest_regionviews.push_back (rv);
2516 Editor::collect_and_select_new_region_view (RegionView* rv)
2519 latest_regionviews.push_back (rv);
2523 Editor::cancel_selection ()
2525 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2526 (*i)->hide_selection ();
2529 selection->clear ();
2530 clicked_selection = 0;
2534 Editor::cancel_time_selection ()
2536 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2537 (*i)->hide_selection ();
2539 selection->time.clear ();
2540 clicked_selection = 0;
2544 Editor::point_trim (GdkEvent* event, framepos_t new_bound)
2546 RegionView* rv = clicked_regionview;
2548 /* Choose action dependant on which button was pressed */
2549 switch (event->button.button) {
2551 begin_reversible_command (_("start point trim"));
2553 if (selection->selected (rv)) {
2554 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
2555 i != selection->regions.by_layer().end(); ++i)
2557 if (!(*i)->region()->locked()) {
2558 (*i)->region()->clear_changes ();
2559 (*i)->region()->trim_front (new_bound);
2560 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2565 if (!rv->region()->locked()) {
2566 rv->region()->clear_changes ();
2567 rv->region()->trim_front (new_bound);
2568 _session->add_command(new StatefulDiffCommand (rv->region()));
2572 commit_reversible_command();
2576 begin_reversible_command (_("End point trim"));
2578 if (selection->selected (rv)) {
2580 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
2582 if (!(*i)->region()->locked()) {
2583 (*i)->region()->clear_changes();
2584 (*i)->region()->trim_end (new_bound);
2585 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2591 if (!rv->region()->locked()) {
2592 rv->region()->clear_changes ();
2593 rv->region()->trim_end (new_bound);
2594 _session->add_command (new StatefulDiffCommand (rv->region()));
2598 commit_reversible_command();
2607 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* /*event*/)
2612 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2613 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2617 Location* location = find_location_from_marker (marker, is_start);
2618 location->set_hidden (true, this);
2623 Editor::reposition_zoom_rect (framepos_t start, framepos_t end)
2625 double x1 = sample_to_pixel (start);
2626 double x2 = sample_to_pixel (end);
2627 double y2 = _full_canvas_height - 1.0;
2629 zoom_rect->set (ArdourCanvas::Rect (x1, 1.0, x2, y2));
2634 Editor::mouse_rename_region (ArdourCanvas::Item* /*item*/, GdkEvent* /*event*/)
2636 using namespace Gtkmm2ext;
2638 ArdourPrompter prompter (false);
2640 prompter.set_prompt (_("Name for region:"));
2641 prompter.set_initial_text (clicked_regionview->region()->name());
2642 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
2643 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
2644 prompter.show_all ();
2645 switch (prompter.run ()) {
2646 case Gtk::RESPONSE_ACCEPT:
2648 prompter.get_result(str);
2650 clicked_regionview->region()->set_name (str);
2659 Editor::mouse_brush_insert_region (RegionView* rv, framepos_t pos)
2661 /* no brushing without a useful snap setting */
2663 switch (_snap_mode) {
2665 return; /* can't work because it allows region to be placed anywhere */
2670 switch (_snap_type) {
2678 /* don't brush a copy over the original */
2680 if (pos == rv->region()->position()) {
2684 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
2686 if (rtv == 0 || !rtv->is_track()) {
2690 boost::shared_ptr<Playlist> playlist = rtv->playlist();
2691 double speed = rtv->track()->speed();
2693 playlist->clear_changes ();
2694 boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region(), true));
2695 playlist->add_region (new_region, (framepos_t) (pos * speed));
2696 _session->add_command (new StatefulDiffCommand (playlist));
2698 // playlist is frozen, so we have to update manually XXX this is disgusting
2700 playlist->RegionAdded (new_region); /* EMIT SIGNAL */
2704 Editor::track_height_step_timeout ()
2706 if (get_microseconds() - last_track_height_step_timestamp < 250000) {
2707 current_stepping_trackview = 0;
2714 Editor::add_region_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2716 assert (region_view);
2718 if (!region_view->region()->playlist()) {
2722 _region_motion_group->raise_to_top ();
2724 if (Config->get_edit_mode() == Splice) {
2725 _drags->add (new RegionSpliceDrag (this, item, region_view, selection->regions.by_layer()));
2727 _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), false, false));
2732 Editor::add_region_copy_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2734 assert (region_view);
2736 if (!region_view->region()->playlist()) {
2740 _region_motion_group->raise_to_top ();
2742 _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), false, true));
2746 Editor::add_region_brush_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2748 assert (region_view);
2750 if (!region_view->region()->playlist()) {
2754 if (Config->get_edit_mode() == Splice) {
2758 _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), true, false));
2760 begin_reversible_command (Operations::drag_region_brush);
2763 /** Start a grab where a time range is selected, track(s) are selected, and the
2764 * user clicks and drags a region with a modifier in order to create a new region containing
2765 * the section of the clicked region that lies within the time range.
2768 Editor::start_selection_grab (ArdourCanvas::Item* /*item*/, GdkEvent* event)
2770 if (clicked_regionview == 0) {
2774 /* lets try to create new Region for the selection */
2776 vector<boost::shared_ptr<Region> > new_regions;
2777 create_region_from_selection (new_regions);
2779 if (new_regions.empty()) {
2783 /* XXX fix me one day to use all new regions */
2785 boost::shared_ptr<Region> region (new_regions.front());
2787 /* add it to the current stream/playlist.
2789 tricky: the streamview for the track will add a new regionview. we will
2790 catch the signal it sends when it creates the regionview to
2791 set the regionview we want to then drag.
2794 latest_regionviews.clear();
2795 sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
2797 /* A selection grab currently creates two undo/redo operations, one for
2798 creating the new region and another for moving it.
2801 begin_reversible_command (Operations::selection_grab);
2803 boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
2805 playlist->clear_changes ();
2806 clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
2807 _session->add_command(new StatefulDiffCommand (playlist));
2809 commit_reversible_command ();
2813 if (latest_regionviews.empty()) {
2814 /* something went wrong */
2818 /* we need to deselect all other regionviews, and select this one
2819 i'm ignoring undo stuff, because the region creation will take care of it
2821 selection->set (latest_regionviews);
2823 _drags->set (new RegionMoveDrag (this, latest_regionviews.front()->get_canvas_group(), latest_regionviews.front(), latest_regionviews, false, false), event);
2829 if (_drags->active ()) {
2832 selection->clear ();
2837 Editor::set_internal_edit (bool yn)
2839 if (_internal_editing == yn) {
2843 _internal_editing = yn;
2846 pre_internal_mouse_mode = mouse_mode;
2847 pre_internal_snap_type = _snap_type;
2848 pre_internal_snap_mode = _snap_mode;
2850 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2851 (*i)->enter_internal_edit_mode ();
2854 set_snap_to (internal_snap_type);
2855 set_snap_mode (internal_snap_mode);
2859 internal_snap_mode = _snap_mode;
2860 internal_snap_type = _snap_type;
2862 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2863 (*i)->leave_internal_edit_mode ();
2866 if (mouse_mode == MouseDraw && pre_internal_mouse_mode != MouseDraw) {
2867 /* we were drawing .. flip back to something sensible */
2868 set_mouse_mode (pre_internal_mouse_mode);
2871 set_snap_to (pre_internal_snap_type);
2872 set_snap_mode (pre_internal_snap_mode);
2875 set_canvas_cursor ();
2878 /** Update _join_object_range_state which indicate whether we are over the top or bottom half of a region view,
2879 * used by the `join object/range' tool mode.
2882 Editor::update_join_object_range_location (double /*x*/, double y)
2884 /* XXX: actually, this decides based on whether the mouse is in the top
2885 or bottom half of a the waveform part RouteTimeAxisView;
2887 Note that entered_{track,regionview} is not always setup (e.g. if
2888 the mouse is over a TimeSelection), and to get a Region
2889 that we're over requires searching the playlist.
2892 if ( !get_smart_mode() ) {
2893 _join_object_range_state = JOIN_OBJECT_RANGE_NONE;
2897 if (mouse_mode == MouseObject) {
2898 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2899 } else if (mouse_mode == MouseRange) {
2900 _join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
2903 /* XXX: maybe we should make entered_track work in all cases, rather than resorting to this */
2904 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
2908 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
2913 rtv->canvas_display()->canvas_to_item (cx, cy);
2915 double const c = cy / (rtv->view()->child_height() - TimeAxisViewItem::NAME_HIGHLIGHT_SIZE);
2917 _join_object_range_state = c <= 0.5 ? JOIN_OBJECT_RANGE_RANGE : JOIN_OBJECT_RANGE_OBJECT;
2923 Editor::effective_mouse_mode () const
2925 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
2927 } else if (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE) {
2935 Editor::remove_midi_note (ArdourCanvas::Item* item, GdkEvent *)
2937 NoteBase* e = reinterpret_cast<NoteBase*> (item->get_data ("notebase"));
2940 e->region_view().delete_note (e->note ());
2944 Editor::set_canvas_cursor_for_region_view (double x, RegionView* rv)
2946 /* XXX: this check should not be necessary */
2951 ArdourCanvas::Group* g = rv->get_canvas_group ();
2952 ArdourCanvas::Group* p = g->parent ();
2954 /* Compute x in region view parent coordinates */
2956 p->canvas_to_item (x, dy);
2958 boost::optional<ArdourCanvas::Rect> item_bbox = g->bounding_box ();
2960 ArdourCanvas::Rect parent_bbox = g->item_to_parent (item_bbox.get ());
2962 /* First or last 10% of region is used for trimming, if the whole
2963 region is wider than 20 pixels at the current zoom level.
2966 double const w = parent_bbox.width();
2968 if (w > 20.0 && x >= parent_bbox.x0 && x < parent_bbox.x1) {
2970 Trimmable::CanTrim ct = rv->region()->can_trim ();
2972 if (((x - parent_bbox.x0) / w) < 0.10) {
2973 if (ct & Trimmable::FrontTrimEarlier) {
2974 set_canvas_cursor (_cursors->left_side_trim, true);
2976 set_canvas_cursor (_cursors->left_side_trim_right_only, true);
2978 } else if (((parent_bbox.x1 - x) / w) < 0.10) {
2979 if (ct & Trimmable::EndTrimLater) {
2980 set_canvas_cursor (_cursors->right_side_trim, true);
2982 set_canvas_cursor (_cursors->right_side_trim_left_only, true);
2988 /** Obtain the pointer position in canvas coordinates */
2990 Editor::get_pointer_position (double& x, double& y) const
2993 _track_canvas->get_pointer (px, py);
2994 _track_canvas->window_to_canvas (px, py, x, y);