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_frame (&event, 0, 0);
135 Editor::window_event_frame (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_frame (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() 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 (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);
349 if (tact->get_active())
350 m = MouseObject; //Smart mode turned to ON, force editing to Object mode
352 set_mouse_mode(m, true); //call this so the button styles can get updated
356 Editor::set_mouse_mode (MouseMode m, bool force)
358 if (_drags->active ()) {
362 if (!force && m == mouse_mode) {
366 Glib::RefPtr<Action> act;
370 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-range"));
374 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-object"));
378 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-draw"));
382 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-gain"));
386 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-zoom"));
390 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-timefx"));
394 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-audition"));
400 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
403 /* go there and back to ensure that the toggled handler is called to set up mouse_mode */
404 tact->set_active (false);
405 tact->set_active (true);
407 //NOTE: this will result in a call to mouse_mode_toggled which does the heavy lifting
411 Editor::mouse_mode_toggled (MouseMode m)
413 Glib::RefPtr<Action> act;
414 Glib::RefPtr<ToggleAction> tact;
418 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-range"));
422 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-object"));
426 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-draw"));
430 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-gain"));
434 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-zoom"));
438 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-timefx"));
442 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-audition"));
448 tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
451 if (!tact->get_active()) {
452 /* this was just the notification that the old mode has been
453 * left. we'll get called again with the new mode active in a
461 act = ActionManager::get_action (X_("MouseMode"), X_("toggle-internal-edit"));
462 tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
463 tact->set_active (true);
469 if (_session && mouse_mode == MouseAudition) {
470 /* stop transport and reset default speed to avoid oddness with
472 _session->request_transport_speed (0.0, true);
479 //TODO: set button styles for smart buttons
481 if ( smart_mode_action->get_active() ) {
482 if( mouse_mode == MouseObject ) { //smart active and object active
483 smart_mode_button.set_active(1);
484 smart_mode_button.set_name("smart mode button");
485 mouse_move_button.set_name("smart mode button");
486 } else { //smart active but object inactive
487 smart_mode_button.set_active(0);
488 smart_mode_button.set_name("smart mode button");
489 mouse_move_button.set_name("mouse mode button");
492 smart_mode_button.set_active(0);
493 smart_mode_button.set_name("mouse mode button");
494 mouse_move_button.set_name("mouse mode button");
498 set_canvas_cursor ();
499 set_gain_envelope_visibility ();
501 MouseModeChanged (); /* EMIT SIGNAL */
505 Editor::step_mouse_mode (bool next)
507 switch (current_mouse_mode()) {
510 if (Profile->get_sae()) {
511 set_mouse_mode (MouseZoom);
513 set_mouse_mode (MouseRange);
516 set_mouse_mode (MouseTimeFX);
521 if (next) set_mouse_mode (MouseDraw);
522 else set_mouse_mode (MouseObject);
526 if (next) set_mouse_mode (MouseZoom);
527 else set_mouse_mode (MouseRange);
532 if (Profile->get_sae()) {
533 set_mouse_mode (MouseTimeFX);
535 set_mouse_mode (MouseGain);
538 if (Profile->get_sae()) {
539 set_mouse_mode (MouseObject);
541 set_mouse_mode (MouseDraw);
547 if (next) set_mouse_mode (MouseTimeFX);
548 else set_mouse_mode (MouseZoom);
553 set_mouse_mode (MouseAudition);
555 if (Profile->get_sae()) {
556 set_mouse_mode (MouseZoom);
558 set_mouse_mode (MouseGain);
564 if (next) set_mouse_mode (MouseObject);
565 else set_mouse_mode (MouseTimeFX);
571 Editor::toggle_internal_editing_from_double_click (GdkEvent* event)
573 if (_drags->active()) {
574 _drags->end_grab (event);
577 ActionManager::do_action ("MouseMode", "toggle-internal-edit");
579 /* prevent reversion of edit cursor on button release */
581 pre_press_cursor = 0;
587 Editor::button_selection (ArdourCanvas::Item* /*item*/, GdkEvent* event, ItemType item_type)
589 /* in object/audition/timefx/gain-automation mode,
590 any button press sets the selection if the object
591 can be selected. this is a bit of hack, because
592 we want to avoid this if the mouse operation is a
595 note: not dbl-click or triple-click
597 Also note that there is no region selection in internal edit mode, otherwise
598 for operations operating on the selection (e.g. cut) it is not obvious whether
599 to cut notes or regions.
602 if (((mouse_mode != MouseObject) &&
603 (mouse_mode != MouseAudition || item_type != RegionItem) &&
604 (mouse_mode != MouseTimeFX || item_type != RegionItem) &&
605 (mouse_mode != MouseGain) &&
606 (mouse_mode != MouseDraw)) ||
607 ((event->type != GDK_BUTTON_PRESS && event->type != GDK_BUTTON_RELEASE) || event->button.button > 3) ||
608 (internal_editing() && mouse_mode != MouseTimeFX)) {
613 if (event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) {
615 if ((event->button.state & Keyboard::RelevantModifierKeyMask) && event->button.button != 1) {
617 /* almost no selection action on modified button-2 or button-3 events */
619 if (item_type != RegionItem && event->button.button != 2) {
625 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
626 bool press = (event->type == GDK_BUTTON_PRESS);
630 if (!get_smart_mode() || (_join_object_range_state != JOIN_OBJECT_RANGE_RANGE)) {
632 if (mouse_mode != MouseRange) {
633 set_selected_regionview_from_click (press, op);
635 /* don't change the selection unless the
636 clicked track is not currently selected. if
637 so, "collapse" the selection to just this
640 if (!selection->selected (clicked_axisview)) {
641 set_selected_track_as_side_effect (Selection::Set);
645 if (mouse_mode != MouseRange) {
646 set_selected_regionview_from_click (press, op);
652 case RegionViewNameHighlight:
654 case LeftFrameHandle:
655 case RightFrameHandle:
656 if ( mouse_mode != MouseRange ) {
657 set_selected_regionview_from_click (press, op);
658 } else if (event->type == GDK_BUTTON_PRESS) {
659 set_selected_track_as_side_effect (op);
663 case FadeInHandleItem:
665 case FadeOutHandleItem:
667 case StartCrossFadeItem:
668 case EndCrossFadeItem:
669 if ( mouse_mode != MouseRange ) {
670 set_selected_regionview_from_click (press, op);
671 } else if (event->type == GDK_BUTTON_PRESS) {
672 set_selected_track_as_side_effect (op);
676 case ControlPointItem:
677 set_selected_track_as_side_effect (op);
678 if ( mouse_mode != MouseRange ) {
679 set_selected_control_point_from_click (press, op);
684 /* for context click, select track */
685 if (event->button.button == 3) {
686 selection->clear_tracks ();
687 set_selected_track_as_side_effect (op);
691 case AutomationTrackItem:
692 set_selected_track_as_side_effect (op);
701 Editor::button_press_handler_1 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
703 /* single mouse clicks on any of these item types operate
704 independent of mouse mode, mostly because they are
705 not on the main track canvas or because we want
710 case PlayheadCursorItem:
711 _drags->set (new CursorDrag (this, *playhead_cursor, true), event);
715 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
716 hide_marker (item, event);
718 _drags->set (new MarkerDrag (this, item), event);
722 case TempoMarkerItem:
724 TempoMarker* m = reinterpret_cast<TempoMarker*> (item->get_data ("marker"));
727 new TempoMarkerDrag (
730 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
737 case MeterMarkerItem:
739 MeterMarker* m = reinterpret_cast<MeterMarker*> (item->get_data ("marker"));
742 new MeterMarkerDrag (
745 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
753 _drags->set (new VideoTimeLineDrag (this, item), event);
760 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
761 _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
767 case RangeMarkerBarItem:
768 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
769 _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
771 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateRangeMarker), event);
776 case CdMarkerBarItem:
777 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
778 _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
780 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateCDMarker), event);
785 case TransportMarkerBarItem:
786 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
787 _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
789 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateTransportMarker), event);
798 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
799 /* special case: allow trim of range selections in joined object mode;
800 in theory eff should equal MouseRange in this case, but it doesn't
801 because entering the range selection canvas item results in entered_regionview
802 being set to 0, so update_join_object_range_location acts as if we aren't
805 if (item_type == StartSelectionTrimItem) {
806 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
807 } else if (item_type == EndSelectionTrimItem) {
808 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
812 Editing::MouseMode eff = effective_mouse_mode ();
814 /* special case: allow drag of region fade in/out in object mode with join object/range enabled */
815 if (item_type == FadeInHandleItem || item_type == FadeOutHandleItem) {
819 /* there is no Range mode when in internal edit mode */
820 if (eff == MouseRange && internal_editing()) {
827 case StartSelectionTrimItem:
828 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
831 case EndSelectionTrimItem:
832 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
836 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
837 start_selection_grab (item, event);
839 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
840 /* grab selection for moving */
841 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionMove), event);
843 double const y = event->button.y;
844 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
846 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
847 if ( get_smart_mode() && atv) {
848 /* smart "join" mode: drag automation */
849 _drags->set (new AutomationRangeDrag (this, atv, selection->time), event, _cursors->up_down);
851 /* this was debated, but decided the more common action was to
852 make a new selection */
853 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
860 if (internal_editing()) {
861 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
862 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
866 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::RangeSelectModifier)) {
867 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionExtend), event);
869 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
875 case RegionViewNameHighlight:
876 if (!clicked_regionview->region()->locked()) {
877 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
883 if (!internal_editing()) {
884 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::RangeSelectModifier)) {
885 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionExtend), event);
887 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
897 if (internal_editing()) {
898 /* trim notes if we're in internal edit mode and near the ends of the note */
899 NoteBase* cn = reinterpret_cast<NoteBase*>(item->get_data ("notebase"));
901 if (cn && cn->big_enough_to_trim() && cn->mouse_near_ends()) {
902 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
904 _drags->set (new NoteDrag (this, item), event);
910 if (internal_editing()) {
911 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
912 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
926 if (internal_editing()) {
927 NoteBase* cn = reinterpret_cast<NoteBase*> (item->get_data ("notebase"));
929 if (cn->mouse_near_ends()) {
930 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
932 _drags->set (new NoteDrag (this, item), event);
942 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)) &&
943 event->type == GDK_BUTTON_PRESS) {
945 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
947 } else if (event->type == GDK_BUTTON_PRESS) {
950 case FadeInHandleItem:
952 _drags->set (new FadeInDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), selection->regions), event, _cursors->fade_in);
956 case FadeOutHandleItem:
958 _drags->set (new FadeOutDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), selection->regions), event, _cursors->fade_out);
962 case StartCrossFadeItem:
963 case EndCrossFadeItem:
964 /* we might allow user to grab inside the fade to trim a region with preserve_fade_anchor. for not this is not fully implemented */
965 // if (!clicked_regionview->region()->locked()) {
966 // _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer(), true), event);
971 case FeatureLineItem:
973 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier)) {
974 remove_transient(item);
978 _drags->set (new FeatureLineDrag (this, item), event);
984 if (dynamic_cast<AutomationRegionView*> (clicked_regionview)) {
985 /* click on an automation region view; do nothing here and let the ARV's signal handler
991 if (internal_editing ()) {
995 /* click on a normal region view */
996 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
997 add_region_copy_drag (item, event, clicked_regionview);
998 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
999 add_region_brush_drag (item, event, clicked_regionview);
1001 add_region_drag (item, event, clicked_regionview);
1005 // if (!internal_editing() && (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE && !selection->regions.empty())) {
1006 // _drags->add (new SelectionDrag (this, clicked_axisview->get_selection_rect (clicked_selection)->rect, SelectionDrag::SelectionMove));
1009 _drags->start_grab (event);
1013 case RegionViewNameHighlight:
1014 case LeftFrameHandle:
1015 case RightFrameHandle:
1016 if (!clicked_regionview->region()->locked()) {
1017 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1022 case RegionViewName:
1024 /* rename happens on edit clicks */
1025 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer()), event);
1030 case ControlPointItem:
1031 _drags->set (new ControlPointDrag (this, item), event);
1035 case AutomationLineItem:
1036 _drags->set (new LineDrag (this, item), event);
1041 if (internal_editing()) {
1042 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
1043 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
1047 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
1051 case AutomationTrackItem:
1053 TimeAxisView* parent = clicked_axisview->get_parent ();
1054 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (clicked_axisview);
1056 if (parent && dynamic_cast<MidiTimeAxisView*> (parent) && atv->show_regions ()) {
1058 RouteTimeAxisView* p = dynamic_cast<RouteTimeAxisView*> (parent);
1060 boost::shared_ptr<Playlist> pl = p->track()->playlist ();
1061 if (pl->n_regions() == 0) {
1062 /* Parent has no regions; create one so that we have somewhere to put automation */
1063 _drags->set (new RegionCreateDrag (this, item, parent), event);
1065 /* See if there's a region before the click that we can extend, and extend it if so */
1066 framepos_t const t = canvas_event_frame (event);
1067 boost::shared_ptr<Region> prev = pl->find_next_region (t, End, -1);
1069 _drags->set (new RegionCreateDrag (this, item, parent), event);
1071 prev->set_length (t - prev->position ());
1075 /* rubberband drag to select automation points */
1076 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
1083 if ( get_smart_mode() ) {
1084 /* we're in "smart" joined mode, and we've clicked on a Selection */
1085 double const y = event->button.y;
1086 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
1088 /* if we're over an automation track, start a drag of its data */
1089 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
1091 _drags->set (new AutomationRangeDrag (this, atv, selection->time), event, _cursors->up_down);
1094 /* if we're over a track and a region, and in the `object' part of a region,
1095 put a selection around the region and drag both
1097 /* RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1098 if (rtv && _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
1099 boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track> (rtv->route ());
1101 boost::shared_ptr<Playlist> pl = t->playlist ();
1104 boost::shared_ptr<Region> r = pl->top_region_at (canvas_event_frame (event));
1106 RegionView* rv = rtv->view()->find_view (r);
1107 clicked_selection = select_range (rv->region()->position(),
1108 rv->region()->last_frame()+1);
1109 _drags->add (new SelectionDrag (this, item, SelectionDrag::SelectionMove));
1110 list<RegionView*> rvs;
1112 _drags->add (new RegionMoveDrag (this, item, rv, rvs, false, false));
1113 _drags->start_grab (event);
1137 switch (item_type) {
1139 _drags->set (new LineDrag (this, item), event);
1142 case ControlPointItem:
1143 _drags->set (new ControlPointDrag (this, item), event);
1149 AudioRegionView* arv = dynamic_cast<AudioRegionView *> (clicked_regionview);
1151 _drags->set (new AutomationRangeDrag (this, arv, selection->time), event, _cursors->up_down);
1152 _drags->start_grab (event);
1158 case AutomationLineItem:
1159 _drags->set (new LineDrag (this, item), event);
1169 if (event->type == GDK_BUTTON_PRESS) {
1170 _drags->set (new MouseZoomDrag (this, item), event);
1177 if (internal_editing() && item_type == NoteItem) {
1178 /* drag notes if we're in internal edit mode */
1179 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
1181 } else if (clicked_regionview) {
1183 _drags->set (new TimeFXDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1189 _drags->set (new ScrubDrag (this, item), event);
1190 scrub_reversals = 0;
1191 scrub_reverse_distance = 0;
1192 last_scrub_x = event->button.x;
1193 scrubbing_direction = 0;
1194 set_canvas_cursor (_cursors->transparent);
1206 Editor::button_press_handler_2 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1208 Editing::MouseMode const eff = effective_mouse_mode ();
1211 switch (item_type) {
1213 if (internal_editing ()) {
1214 /* no region drags in internal edit mode */
1218 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
1219 add_region_copy_drag (item, event, clicked_regionview);
1221 add_region_drag (item, event, clicked_regionview);
1223 _drags->start_grab (event);
1226 case ControlPointItem:
1227 _drags->set (new ControlPointDrag (this, item), event);
1235 switch (item_type) {
1236 case RegionViewNameHighlight:
1237 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1241 case LeftFrameHandle:
1242 case RightFrameHandle:
1243 if (!internal_editing ()) {
1244 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1249 case RegionViewName:
1250 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer()), event);
1264 /* relax till release */
1270 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1271 temporal_zoom_to_frame (false, canvas_event_frame (event));
1273 temporal_zoom_to_frame (true, canvas_event_frame(event));
1286 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1288 if (event->type == GDK_2BUTTON_PRESS) {
1289 _drags->mark_double_click ();
1290 gdk_pointer_ungrab (GDK_CURRENT_TIME);
1294 if (event->type != GDK_BUTTON_PRESS) {
1298 Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->_track_canvas_viewport->get_window();
1300 if (canvas_window) {
1301 Glib::RefPtr<const Gdk::Window> pointer_window;
1304 Gdk::ModifierType mask;
1306 pointer_window = canvas_window->get_pointer (x, y, mask);
1308 if (pointer_window == _track_canvas->get_window()) {
1309 _track_canvas->window_to_canvas (x, y, wx, wy);
1313 pre_press_cursor = current_canvas_cursor;
1315 _track_canvas->grab_focus();
1317 if (_session && _session->actively_recording()) {
1321 if (internal_editing()) {
1322 bool leave_internal_edit_mode = false;
1324 switch (item_type) {
1329 if (!dynamic_cast<MidiRegionView*> (clicked_regionview) && !dynamic_cast<AutomationRegionView*> (clicked_regionview)) {
1330 leave_internal_edit_mode = true;
1334 case PlayheadCursorItem:
1336 case TempoMarkerItem:
1337 case MeterMarkerItem:
1341 case RangeMarkerBarItem:
1342 case CdMarkerBarItem:
1343 case TransportMarkerBarItem:
1345 /* button press on these events never does anything to
1346 change the editing mode.
1354 if (leave_internal_edit_mode) {
1355 ActionManager::do_action ("MouseMode", "toggle-internal-edit");
1359 button_selection (item, event, item_type);
1361 if (!_drags->active () &&
1362 (Keyboard::is_delete_event (&event->button) ||
1363 Keyboard::is_context_menu_event (&event->button) ||
1364 Keyboard::is_edit_event (&event->button))) {
1366 /* handled by button release */
1370 //not rolling, range mode click + join_play_range : locate the PH here
1371 if ( !_drags->active () && !_session->transport_rolling() && ( effective_mouse_mode() == MouseRange ) && Config->get_always_play_range() ) {
1372 framepos_t where = canvas_event_frame (event);
1374 _session->request_locate (where, false);
1377 switch (event->button.button) {
1379 return button_press_handler_1 (item, event, item_type);
1383 return button_press_handler_2 (item, event, item_type);
1390 return button_press_dispatch (&event->button);
1399 Editor::button_press_dispatch (GdkEventButton* ev)
1401 /* this function is intended only for buttons 4 and above.
1404 Gtkmm2ext::MouseButton b (ev->state, ev->button);
1405 return button_bindings->activate (b, Gtkmm2ext::Bindings::Press);
1409 Editor::button_release_dispatch (GdkEventButton* ev)
1411 /* this function is intended only for buttons 4 and above.
1414 Gtkmm2ext::MouseButton b (ev->state, ev->button);
1415 return button_bindings->activate (b, Gtkmm2ext::Bindings::Release);
1419 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1421 framepos_t where = canvas_event_frame (event);
1422 AutomationTimeAxisView* atv = 0;
1424 if (pre_press_cursor) {
1425 set_canvas_cursor (pre_press_cursor);
1426 pre_press_cursor = 0;
1429 /* no action if we're recording */
1431 if (_session && _session->actively_recording()) {
1435 /* see if we're finishing a drag */
1437 bool were_dragging = false;
1438 if (_drags->active ()) {
1439 bool const r = _drags->end_grab (event);
1441 /* grab dragged, so do nothing else */
1445 were_dragging = true;
1448 update_region_layering_order_editor ();
1450 /* edit events get handled here */
1452 if (!_drags->active () && Keyboard::is_edit_event (&event->button)) {
1453 switch (item_type) {
1455 show_region_properties ();
1458 case TempoMarkerItem: {
1460 TempoMarker* tempo_marker;
1462 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
1463 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
1467 if ((tempo_marker = dynamic_cast<TempoMarker*> (marker)) == 0) {
1468 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
1472 edit_tempo_marker (*tempo_marker);
1476 case MeterMarkerItem: {
1478 MeterMarker* meter_marker;
1480 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
1481 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
1485 if ((meter_marker = dynamic_cast<MeterMarker*> (marker)) == 0) {
1486 fatal << _("programming error: marker for meter is not a meter marker!") << endmsg;
1489 edit_meter_marker (*meter_marker);
1493 case RegionViewName:
1494 if (clicked_regionview->name_active()) {
1495 return mouse_rename_region (item, event);
1499 case ControlPointItem:
1500 edit_control_point (item);
1509 /* context menu events get handled here */
1510 if (Keyboard::is_context_menu_event (&event->button)) {
1512 context_click_event = *event;
1514 if (!_drags->active ()) {
1516 /* no matter which button pops up the context menu, tell the menu
1517 widget to use button 1 to drive menu selection.
1520 switch (item_type) {
1522 case FadeInHandleItem:
1524 case FadeOutHandleItem:
1525 popup_fade_context_menu (1, event->button.time, item, item_type);
1528 case StartCrossFadeItem:
1529 popup_xfade_in_context_menu (1, event->button.time, item, item_type);
1532 case EndCrossFadeItem:
1533 popup_xfade_out_context_menu (1, event->button.time, item, item_type);
1537 popup_track_context_menu (1, event->button.time, item_type, false);
1541 case RegionViewNameHighlight:
1542 case LeftFrameHandle:
1543 case RightFrameHandle:
1544 case RegionViewName:
1545 popup_track_context_menu (1, event->button.time, item_type, false);
1549 popup_track_context_menu (1, event->button.time, item_type, true);
1552 case AutomationTrackItem:
1553 popup_track_context_menu (1, event->button.time, item_type, false);
1557 case RangeMarkerBarItem:
1558 case TransportMarkerBarItem:
1559 case CdMarkerBarItem:
1563 popup_ruler_menu (where, item_type);
1567 marker_context_menu (&event->button, item);
1570 case TempoMarkerItem:
1571 tempo_or_meter_marker_context_menu (&event->button, item);
1574 case MeterMarkerItem:
1575 tempo_or_meter_marker_context_menu (&event->button, item);
1578 case CrossfadeViewItem:
1579 popup_track_context_menu (1, event->button.time, item_type, false);
1582 case ControlPointItem:
1583 popup_control_point_context_menu (item, event);
1594 /* delete events get handled here */
1596 Editing::MouseMode const eff = effective_mouse_mode ();
1598 if (!_drags->active () && Keyboard::is_delete_event (&event->button)) {
1600 switch (item_type) {
1601 case TempoMarkerItem:
1602 remove_tempo_marker (item);
1605 case MeterMarkerItem:
1606 remove_meter_marker (item);
1610 remove_marker (*item, event);
1614 if (eff == MouseObject) {
1615 remove_clicked_region ();
1619 case ControlPointItem:
1620 remove_control_point (item);
1624 remove_midi_note (item, event);
1633 switch (event->button.button) {
1636 switch (item_type) {
1637 /* see comments in button_press_handler */
1638 case PlayheadCursorItem:
1641 case AutomationLineItem:
1642 case StartSelectionTrimItem:
1643 case EndSelectionTrimItem:
1647 if (!_dragging_playhead) {
1648 snap_to_with_modifier (where, event, 0, true);
1649 mouse_add_new_marker (where);
1653 case CdMarkerBarItem:
1654 if (!_dragging_playhead) {
1655 // if we get here then a dragged range wasn't done
1656 snap_to_with_modifier (where, event, 0, true);
1657 mouse_add_new_marker (where, true);
1662 if (!_dragging_playhead) {
1663 snap_to_with_modifier (where, event);
1664 mouse_add_new_tempo_event (where);
1669 if (!_dragging_playhead) {
1670 mouse_add_new_meter_event (pixel_to_sample (event->button.x));
1681 switch (item_type) {
1682 case AutomationTrackItem:
1683 atv = dynamic_cast<AutomationTimeAxisView*>(clicked_axisview);
1685 atv->add_automation_event (event, where, event->button.y);
1695 switch (item_type) {
1698 /* check that we didn't drag before releasing, since
1699 its really annoying to create new control
1700 points when doing this.
1702 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (clicked_regionview);
1703 if (!were_dragging && arv) {
1704 arv->add_gain_point_event (item, event);
1710 case AutomationTrackItem:
1711 dynamic_cast<AutomationTimeAxisView*>(clicked_axisview)->
1712 add_automation_event (event, where, event->button.y);
1721 set_canvas_cursor (current_canvas_cursor);
1722 if (scrubbing_direction == 0) {
1723 /* no drag, just a click */
1724 switch (item_type) {
1726 play_selected_region ();
1732 /* make sure we stop */
1733 _session->request_transport_speed (0.0);
1742 /* do any (de)selection operations that should occur on button release */
1743 button_selection (item, event, item_type);
1752 switch (item_type) {
1754 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1756 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier|Keyboard::SecondaryModifier))) {
1759 // Button2 click is unused
1774 // x_style_paste (where, 1.0);
1795 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1802 switch (item_type) {
1803 case ControlPointItem:
1804 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1805 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1810 at_y = cp->get_y ();
1811 cp->i2w (at_x, at_y);
1815 fraction = 1.0 - (cp->get_y() / cp->line().height());
1817 if (is_drawable() && !_drags->active ()) {
1818 set_canvas_cursor (_cursors->fader);
1821 _verbose_cursor->set (cp->line().get_verbose_cursor_string (fraction), at_x, at_y);
1822 _verbose_cursor->show ();
1827 if (mouse_mode == MouseGain) {
1828 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1830 line->set_outline_color (ARDOUR_UI::config()->get_canvasvar_EnteredGainLine());
1832 if (is_drawable()) {
1833 set_canvas_cursor (_cursors->fader);
1838 case AutomationLineItem:
1839 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1840 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1842 line->set_outline_color (ARDOUR_UI::config()->get_canvasvar_EnteredAutomationLine());
1844 if (is_drawable()) {
1845 set_canvas_cursor (_cursors->fader);
1850 case RegionViewNameHighlight:
1851 if (is_drawable() && effective_mouse_mode() == MouseObject && entered_regionview) {
1852 set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1853 _over_region_trim_target = true;
1857 case LeftFrameHandle:
1858 case RightFrameHandle:
1859 if (is_drawable() && effective_mouse_mode() == MouseObject && !internal_editing() && entered_regionview) {
1860 set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1864 case StartSelectionTrimItem:
1865 if (is_drawable()) {
1866 set_canvas_cursor (_cursors->left_side_trim);
1869 case EndSelectionTrimItem:
1870 if (is_drawable()) {
1871 set_canvas_cursor (_cursors->right_side_trim);
1875 case PlayheadCursorItem:
1876 if (is_drawable()) {
1877 switch (_edit_point) {
1879 set_canvas_cursor (_cursors->grabber_edit_point);
1882 set_canvas_cursor (_cursors->grabber);
1888 case RegionViewName:
1890 /* when the name is not an active item, the entire name highlight is for trimming */
1892 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1893 if (mouse_mode == MouseObject && is_drawable()) {
1894 set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1895 _over_region_trim_target = true;
1901 case AutomationTrackItem:
1902 if (is_drawable()) {
1903 Gdk::Cursor *cursor;
1904 switch (mouse_mode) {
1906 cursor = _cursors->selector;
1909 cursor = _cursors->zoom_in;
1912 cursor = _cursors->cross_hair;
1916 set_canvas_cursor (cursor);
1918 AutomationTimeAxisView* atv;
1919 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1920 clear_entered_track = false;
1921 set_entered_track (atv);
1927 case RangeMarkerBarItem:
1928 case TransportMarkerBarItem:
1929 case CdMarkerBarItem:
1932 if (is_drawable()) {
1933 set_canvas_cursor (_cursors->timebar);
1938 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1941 entered_marker = marker;
1942 marker->set_color_rgba (ARDOUR_UI::config()->get_canvasvar_EnteredMarker());
1944 case MeterMarkerItem:
1945 case TempoMarkerItem:
1946 if (is_drawable()) {
1947 set_canvas_cursor (_cursors->timebar);
1951 case FadeInHandleItem:
1952 if (mouse_mode == MouseObject && !internal_editing()) {
1953 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
1955 RegionView* rv = static_cast<RegionView*>(item->get_data ("regionview"));
1956 rect->set_fill_color (rv->get_fill_color());
1957 set_canvas_cursor (_cursors->fade_in);
1962 case FadeOutHandleItem:
1963 if (mouse_mode == MouseObject && !internal_editing()) {
1964 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
1966 RegionView* rv = static_cast<RegionView*>(item->get_data ("regionview"));
1967 rect->set_fill_color (rv->get_fill_color ());
1968 set_canvas_cursor (_cursors->fade_out);
1972 case FeatureLineItem:
1974 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1975 line->set_outline_color (0xFF0000FF);
1980 if ( get_smart_mode() ) {
1981 set_canvas_cursor ();
1989 /* second pass to handle entered track status in a comprehensible way.
1992 switch (item_type) {
1994 case AutomationLineItem:
1995 case ControlPointItem:
1996 /* these do not affect the current entered track state */
1997 clear_entered_track = false;
2000 case AutomationTrackItem:
2001 /* handled above already */
2005 set_entered_track (0);
2013 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent*, ItemType item_type)
2022 switch (item_type) {
2023 case ControlPointItem:
2024 if (is_drawable()) {
2025 set_canvas_cursor (current_canvas_cursor);
2028 _verbose_cursor->hide ();
2031 case RegionViewNameHighlight:
2032 case LeftFrameHandle:
2033 case RightFrameHandle:
2034 case StartSelectionTrimItem:
2035 case EndSelectionTrimItem:
2036 case PlayheadCursorItem:
2038 _over_region_trim_target = false;
2040 if (is_drawable()) {
2041 set_canvas_cursor (current_canvas_cursor);
2046 case AutomationLineItem:
2047 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
2049 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
2051 line->set_outline_color (al->get_line_color());
2054 if (is_drawable()) {
2055 set_canvas_cursor (current_canvas_cursor);
2059 case RegionViewName:
2060 /* see enter_handler() for notes */
2061 _over_region_trim_target = false;
2063 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
2064 if (is_drawable() && mouse_mode == MouseObject) {
2065 set_canvas_cursor (current_canvas_cursor);
2070 case RangeMarkerBarItem:
2071 case TransportMarkerBarItem:
2072 case CdMarkerBarItem:
2076 if (is_drawable()) {
2077 set_canvas_cursor (current_canvas_cursor);
2082 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2086 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
2087 location_flags_changed (loc, this);
2090 case MeterMarkerItem:
2091 case TempoMarkerItem:
2093 if (is_drawable()) {
2094 set_canvas_cursor (current_canvas_cursor);
2099 case FadeInHandleItem:
2100 case FadeOutHandleItem:
2101 rv = static_cast<RegionView*>(item->get_data ("regionview"));
2103 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
2105 rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_InactiveFadeHandle());
2108 set_canvas_cursor (current_canvas_cursor);
2111 case AutomationTrackItem:
2112 if (is_drawable()) {
2113 set_canvas_cursor (current_canvas_cursor);
2114 clear_entered_track = true;
2115 Glib::signal_idle().connect (sigc::mem_fun(*this, &Editor::left_automation_track));
2118 case FeatureLineItem:
2120 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
2121 line->set_outline_color (ARDOUR_UI::config()->get_canvasvar_ZeroLine());
2133 Editor::left_automation_track ()
2135 if (clear_entered_track) {
2136 set_entered_track (0);
2137 clear_entered_track = false;
2143 Editor::scrub (framepos_t frame, double current_x)
2147 if (scrubbing_direction == 0) {
2149 _session->request_locate (frame, false);
2150 _session->request_transport_speed (0.1);
2151 scrubbing_direction = 1;
2155 if (last_scrub_x > current_x) {
2157 /* pointer moved to the left */
2159 if (scrubbing_direction > 0) {
2161 /* we reversed direction to go backwards */
2164 scrub_reverse_distance += (int) (last_scrub_x - current_x);
2168 /* still moving to the left (backwards) */
2170 scrub_reversals = 0;
2171 scrub_reverse_distance = 0;
2173 delta = 0.01 * (last_scrub_x - current_x);
2174 _session->request_transport_speed_nonzero (_session->transport_speed() - delta);
2178 /* pointer moved to the right */
2180 if (scrubbing_direction < 0) {
2181 /* we reversed direction to go forward */
2184 scrub_reverse_distance += (int) (current_x - last_scrub_x);
2187 /* still moving to the right */
2189 scrub_reversals = 0;
2190 scrub_reverse_distance = 0;
2192 delta = 0.01 * (current_x - last_scrub_x);
2193 _session->request_transport_speed_nonzero (_session->transport_speed() + delta);
2197 /* if there have been more than 2 opposite motion moves detected, or one that moves
2198 back more than 10 pixels, reverse direction
2201 if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
2203 if (scrubbing_direction > 0) {
2204 /* was forwards, go backwards */
2205 _session->request_transport_speed (-0.1);
2206 scrubbing_direction = -1;
2208 /* was backwards, go forwards */
2209 _session->request_transport_speed (0.1);
2210 scrubbing_direction = 1;
2213 scrub_reverse_distance = 0;
2214 scrub_reversals = 0;
2218 last_scrub_x = current_x;
2222 Editor::motion_handler (ArdourCanvas::Item* /*item*/, GdkEvent* event, bool from_autoscroll)
2224 _last_motion_y = event->motion.y;
2226 if (event->motion.is_hint) {
2229 /* We call this so that MOTION_NOTIFY events continue to be
2230 delivered to the canvas. We need to do this because we set
2231 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
2232 the density of the events, at the expense of a round-trip
2233 to the server. Given that this will mostly occur on cases
2234 where DISPLAY = :0.0, and given the cost of what the motion
2235 event might do, its a good tradeoff.
2238 _track_canvas->get_pointer (x, y);
2241 if (current_stepping_trackview) {
2242 /* don't keep the persistent stepped trackview if the mouse moves */
2243 current_stepping_trackview = 0;
2244 step_timeout.disconnect ();
2247 if (_session && _session->actively_recording()) {
2248 /* Sorry. no dragging stuff around while we record */
2252 JoinObjectRangeState const old = _join_object_range_state;
2253 update_join_object_range_location (event->motion.x, event->motion.y);
2255 if (!_internal_editing && _join_object_range_state != old) {
2256 set_canvas_cursor ();
2259 if (!_internal_editing && _over_region_trim_target) {
2260 set_canvas_cursor_for_region_view (event->motion.x, entered_regionview);
2263 bool handled = false;
2264 if (_drags->active ()) {
2265 handled = _drags->motion_handler (event, from_autoscroll);
2272 track_canvas_motion (event);
2277 Editor::can_remove_control_point (ArdourCanvas::Item* item)
2279 ControlPoint* control_point;
2281 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2282 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2286 AutomationLine& line = control_point->line ();
2287 if (dynamic_cast<AudioRegionGainLine*> (&line)) {
2288 /* we shouldn't remove the first or last gain point in region gain lines */
2289 if (line.is_last_point(*control_point) || line.is_first_point(*control_point)) {
2298 Editor::remove_control_point (ArdourCanvas::Item* item)
2300 if (!can_remove_control_point (item)) {
2304 ControlPoint* control_point;
2306 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2307 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2311 control_point->line().remove_point (*control_point);
2315 Editor::edit_control_point (ArdourCanvas::Item* item)
2317 ControlPoint* p = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"));
2320 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2324 ControlPointDialog d (p);
2327 if (d.run () != RESPONSE_ACCEPT) {
2331 p->line().modify_point_y (*p, d.get_y_fraction ());
2335 Editor::edit_notes (TimeAxisViewItem& tavi)
2337 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(&tavi);
2343 MidiRegionView::Selection const & s = mrv->selection();
2349 EditNoteDialog* d = new EditNoteDialog (&(*s.begin())->region_view(), s);
2353 d->signal_response().connect (sigc::bind (sigc::mem_fun (*this, &Editor::note_edit_done), d));
2357 Editor::note_edit_done (int r, EditNoteDialog* d)
2364 Editor::visible_order_range (int* low, int* high) const
2366 *low = TimeAxisView::max_order ();
2369 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
2371 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
2373 if (!rtv->hidden()) {
2375 if (*high < rtv->order()) {
2376 *high = rtv->order ();
2379 if (*low > rtv->order()) {
2380 *low = rtv->order ();
2387 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
2389 /* Either add to or set the set the region selection, unless
2390 this is an alignment click (control used)
2393 if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
2394 TimeAxisView* tv = &rv.get_time_axis_view();
2395 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
2397 if (rtv && rtv->is_track()) {
2398 speed = rtv->track()->speed();
2401 framepos_t where = get_preferred_edit_position();
2405 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
2407 align_region (rv.region(), SyncPoint, (framepos_t) (where * speed));
2409 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
2411 align_region (rv.region(), End, (framepos_t) (where * speed));
2415 align_region (rv.region(), Start, (framepos_t) (where * speed));
2422 Editor::collect_new_region_view (RegionView* rv)
2424 latest_regionviews.push_back (rv);
2428 Editor::collect_and_select_new_region_view (RegionView* rv)
2431 latest_regionviews.push_back (rv);
2435 Editor::cancel_selection ()
2437 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2438 (*i)->hide_selection ();
2441 selection->clear ();
2442 clicked_selection = 0;
2446 Editor::cancel_time_selection ()
2448 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2449 (*i)->hide_selection ();
2451 selection->time.clear ();
2452 clicked_selection = 0;
2456 Editor::point_trim (GdkEvent* event, framepos_t new_bound)
2458 RegionView* rv = clicked_regionview;
2460 /* Choose action dependant on which button was pressed */
2461 switch (event->button.button) {
2463 begin_reversible_command (_("start point trim"));
2465 if (selection->selected (rv)) {
2466 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
2467 i != selection->regions.by_layer().end(); ++i)
2469 if (!(*i)->region()->locked()) {
2470 (*i)->region()->clear_changes ();
2471 (*i)->region()->trim_front (new_bound);
2472 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2477 if (!rv->region()->locked()) {
2478 rv->region()->clear_changes ();
2479 rv->region()->trim_front (new_bound);
2480 _session->add_command(new StatefulDiffCommand (rv->region()));
2484 commit_reversible_command();
2488 begin_reversible_command (_("End point trim"));
2490 if (selection->selected (rv)) {
2492 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
2494 if (!(*i)->region()->locked()) {
2495 (*i)->region()->clear_changes();
2496 (*i)->region()->trim_end (new_bound);
2497 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2503 if (!rv->region()->locked()) {
2504 rv->region()->clear_changes ();
2505 rv->region()->trim_end (new_bound);
2506 _session->add_command (new StatefulDiffCommand (rv->region()));
2510 commit_reversible_command();
2519 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* /*event*/)
2524 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2525 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2529 Location* location = find_location_from_marker (marker, is_start);
2530 location->set_hidden (true, this);
2535 Editor::reposition_zoom_rect (framepos_t start, framepos_t end)
2537 double x1 = sample_to_pixel (start);
2538 double x2 = sample_to_pixel (end);
2539 double y2 = _full_canvas_height - 1.0;
2541 zoom_rect->set (ArdourCanvas::Rect (x1, 1.0, x2, y2));
2546 Editor::mouse_rename_region (ArdourCanvas::Item* /*item*/, GdkEvent* /*event*/)
2548 using namespace Gtkmm2ext;
2550 ArdourPrompter prompter (false);
2552 prompter.set_prompt (_("Name for region:"));
2553 prompter.set_initial_text (clicked_regionview->region()->name());
2554 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
2555 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
2556 prompter.show_all ();
2557 switch (prompter.run ()) {
2558 case Gtk::RESPONSE_ACCEPT:
2560 prompter.get_result(str);
2562 clicked_regionview->region()->set_name (str);
2571 Editor::mouse_brush_insert_region (RegionView* rv, framepos_t pos)
2573 /* no brushing without a useful snap setting */
2575 switch (_snap_mode) {
2577 return; /* can't work because it allows region to be placed anywhere */
2582 switch (_snap_type) {
2590 /* don't brush a copy over the original */
2592 if (pos == rv->region()->position()) {
2596 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
2598 if (rtv == 0 || !rtv->is_track()) {
2602 boost::shared_ptr<Playlist> playlist = rtv->playlist();
2603 double speed = rtv->track()->speed();
2605 playlist->clear_changes ();
2606 boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region(), true));
2607 playlist->add_region (new_region, (framepos_t) (pos * speed));
2608 _session->add_command (new StatefulDiffCommand (playlist));
2610 // playlist is frozen, so we have to update manually XXX this is disgusting
2612 playlist->RegionAdded (new_region); /* EMIT SIGNAL */
2616 Editor::track_height_step_timeout ()
2618 if (get_microseconds() - last_track_height_step_timestamp < 250000) {
2619 current_stepping_trackview = 0;
2626 Editor::add_region_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2628 assert (region_view);
2630 if (!region_view->region()->playlist()) {
2634 _region_motion_group->raise_to_top ();
2636 if (Config->get_edit_mode() == Splice) {
2637 _drags->add (new RegionSpliceDrag (this, item, region_view, selection->regions.by_layer()));
2639 _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), false, false));
2644 Editor::add_region_copy_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2646 assert (region_view);
2648 if (!region_view->region()->playlist()) {
2652 _region_motion_group->raise_to_top ();
2654 _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), false, true));
2658 Editor::add_region_brush_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2660 assert (region_view);
2662 if (!region_view->region()->playlist()) {
2666 if (Config->get_edit_mode() == Splice) {
2670 _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), true, false));
2672 begin_reversible_command (Operations::drag_region_brush);
2675 /** Start a grab where a time range is selected, track(s) are selected, and the
2676 * user clicks and drags a region with a modifier in order to create a new region containing
2677 * the section of the clicked region that lies within the time range.
2680 Editor::start_selection_grab (ArdourCanvas::Item* /*item*/, GdkEvent* event)
2682 if (clicked_regionview == 0) {
2686 /* lets try to create new Region for the selection */
2688 vector<boost::shared_ptr<Region> > new_regions;
2689 create_region_from_selection (new_regions);
2691 if (new_regions.empty()) {
2695 /* XXX fix me one day to use all new regions */
2697 boost::shared_ptr<Region> region (new_regions.front());
2699 /* add it to the current stream/playlist.
2701 tricky: the streamview for the track will add a new regionview. we will
2702 catch the signal it sends when it creates the regionview to
2703 set the regionview we want to then drag.
2706 latest_regionviews.clear();
2707 sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
2709 /* A selection grab currently creates two undo/redo operations, one for
2710 creating the new region and another for moving it.
2713 begin_reversible_command (Operations::selection_grab);
2715 boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
2717 playlist->clear_changes ();
2718 clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
2719 _session->add_command(new StatefulDiffCommand (playlist));
2721 commit_reversible_command ();
2725 if (latest_regionviews.empty()) {
2726 /* something went wrong */
2730 /* we need to deselect all other regionviews, and select this one
2731 i'm ignoring undo stuff, because the region creation will take care of it
2733 selection->set (latest_regionviews);
2735 _drags->set (new RegionMoveDrag (this, latest_regionviews.front()->get_canvas_group(), latest_regionviews.front(), latest_regionviews, false, false), event);
2741 if (_drags->active ()) {
2744 selection->clear ();
2749 Editor::set_internal_edit (bool yn)
2751 if (_internal_editing == yn) {
2755 _internal_editing = yn;
2758 pre_internal_mouse_mode = mouse_mode;
2759 pre_internal_snap_type = _snap_type;
2760 pre_internal_snap_mode = _snap_mode;
2762 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2763 (*i)->enter_internal_edit_mode ();
2766 set_snap_to (internal_snap_type);
2767 set_snap_mode (internal_snap_mode);
2771 internal_snap_mode = _snap_mode;
2772 internal_snap_type = _snap_type;
2774 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2775 (*i)->leave_internal_edit_mode ();
2778 if (mouse_mode == MouseDraw && pre_internal_mouse_mode != MouseDraw) {
2779 /* we were drawing .. flip back to something sensible */
2780 set_mouse_mode (pre_internal_mouse_mode);
2783 set_snap_to (pre_internal_snap_type);
2784 set_snap_mode (pre_internal_snap_mode);
2787 set_canvas_cursor ();
2790 /** Update _join_object_range_state which indicate whether we are over the top or bottom half of a region view,
2791 * used by the `join object/range' tool mode.
2794 Editor::update_join_object_range_location (double /*x*/, double y)
2796 /* XXX: actually, this decides based on whether the mouse is in the top
2797 or bottom half of a the waveform part RouteTimeAxisView;
2799 Note that entered_{track,regionview} is not always setup (e.g. if
2800 the mouse is over a TimeSelection), and to get a Region
2801 that we're over requires searching the playlist.
2804 if ( !get_smart_mode() ) {
2805 _join_object_range_state = JOIN_OBJECT_RANGE_NONE;
2809 if (mouse_mode == MouseObject) {
2810 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2811 } else if (mouse_mode == MouseRange) {
2812 _join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
2815 /* XXX: maybe we should make entered_track work in all cases, rather than resorting to this */
2816 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
2820 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
2825 rtv->canvas_display()->canvas_to_item (cx, cy);
2827 double const c = cy / (rtv->view()->child_height() - TimeAxisViewItem::NAME_HIGHLIGHT_SIZE);
2829 _join_object_range_state = c <= 0.5 ? JOIN_OBJECT_RANGE_RANGE : JOIN_OBJECT_RANGE_OBJECT;
2835 Editor::effective_mouse_mode () const
2837 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
2839 } else if (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE) {
2847 Editor::remove_midi_note (ArdourCanvas::Item* item, GdkEvent *)
2849 NoteBase* e = reinterpret_cast<NoteBase*> (item->get_data ("notebase"));
2852 e->region_view().delete_note (e->note ());
2856 Editor::set_canvas_cursor_for_region_view (double x, RegionView* rv)
2858 /* XXX: this check should not be necessary */
2865 ArdourCanvas::Group* g = rv->get_canvas_group ();
2866 ArdourCanvas::Group* p = g->parent ();
2868 /* Compute x in region view parent coordinates */
2870 p->canvas_to_item (x, dy);
2872 boost::optional<ArdourCanvas::Rect> item_bbox = g->bounding_box ();
2874 ArdourCanvas::Rect parent_bbox = g->item_to_parent (item_bbox.get ());
2876 /* Halfway across the region */
2877 double const h = (parent_bbox.x0 + parent_bbox.x1) / 2;
2879 Trimmable::CanTrim ct = rv->region()->can_trim ();
2881 if (ct & Trimmable::FrontTrimEarlier) {
2882 set_canvas_cursor (_cursors->left_side_trim);
2884 set_canvas_cursor (_cursors->left_side_trim_right_only);
2887 if (ct & Trimmable::EndTrimLater) {
2888 set_canvas_cursor (_cursors->right_side_trim);
2890 set_canvas_cursor (_cursors->right_side_trim_left_only);
2895 /** Obtain the pointer position in canvas coordinates */
2897 Editor::get_pointer_position (double& x, double& y) const
2900 _track_canvas->get_pointer (px, py);
2901 _track_canvas->window_to_canvas (px, py, x, y);