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 bool with_guard_points = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
1686 atv->add_automation_event (event, where, event->button.y, with_guard_points);
1696 switch (item_type) {
1699 /* check that we didn't drag before releasing, since
1700 its really annoying to create new control
1701 points when doing this.
1703 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (clicked_regionview);
1704 if (!were_dragging && arv) {
1705 bool with_guard_points = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
1706 arv->add_gain_point_event (item, event, with_guard_points);
1712 case AutomationTrackItem: {
1713 bool with_guard_points = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
1714 dynamic_cast<AutomationTimeAxisView*>(clicked_axisview)->
1715 add_automation_event (event, where, event->button.y, with_guard_points);
1725 set_canvas_cursor (current_canvas_cursor);
1726 if (scrubbing_direction == 0) {
1727 /* no drag, just a click */
1728 switch (item_type) {
1730 play_selected_region ();
1736 /* make sure we stop */
1737 _session->request_transport_speed (0.0);
1746 /* do any (de)selection operations that should occur on button release */
1747 button_selection (item, event, item_type);
1756 switch (item_type) {
1758 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1760 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier|Keyboard::SecondaryModifier))) {
1763 // Button2 click is unused
1778 // x_style_paste (where, 1.0);
1799 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1806 switch (item_type) {
1807 case ControlPointItem:
1808 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1809 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1814 at_y = cp->get_y ();
1815 cp->i2w (at_x, at_y);
1819 fraction = 1.0 - (cp->get_y() / cp->line().height());
1821 if (is_drawable() && !_drags->active ()) {
1822 set_canvas_cursor (_cursors->fader);
1825 _verbose_cursor->set (cp->line().get_verbose_cursor_string (fraction), at_x, at_y);
1826 _verbose_cursor->show ();
1831 if (mouse_mode == MouseGain) {
1832 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1834 line->set_outline_color (ARDOUR_UI::config()->get_canvasvar_EnteredGainLine());
1836 if (is_drawable()) {
1837 set_canvas_cursor (_cursors->fader);
1842 case AutomationLineItem:
1843 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1844 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1846 line->set_outline_color (ARDOUR_UI::config()->get_canvasvar_EnteredAutomationLine());
1848 if (is_drawable()) {
1849 set_canvas_cursor (_cursors->fader);
1854 case RegionViewNameHighlight:
1855 if (is_drawable() && effective_mouse_mode() == MouseObject && entered_regionview) {
1856 set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1857 _over_region_trim_target = true;
1861 case LeftFrameHandle:
1862 case RightFrameHandle:
1863 if (is_drawable() && effective_mouse_mode() == MouseObject && !internal_editing() && entered_regionview) {
1864 set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1868 case StartSelectionTrimItem:
1869 if (is_drawable()) {
1870 set_canvas_cursor (_cursors->left_side_trim);
1873 case EndSelectionTrimItem:
1874 if (is_drawable()) {
1875 set_canvas_cursor (_cursors->right_side_trim);
1879 case PlayheadCursorItem:
1880 if (is_drawable()) {
1881 switch (_edit_point) {
1883 set_canvas_cursor (_cursors->grabber_edit_point);
1886 set_canvas_cursor (_cursors->grabber);
1892 case RegionViewName:
1894 /* when the name is not an active item, the entire name highlight is for trimming */
1896 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1897 if (mouse_mode == MouseObject && is_drawable()) {
1898 set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1899 _over_region_trim_target = true;
1905 case AutomationTrackItem:
1906 if (is_drawable()) {
1907 Gdk::Cursor *cursor;
1908 switch (mouse_mode) {
1910 cursor = _cursors->selector;
1913 cursor = _cursors->zoom_in;
1916 cursor = _cursors->cross_hair;
1920 set_canvas_cursor (cursor);
1922 AutomationTimeAxisView* atv;
1923 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1924 clear_entered_track = false;
1925 set_entered_track (atv);
1931 case RangeMarkerBarItem:
1932 case TransportMarkerBarItem:
1933 case CdMarkerBarItem:
1936 if (is_drawable()) {
1937 set_canvas_cursor (_cursors->timebar);
1942 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1945 entered_marker = marker;
1946 marker->set_color_rgba (ARDOUR_UI::config()->get_canvasvar_EnteredMarker());
1948 case MeterMarkerItem:
1949 case TempoMarkerItem:
1950 if (is_drawable()) {
1951 set_canvas_cursor (_cursors->timebar);
1955 case FadeInHandleItem:
1956 if (mouse_mode == MouseObject && !internal_editing()) {
1957 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
1959 RegionView* rv = static_cast<RegionView*>(item->get_data ("regionview"));
1960 rect->set_fill_color (rv->get_fill_color());
1961 set_canvas_cursor (_cursors->fade_in);
1966 case FadeOutHandleItem:
1967 if (mouse_mode == MouseObject && !internal_editing()) {
1968 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
1970 RegionView* rv = static_cast<RegionView*>(item->get_data ("regionview"));
1971 rect->set_fill_color (rv->get_fill_color ());
1972 set_canvas_cursor (_cursors->fade_out);
1976 case FeatureLineItem:
1978 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1979 line->set_outline_color (0xFF0000FF);
1984 if ( get_smart_mode() ) {
1985 set_canvas_cursor ();
1993 /* second pass to handle entered track status in a comprehensible way.
1996 switch (item_type) {
1998 case AutomationLineItem:
1999 case ControlPointItem:
2000 /* these do not affect the current entered track state */
2001 clear_entered_track = false;
2004 case AutomationTrackItem:
2005 /* handled above already */
2009 set_entered_track (0);
2017 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent*, ItemType item_type)
2026 switch (item_type) {
2027 case ControlPointItem:
2028 if (is_drawable()) {
2029 set_canvas_cursor (current_canvas_cursor);
2032 _verbose_cursor->hide ();
2035 case RegionViewNameHighlight:
2036 case LeftFrameHandle:
2037 case RightFrameHandle:
2038 case StartSelectionTrimItem:
2039 case EndSelectionTrimItem:
2040 case PlayheadCursorItem:
2042 _over_region_trim_target = false;
2044 if (is_drawable()) {
2045 set_canvas_cursor (current_canvas_cursor);
2050 case AutomationLineItem:
2051 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
2053 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
2055 line->set_outline_color (al->get_line_color());
2058 if (is_drawable()) {
2059 set_canvas_cursor (current_canvas_cursor);
2063 case RegionViewName:
2064 /* see enter_handler() for notes */
2065 _over_region_trim_target = false;
2067 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
2068 if (is_drawable() && mouse_mode == MouseObject) {
2069 set_canvas_cursor (current_canvas_cursor);
2074 case RangeMarkerBarItem:
2075 case TransportMarkerBarItem:
2076 case CdMarkerBarItem:
2080 if (is_drawable()) {
2081 set_canvas_cursor (current_canvas_cursor);
2086 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2090 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
2091 location_flags_changed (loc, this);
2094 case MeterMarkerItem:
2095 case TempoMarkerItem:
2097 if (is_drawable()) {
2098 set_canvas_cursor (current_canvas_cursor);
2103 case FadeInHandleItem:
2104 case FadeOutHandleItem:
2105 rv = static_cast<RegionView*>(item->get_data ("regionview"));
2107 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
2109 rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_InactiveFadeHandle());
2112 set_canvas_cursor (current_canvas_cursor);
2115 case AutomationTrackItem:
2116 if (is_drawable()) {
2117 set_canvas_cursor (current_canvas_cursor);
2118 clear_entered_track = true;
2119 Glib::signal_idle().connect (sigc::mem_fun(*this, &Editor::left_automation_track));
2122 case FeatureLineItem:
2124 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
2125 line->set_outline_color (ARDOUR_UI::config()->get_canvasvar_ZeroLine());
2137 Editor::left_automation_track ()
2139 if (clear_entered_track) {
2140 set_entered_track (0);
2141 clear_entered_track = false;
2147 Editor::scrub (framepos_t frame, double current_x)
2151 if (scrubbing_direction == 0) {
2153 _session->request_locate (frame, false);
2154 _session->request_transport_speed (0.1);
2155 scrubbing_direction = 1;
2159 if (last_scrub_x > current_x) {
2161 /* pointer moved to the left */
2163 if (scrubbing_direction > 0) {
2165 /* we reversed direction to go backwards */
2168 scrub_reverse_distance += (int) (last_scrub_x - current_x);
2172 /* still moving to the left (backwards) */
2174 scrub_reversals = 0;
2175 scrub_reverse_distance = 0;
2177 delta = 0.01 * (last_scrub_x - current_x);
2178 _session->request_transport_speed_nonzero (_session->transport_speed() - delta);
2182 /* pointer moved to the right */
2184 if (scrubbing_direction < 0) {
2185 /* we reversed direction to go forward */
2188 scrub_reverse_distance += (int) (current_x - last_scrub_x);
2191 /* still moving to the right */
2193 scrub_reversals = 0;
2194 scrub_reverse_distance = 0;
2196 delta = 0.01 * (current_x - last_scrub_x);
2197 _session->request_transport_speed_nonzero (_session->transport_speed() + delta);
2201 /* if there have been more than 2 opposite motion moves detected, or one that moves
2202 back more than 10 pixels, reverse direction
2205 if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
2207 if (scrubbing_direction > 0) {
2208 /* was forwards, go backwards */
2209 _session->request_transport_speed (-0.1);
2210 scrubbing_direction = -1;
2212 /* was backwards, go forwards */
2213 _session->request_transport_speed (0.1);
2214 scrubbing_direction = 1;
2217 scrub_reverse_distance = 0;
2218 scrub_reversals = 0;
2222 last_scrub_x = current_x;
2226 Editor::motion_handler (ArdourCanvas::Item* /*item*/, GdkEvent* event, bool from_autoscroll)
2228 _last_motion_y = event->motion.y;
2230 if (event->motion.is_hint) {
2233 /* We call this so that MOTION_NOTIFY events continue to be
2234 delivered to the canvas. We need to do this because we set
2235 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
2236 the density of the events, at the expense of a round-trip
2237 to the server. Given that this will mostly occur on cases
2238 where DISPLAY = :0.0, and given the cost of what the motion
2239 event might do, its a good tradeoff.
2242 _track_canvas->get_pointer (x, y);
2245 if (current_stepping_trackview) {
2246 /* don't keep the persistent stepped trackview if the mouse moves */
2247 current_stepping_trackview = 0;
2248 step_timeout.disconnect ();
2251 if (_session && _session->actively_recording()) {
2252 /* Sorry. no dragging stuff around while we record */
2256 JoinObjectRangeState const old = _join_object_range_state;
2257 update_join_object_range_location (event->motion.x, event->motion.y);
2259 if (!_internal_editing && _join_object_range_state != old) {
2260 set_canvas_cursor ();
2263 if (!_internal_editing && _over_region_trim_target) {
2264 set_canvas_cursor_for_region_view (event->motion.x, entered_regionview);
2267 bool handled = false;
2268 if (_drags->active ()) {
2269 handled = _drags->motion_handler (event, from_autoscroll);
2276 track_canvas_motion (event);
2281 Editor::can_remove_control_point (ArdourCanvas::Item* item)
2283 ControlPoint* control_point;
2285 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2286 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2290 AutomationLine& line = control_point->line ();
2291 if (dynamic_cast<AudioRegionGainLine*> (&line)) {
2292 /* we shouldn't remove the first or last gain point in region gain lines */
2293 if (line.is_last_point(*control_point) || line.is_first_point(*control_point)) {
2302 Editor::remove_control_point (ArdourCanvas::Item* item)
2304 if (!can_remove_control_point (item)) {
2308 ControlPoint* control_point;
2310 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2311 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2315 control_point->line().remove_point (*control_point);
2319 Editor::edit_control_point (ArdourCanvas::Item* item)
2321 ControlPoint* p = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"));
2324 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2328 ControlPointDialog d (p);
2331 if (d.run () != RESPONSE_ACCEPT) {
2335 p->line().modify_point_y (*p, d.get_y_fraction ());
2339 Editor::edit_notes (TimeAxisViewItem& tavi)
2341 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(&tavi);
2347 MidiRegionView::Selection const & s = mrv->selection();
2353 EditNoteDialog* d = new EditNoteDialog (&(*s.begin())->region_view(), s);
2357 d->signal_response().connect (sigc::bind (sigc::mem_fun (*this, &Editor::note_edit_done), d));
2361 Editor::note_edit_done (int r, EditNoteDialog* d)
2368 Editor::visible_order_range (int* low, int* high) const
2370 *low = TimeAxisView::max_order ();
2373 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
2375 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
2377 if (!rtv->hidden()) {
2379 if (*high < rtv->order()) {
2380 *high = rtv->order ();
2383 if (*low > rtv->order()) {
2384 *low = rtv->order ();
2391 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
2393 /* Either add to or set the set the region selection, unless
2394 this is an alignment click (control used)
2397 if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
2398 TimeAxisView* tv = &rv.get_time_axis_view();
2399 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
2401 if (rtv && rtv->is_track()) {
2402 speed = rtv->track()->speed();
2405 framepos_t where = get_preferred_edit_position();
2409 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
2411 align_region (rv.region(), SyncPoint, (framepos_t) (where * speed));
2413 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
2415 align_region (rv.region(), End, (framepos_t) (where * speed));
2419 align_region (rv.region(), Start, (framepos_t) (where * speed));
2426 Editor::collect_new_region_view (RegionView* rv)
2428 latest_regionviews.push_back (rv);
2432 Editor::collect_and_select_new_region_view (RegionView* rv)
2435 latest_regionviews.push_back (rv);
2439 Editor::cancel_selection ()
2441 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2442 (*i)->hide_selection ();
2445 selection->clear ();
2446 clicked_selection = 0;
2450 Editor::cancel_time_selection ()
2452 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2453 (*i)->hide_selection ();
2455 selection->time.clear ();
2456 clicked_selection = 0;
2460 Editor::point_trim (GdkEvent* event, framepos_t new_bound)
2462 RegionView* rv = clicked_regionview;
2464 /* Choose action dependant on which button was pressed */
2465 switch (event->button.button) {
2467 begin_reversible_command (_("start point trim"));
2469 if (selection->selected (rv)) {
2470 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
2471 i != selection->regions.by_layer().end(); ++i)
2473 if (!(*i)->region()->locked()) {
2474 (*i)->region()->clear_changes ();
2475 (*i)->region()->trim_front (new_bound);
2476 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2481 if (!rv->region()->locked()) {
2482 rv->region()->clear_changes ();
2483 rv->region()->trim_front (new_bound);
2484 _session->add_command(new StatefulDiffCommand (rv->region()));
2488 commit_reversible_command();
2492 begin_reversible_command (_("End point trim"));
2494 if (selection->selected (rv)) {
2496 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
2498 if (!(*i)->region()->locked()) {
2499 (*i)->region()->clear_changes();
2500 (*i)->region()->trim_end (new_bound);
2501 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2507 if (!rv->region()->locked()) {
2508 rv->region()->clear_changes ();
2509 rv->region()->trim_end (new_bound);
2510 _session->add_command (new StatefulDiffCommand (rv->region()));
2514 commit_reversible_command();
2523 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* /*event*/)
2528 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2529 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2533 Location* location = find_location_from_marker (marker, is_start);
2534 location->set_hidden (true, this);
2539 Editor::reposition_zoom_rect (framepos_t start, framepos_t end)
2541 double x1 = sample_to_pixel (start);
2542 double x2 = sample_to_pixel (end);
2543 double y2 = _full_canvas_height - 1.0;
2545 zoom_rect->set (ArdourCanvas::Rect (x1, 1.0, x2, y2));
2550 Editor::mouse_rename_region (ArdourCanvas::Item* /*item*/, GdkEvent* /*event*/)
2552 using namespace Gtkmm2ext;
2554 ArdourPrompter prompter (false);
2556 prompter.set_prompt (_("Name for region:"));
2557 prompter.set_initial_text (clicked_regionview->region()->name());
2558 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
2559 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
2560 prompter.show_all ();
2561 switch (prompter.run ()) {
2562 case Gtk::RESPONSE_ACCEPT:
2564 prompter.get_result(str);
2566 clicked_regionview->region()->set_name (str);
2575 Editor::mouse_brush_insert_region (RegionView* rv, framepos_t pos)
2577 /* no brushing without a useful snap setting */
2579 switch (_snap_mode) {
2581 return; /* can't work because it allows region to be placed anywhere */
2586 switch (_snap_type) {
2594 /* don't brush a copy over the original */
2596 if (pos == rv->region()->position()) {
2600 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
2602 if (rtv == 0 || !rtv->is_track()) {
2606 boost::shared_ptr<Playlist> playlist = rtv->playlist();
2607 double speed = rtv->track()->speed();
2609 playlist->clear_changes ();
2610 boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region(), true));
2611 playlist->add_region (new_region, (framepos_t) (pos * speed));
2612 _session->add_command (new StatefulDiffCommand (playlist));
2614 // playlist is frozen, so we have to update manually XXX this is disgusting
2616 playlist->RegionAdded (new_region); /* EMIT SIGNAL */
2620 Editor::track_height_step_timeout ()
2622 if (get_microseconds() - last_track_height_step_timestamp < 250000) {
2623 current_stepping_trackview = 0;
2630 Editor::add_region_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2632 assert (region_view);
2634 if (!region_view->region()->playlist()) {
2638 _region_motion_group->raise_to_top ();
2640 if (Config->get_edit_mode() == Splice) {
2641 _drags->add (new RegionSpliceDrag (this, item, region_view, selection->regions.by_layer()));
2643 _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), false, false));
2648 Editor::add_region_copy_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2650 assert (region_view);
2652 if (!region_view->region()->playlist()) {
2656 _region_motion_group->raise_to_top ();
2658 _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), false, true));
2662 Editor::add_region_brush_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2664 assert (region_view);
2666 if (!region_view->region()->playlist()) {
2670 if (Config->get_edit_mode() == Splice) {
2674 _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), true, false));
2676 begin_reversible_command (Operations::drag_region_brush);
2679 /** Start a grab where a time range is selected, track(s) are selected, and the
2680 * user clicks and drags a region with a modifier in order to create a new region containing
2681 * the section of the clicked region that lies within the time range.
2684 Editor::start_selection_grab (ArdourCanvas::Item* /*item*/, GdkEvent* event)
2686 if (clicked_regionview == 0) {
2690 /* lets try to create new Region for the selection */
2692 vector<boost::shared_ptr<Region> > new_regions;
2693 create_region_from_selection (new_regions);
2695 if (new_regions.empty()) {
2699 /* XXX fix me one day to use all new regions */
2701 boost::shared_ptr<Region> region (new_regions.front());
2703 /* add it to the current stream/playlist.
2705 tricky: the streamview for the track will add a new regionview. we will
2706 catch the signal it sends when it creates the regionview to
2707 set the regionview we want to then drag.
2710 latest_regionviews.clear();
2711 sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
2713 /* A selection grab currently creates two undo/redo operations, one for
2714 creating the new region and another for moving it.
2717 begin_reversible_command (Operations::selection_grab);
2719 boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
2721 playlist->clear_changes ();
2722 clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
2723 _session->add_command(new StatefulDiffCommand (playlist));
2725 commit_reversible_command ();
2729 if (latest_regionviews.empty()) {
2730 /* something went wrong */
2734 /* we need to deselect all other regionviews, and select this one
2735 i'm ignoring undo stuff, because the region creation will take care of it
2737 selection->set (latest_regionviews);
2739 _drags->set (new RegionMoveDrag (this, latest_regionviews.front()->get_canvas_group(), latest_regionviews.front(), latest_regionviews, false, false), event);
2745 if (_drags->active ()) {
2748 selection->clear ();
2753 Editor::set_internal_edit (bool yn)
2755 if (_internal_editing == yn) {
2759 _internal_editing = yn;
2762 pre_internal_mouse_mode = mouse_mode;
2763 pre_internal_snap_type = _snap_type;
2764 pre_internal_snap_mode = _snap_mode;
2766 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2767 (*i)->enter_internal_edit_mode ();
2770 set_snap_to (internal_snap_type);
2771 set_snap_mode (internal_snap_mode);
2775 internal_snap_mode = _snap_mode;
2776 internal_snap_type = _snap_type;
2778 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2779 (*i)->leave_internal_edit_mode ();
2782 if (mouse_mode == MouseDraw && pre_internal_mouse_mode != MouseDraw) {
2783 /* we were drawing .. flip back to something sensible */
2784 set_mouse_mode (pre_internal_mouse_mode);
2787 set_snap_to (pre_internal_snap_type);
2788 set_snap_mode (pre_internal_snap_mode);
2791 set_canvas_cursor ();
2794 /** Update _join_object_range_state which indicate whether we are over the top or bottom half of a region view,
2795 * used by the `join object/range' tool mode.
2798 Editor::update_join_object_range_location (double /*x*/, double y)
2800 /* XXX: actually, this decides based on whether the mouse is in the top
2801 or bottom half of a the waveform part RouteTimeAxisView;
2803 Note that entered_{track,regionview} is not always setup (e.g. if
2804 the mouse is over a TimeSelection), and to get a Region
2805 that we're over requires searching the playlist.
2808 if ( !get_smart_mode() ) {
2809 _join_object_range_state = JOIN_OBJECT_RANGE_NONE;
2813 if (mouse_mode == MouseObject) {
2814 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2815 } else if (mouse_mode == MouseRange) {
2816 _join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
2819 /* XXX: maybe we should make entered_track work in all cases, rather than resorting to this */
2820 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
2824 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
2829 rtv->canvas_display()->canvas_to_item (cx, cy);
2831 double const c = cy / (rtv->view()->child_height() - TimeAxisViewItem::NAME_HIGHLIGHT_SIZE);
2833 _join_object_range_state = c <= 0.5 ? JOIN_OBJECT_RANGE_RANGE : JOIN_OBJECT_RANGE_OBJECT;
2839 Editor::effective_mouse_mode () const
2841 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
2843 } else if (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE) {
2851 Editor::remove_midi_note (ArdourCanvas::Item* item, GdkEvent *)
2853 NoteBase* e = reinterpret_cast<NoteBase*> (item->get_data ("notebase"));
2856 e->region_view().delete_note (e->note ());
2860 Editor::set_canvas_cursor_for_region_view (double x, RegionView* rv)
2862 /* XXX: this check should not be necessary */
2869 ArdourCanvas::Group* g = rv->get_canvas_group ();
2870 ArdourCanvas::Group* p = g->parent ();
2872 /* Compute x in region view parent coordinates */
2874 p->canvas_to_item (x, dy);
2876 boost::optional<ArdourCanvas::Rect> item_bbox = g->bounding_box ();
2878 ArdourCanvas::Rect parent_bbox = g->item_to_parent (item_bbox.get ());
2880 /* Halfway across the region */
2881 double const h = (parent_bbox.x0 + parent_bbox.x1) / 2;
2883 Trimmable::CanTrim ct = rv->region()->can_trim ();
2885 if (ct & Trimmable::FrontTrimEarlier) {
2886 set_canvas_cursor (_cursors->left_side_trim);
2888 set_canvas_cursor (_cursors->left_side_trim_right_only);
2891 if (ct & Trimmable::EndTrimLater) {
2892 set_canvas_cursor (_cursors->right_side_trim);
2894 set_canvas_cursor (_cursors->right_side_trim_left_only);
2899 /** Obtain the pointer position in canvas coordinates */
2901 Editor::get_pointer_position (double& x, double& y) const
2904 _track_canvas->get_pointer (px, py);
2905 _track_canvas->window_to_canvas (px, py, x, y);