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_OBJECT)) {
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"));
726 if (m->tempo().movable ()) {
728 new TempoMarkerDrag (
731 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
741 case MeterMarkerItem:
743 MeterMarker* m = reinterpret_cast<MeterMarker*> (item->get_data ("marker"));
745 if (m->meter().movable ()) {
747 new MeterMarkerDrag (
750 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
761 _drags->set (new VideoTimeLineDrag (this, item), event);
768 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
769 _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
775 case RangeMarkerBarItem:
776 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
777 _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
779 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateRangeMarker), event);
784 case CdMarkerBarItem:
785 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
786 _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
788 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateCDMarker), event);
793 case TransportMarkerBarItem:
794 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
795 _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
797 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateTransportMarker), event);
806 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
807 /* special case: allow trim of range selections in joined object mode;
808 in theory eff should equal MouseRange in this case, but it doesn't
809 because entering the range selection canvas item results in entered_regionview
810 being set to 0, so update_join_object_range_location acts as if we aren't
813 if (item_type == StartSelectionTrimItem) {
814 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
815 } else if (item_type == EndSelectionTrimItem) {
816 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
820 Editing::MouseMode eff = effective_mouse_mode ();
822 /* special case: allow drag of region fade in/out in object mode with join object/range enabled */
823 if (item_type == FadeInHandleItem || item_type == FadeOutHandleItem) {
827 /* there is no Range mode when in internal edit mode */
828 if (eff == MouseRange && internal_editing()) {
835 case StartSelectionTrimItem:
836 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
839 case EndSelectionTrimItem:
840 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
844 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
845 start_selection_grab (item, event);
847 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
848 /* grab selection for moving */
849 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionMove), event);
851 double const y = event->button.y;
852 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
854 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
855 if ( get_smart_mode() && atv) {
856 /* smart "join" mode: drag automation */
857 _drags->set (new AutomationRangeDrag (this, atv, selection->time), event, _cursors->up_down);
859 /* this was debated, but decided the more common action was to
860 make a new selection */
861 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
868 if (internal_editing()) {
869 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
870 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
874 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::RangeSelectModifier)) {
875 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionExtend), event);
877 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
883 case RegionViewNameHighlight:
884 if (!clicked_regionview->region()->locked()) {
885 RegionSelection s = get_equivalent_regions (selection->regions, Properties::select.property_id);
886 _drags->set (new TrimDrag (this, item, clicked_regionview, s.by_layer()), event);
892 if (!internal_editing()) {
893 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::RangeSelectModifier)) {
894 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionExtend), event);
896 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
906 if (internal_editing()) {
907 /* trim notes if we're in internal edit mode and near the ends of the note */
908 NoteBase* cn = reinterpret_cast<NoteBase*>(item->get_data ("notebase"));
910 if (cn && cn->big_enough_to_trim() && cn->mouse_near_ends()) {
911 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
913 _drags->set (new NoteDrag (this, item), event);
919 if (internal_editing()) {
920 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
921 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
935 if (internal_editing()) {
936 NoteBase* cn = reinterpret_cast<NoteBase*> (item->get_data ("notebase"));
938 if (cn->mouse_near_ends()) {
939 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
941 _drags->set (new NoteDrag (this, item), event);
951 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)) &&
952 event->type == GDK_BUTTON_PRESS) {
954 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
956 } else if (event->type == GDK_BUTTON_PRESS) {
959 case FadeInHandleItem:
961 RegionSelection s = get_equivalent_regions (selection->regions, Properties::select.property_id);
962 _drags->set (new FadeInDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), s), event, _cursors->fade_in);
966 case FadeOutHandleItem:
968 RegionSelection s = get_equivalent_regions (selection->regions, Properties::select.property_id);
969 _drags->set (new FadeOutDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), s), event, _cursors->fade_out);
973 case StartCrossFadeItem:
974 case EndCrossFadeItem:
975 /* we might allow user to grab inside the fade to trim a region with preserve_fade_anchor. for not this is not fully implemented */
976 // if (!clicked_regionview->region()->locked()) {
977 // RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
978 // _drags->set (new TrimDrag (this, item, clicked_regionview, s.by_layer(), true), event);
983 case FeatureLineItem:
985 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier)) {
986 remove_transient(item);
990 _drags->set (new FeatureLineDrag (this, item), event);
996 if (dynamic_cast<AutomationRegionView*> (clicked_regionview)) {
997 /* click on an automation region view; do nothing here and let the ARV's signal handler
1003 if (internal_editing ()) {
1007 /* click on a normal region view */
1008 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
1009 add_region_copy_drag (item, event, clicked_regionview);
1010 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
1011 add_region_brush_drag (item, event, clicked_regionview);
1013 add_region_drag (item, event, clicked_regionview);
1017 // if (!internal_editing() && (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE && !selection->regions.empty())) {
1018 // _drags->add (new SelectionDrag (this, clicked_axisview->get_selection_rect (clicked_selection)->rect, SelectionDrag::SelectionMove));
1021 _drags->start_grab (event);
1025 case RegionViewNameHighlight:
1026 case LeftFrameHandle:
1027 case RightFrameHandle:
1028 if (!clicked_regionview->region()->locked()) {
1029 RegionSelection s = get_equivalent_regions (selection->regions, Properties::select.property_id);
1030 _drags->set (new TrimDrag (this, item, clicked_regionview, s.by_layer()), event);
1035 case RegionViewName:
1037 /* rename happens on edit clicks */
1038 RegionSelection s = get_equivalent_regions (selection->regions, Properties::select.property_id);
1039 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, s.by_layer()), event);
1044 case ControlPointItem:
1045 _drags->set (new ControlPointDrag (this, item), event);
1049 case AutomationLineItem:
1050 _drags->set (new LineDrag (this, item), event);
1055 if (internal_editing()) {
1056 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
1057 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
1061 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
1065 case AutomationTrackItem:
1067 TimeAxisView* parent = clicked_axisview->get_parent ();
1068 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (clicked_axisview);
1070 if (parent && dynamic_cast<MidiTimeAxisView*> (parent) && atv->show_regions ()) {
1072 RouteTimeAxisView* p = dynamic_cast<RouteTimeAxisView*> (parent);
1074 boost::shared_ptr<Playlist> pl = p->track()->playlist ();
1075 if (pl->n_regions() == 0) {
1076 /* Parent has no regions; create one so that we have somewhere to put automation */
1077 _drags->set (new RegionCreateDrag (this, item, parent), event);
1079 /* See if there's a region before the click that we can extend, and extend it if so */
1080 framepos_t const t = canvas_event_frame (event);
1081 boost::shared_ptr<Region> prev = pl->find_next_region (t, End, -1);
1083 _drags->set (new RegionCreateDrag (this, item, parent), event);
1085 prev->set_length (t - prev->position ());
1089 /* rubberband drag to select automation points */
1090 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
1097 if ( get_smart_mode() ) {
1098 /* we're in "smart" joined mode, and we've clicked on a Selection */
1099 double const y = event->button.y;
1100 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
1102 /* if we're over an automation track, start a drag of its data */
1103 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
1105 _drags->set (new AutomationRangeDrag (this, atv, selection->time), event, _cursors->up_down);
1108 /* if we're over a track and a region, and in the `object' part of a region,
1109 put a selection around the region and drag both
1111 /* RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1112 if (rtv && _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
1113 boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track> (rtv->route ());
1115 boost::shared_ptr<Playlist> pl = t->playlist ();
1118 boost::shared_ptr<Region> r = pl->top_region_at (canvas_event_frame (event));
1120 RegionView* rv = rtv->view()->find_view (r);
1121 clicked_selection = select_range (rv->region()->position(),
1122 rv->region()->last_frame()+1);
1123 _drags->add (new SelectionDrag (this, item, SelectionDrag::SelectionMove));
1124 list<RegionView*> rvs;
1126 _drags->add (new RegionMoveDrag (this, item, rv, rvs, false, false));
1127 _drags->start_grab (event);
1151 switch (item_type) {
1153 _drags->set (new LineDrag (this, item), event);
1156 case ControlPointItem:
1157 _drags->set (new ControlPointDrag (this, item), event);
1163 AudioRegionView* arv = dynamic_cast<AudioRegionView *> (clicked_regionview);
1165 _drags->set (new AutomationRangeDrag (this, arv, selection->time), event, _cursors->up_down);
1166 _drags->start_grab (event);
1172 case AutomationLineItem:
1173 _drags->set (new LineDrag (this, item), event);
1183 if (event->type == GDK_BUTTON_PRESS) {
1184 _drags->set (new MouseZoomDrag (this, item), event);
1191 if (internal_editing() && item_type == NoteItem) {
1192 /* drag notes if we're in internal edit mode */
1193 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
1195 } else if (clicked_regionview) {
1197 _drags->set (new TimeFXDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1203 _drags->set (new ScrubDrag (this, item), event);
1204 scrub_reversals = 0;
1205 scrub_reverse_distance = 0;
1206 last_scrub_x = event->button.x;
1207 scrubbing_direction = 0;
1208 set_canvas_cursor (_cursors->transparent);
1220 Editor::button_press_handler_2 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1222 Editing::MouseMode const eff = effective_mouse_mode ();
1225 switch (item_type) {
1227 if (internal_editing ()) {
1228 /* no region drags in internal edit mode */
1232 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
1233 add_region_copy_drag (item, event, clicked_regionview);
1235 add_region_drag (item, event, clicked_regionview);
1237 _drags->start_grab (event);
1240 case ControlPointItem:
1241 _drags->set (new ControlPointDrag (this, item), event);
1249 switch (item_type) {
1250 case RegionViewNameHighlight:
1251 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1255 case LeftFrameHandle:
1256 case RightFrameHandle:
1257 if (!internal_editing ()) {
1258 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1263 case RegionViewName:
1264 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer()), event);
1278 /* relax till release */
1284 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1285 temporal_zoom_to_frame (false, canvas_event_frame (event));
1287 temporal_zoom_to_frame (true, canvas_event_frame(event));
1300 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1302 if (event->type != GDK_BUTTON_PRESS) {
1306 Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->_track_canvas_viewport->get_window();
1308 if (canvas_window) {
1309 Glib::RefPtr<const Gdk::Window> pointer_window;
1312 Gdk::ModifierType mask;
1314 pointer_window = canvas_window->get_pointer (x, y, mask);
1316 if (pointer_window == _track_canvas->get_window()) {
1317 _track_canvas->window_to_canvas (x, y, wx, wy);
1321 pre_press_cursor = current_canvas_cursor;
1323 _track_canvas->grab_focus();
1325 if (_session && _session->actively_recording()) {
1329 if (internal_editing()) {
1330 bool leave_internal_edit_mode = false;
1332 switch (item_type) {
1337 if (!dynamic_cast<MidiRegionView*> (clicked_regionview) && !dynamic_cast<AutomationRegionView*> (clicked_regionview)) {
1338 leave_internal_edit_mode = true;
1342 case PlayheadCursorItem:
1344 case TempoMarkerItem:
1345 case MeterMarkerItem:
1349 case RangeMarkerBarItem:
1350 case CdMarkerBarItem:
1351 case TransportMarkerBarItem:
1353 /* button press on these events never does anything to
1354 change the editing mode.
1362 if (leave_internal_edit_mode) {
1363 ActionManager::do_action ("MouseMode", "toggle-internal-edit");
1367 button_selection (item, event, item_type);
1369 if (!_drags->active () &&
1370 (Keyboard::is_delete_event (&event->button) ||
1371 Keyboard::is_context_menu_event (&event->button) ||
1372 Keyboard::is_edit_event (&event->button))) {
1374 /* handled by button release */
1378 //not rolling, range mode click + join_play_range : locate the PH here
1379 if ( !_drags->active () && !_session->transport_rolling() && ( effective_mouse_mode() == MouseRange ) && Config->get_always_play_range() ) {
1380 framepos_t where = canvas_event_frame (event, 0, 0);
1382 _session->request_locate (where, false);
1385 switch (event->button.button) {
1387 return button_press_handler_1 (item, event, item_type);
1391 return button_press_handler_2 (item, event, item_type);
1398 return button_press_dispatch (&event->button);
1407 Editor::button_press_dispatch (GdkEventButton* ev)
1409 /* this function is intended only for buttons 4 and above.
1412 Gtkmm2ext::MouseButton b (ev->state, ev->button);
1413 return button_bindings->activate (b, Gtkmm2ext::Bindings::Press);
1417 Editor::button_release_dispatch (GdkEventButton* ev)
1419 /* this function is intended only for buttons 4 and above.
1422 Gtkmm2ext::MouseButton b (ev->state, ev->button);
1423 return button_bindings->activate (b, Gtkmm2ext::Bindings::Release);
1427 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1429 framepos_t where = canvas_event_frame (event, 0, 0);
1430 AutomationTimeAxisView* atv = 0;
1432 if (pre_press_cursor) {
1433 set_canvas_cursor (pre_press_cursor);
1434 pre_press_cursor = 0;
1437 /* no action if we're recording */
1439 if (_session && _session->actively_recording()) {
1443 /* see if we're finishing a drag */
1445 bool were_dragging = false;
1446 if (_drags->active ()) {
1447 bool const r = _drags->end_grab (event);
1449 /* grab dragged, so do nothing else */
1453 were_dragging = true;
1456 update_region_layering_order_editor ();
1458 /* edit events get handled here */
1460 if (!_drags->active () && Keyboard::is_edit_event (&event->button)) {
1461 switch (item_type) {
1463 show_region_properties ();
1466 case TempoMarkerItem:
1467 edit_tempo_marker (item);
1470 case MeterMarkerItem:
1471 edit_meter_marker (item);
1474 case RegionViewName:
1475 if (clicked_regionview->name_active()) {
1476 return mouse_rename_region (item, event);
1480 case ControlPointItem:
1481 edit_control_point (item);
1490 /* context menu events get handled here */
1491 if (Keyboard::is_context_menu_event (&event->button)) {
1493 context_click_event = *event;
1495 if (!_drags->active ()) {
1497 /* no matter which button pops up the context menu, tell the menu
1498 widget to use button 1 to drive menu selection.
1501 switch (item_type) {
1503 case FadeInHandleItem:
1505 case FadeOutHandleItem:
1506 popup_fade_context_menu (1, event->button.time, item, item_type);
1509 case StartCrossFadeItem:
1510 popup_xfade_in_context_menu (1, event->button.time, item, item_type);
1513 case EndCrossFadeItem:
1514 popup_xfade_out_context_menu (1, event->button.time, item, item_type);
1518 popup_track_context_menu (1, event->button.time, item_type, false);
1522 case RegionViewNameHighlight:
1523 case LeftFrameHandle:
1524 case RightFrameHandle:
1525 case RegionViewName:
1526 popup_track_context_menu (1, event->button.time, item_type, false);
1530 popup_track_context_menu (1, event->button.time, item_type, true);
1533 case AutomationTrackItem:
1534 popup_track_context_menu (1, event->button.time, item_type, false);
1538 case RangeMarkerBarItem:
1539 case TransportMarkerBarItem:
1540 case CdMarkerBarItem:
1544 popup_ruler_menu (where, item_type);
1548 marker_context_menu (&event->button, item);
1551 case TempoMarkerItem:
1552 tempo_or_meter_marker_context_menu (&event->button, item);
1555 case MeterMarkerItem:
1556 tempo_or_meter_marker_context_menu (&event->button, item);
1559 case CrossfadeViewItem:
1560 popup_track_context_menu (1, event->button.time, item_type, false);
1563 case ControlPointItem:
1564 popup_control_point_context_menu (item, event);
1575 /* delete events get handled here */
1577 Editing::MouseMode const eff = effective_mouse_mode ();
1579 if (!_drags->active () && Keyboard::is_delete_event (&event->button)) {
1581 switch (item_type) {
1582 case TempoMarkerItem:
1583 remove_tempo_marker (item);
1586 case MeterMarkerItem:
1587 remove_meter_marker (item);
1591 remove_marker (*item, event);
1595 if (eff == MouseObject) {
1596 remove_clicked_region ();
1600 case ControlPointItem:
1601 remove_control_point (item);
1605 remove_midi_note (item, event);
1614 switch (event->button.button) {
1617 switch (item_type) {
1618 /* see comments in button_press_handler */
1619 case PlayheadCursorItem:
1622 case AutomationLineItem:
1623 case StartSelectionTrimItem:
1624 case EndSelectionTrimItem:
1628 if (!_dragging_playhead) {
1629 snap_to_with_modifier (where, event, 0, true);
1630 mouse_add_new_marker (where);
1634 case CdMarkerBarItem:
1635 if (!_dragging_playhead) {
1636 // if we get here then a dragged range wasn't done
1637 snap_to_with_modifier (where, event, 0, true);
1638 mouse_add_new_marker (where, true);
1643 if (!_dragging_playhead) {
1644 snap_to_with_modifier (where, event);
1645 mouse_add_new_tempo_event (where);
1650 if (!_dragging_playhead) {
1651 mouse_add_new_meter_event (pixel_to_sample (event->button.x));
1662 switch (item_type) {
1663 case AutomationTrackItem:
1664 atv = dynamic_cast<AutomationTimeAxisView*>(clicked_axisview);
1666 atv->add_automation_event (event, where, event->button.y);
1676 switch (item_type) {
1679 /* check that we didn't drag before releasing, since
1680 its really annoying to create new control
1681 points when doing this.
1683 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (clicked_regionview);
1684 if (!were_dragging && arv) {
1685 arv->add_gain_point_event (item, event);
1691 case AutomationTrackItem:
1692 dynamic_cast<AutomationTimeAxisView*>(clicked_axisview)->
1693 add_automation_event (event, where, event->button.y);
1702 set_canvas_cursor (current_canvas_cursor);
1703 if (scrubbing_direction == 0) {
1704 /* no drag, just a click */
1705 switch (item_type) {
1707 play_selected_region ();
1713 /* make sure we stop */
1714 _session->request_transport_speed (0.0);
1723 /* do any (de)selection operations that should occur on button release */
1724 button_selection (item, event, item_type);
1733 switch (item_type) {
1735 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1737 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier|Keyboard::SecondaryModifier))) {
1740 // Button2 click is unused
1755 // x_style_paste (where, 1.0);
1776 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1783 switch (item_type) {
1784 case ControlPointItem:
1785 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1786 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1787 cp->set_visible (true);
1791 at_y = cp->get_y ();
1792 cp->i2w (at_x, at_y);
1796 fraction = 1.0 - (cp->get_y() / cp->line().height());
1798 if (is_drawable() && !_drags->active ()) {
1799 set_canvas_cursor (_cursors->fader);
1802 _verbose_cursor->set (cp->line().get_verbose_cursor_string (fraction), at_x, at_y);
1803 _verbose_cursor->show ();
1808 if (mouse_mode == MouseGain) {
1809 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1811 line->set_outline_color (ARDOUR_UI::config()->get_canvasvar_EnteredGainLine());
1813 if (is_drawable()) {
1814 set_canvas_cursor (_cursors->fader);
1819 case AutomationLineItem:
1820 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1821 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1823 line->set_outline_color (ARDOUR_UI::config()->get_canvasvar_EnteredAutomationLine());
1825 if (is_drawable()) {
1826 set_canvas_cursor (_cursors->fader);
1831 case RegionViewNameHighlight:
1832 if (is_drawable() && effective_mouse_mode() == MouseObject && entered_regionview) {
1833 set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1834 _over_region_trim_target = true;
1838 case LeftFrameHandle:
1839 case RightFrameHandle:
1840 if (is_drawable() && effective_mouse_mode() == MouseObject && !internal_editing() && entered_regionview) {
1841 set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1845 case StartSelectionTrimItem:
1846 if (is_drawable()) {
1847 set_canvas_cursor (_cursors->left_side_trim);
1850 case EndSelectionTrimItem:
1851 if (is_drawable()) {
1852 set_canvas_cursor (_cursors->right_side_trim);
1856 case PlayheadCursorItem:
1857 if (is_drawable()) {
1858 switch (_edit_point) {
1860 set_canvas_cursor (_cursors->grabber_edit_point);
1863 set_canvas_cursor (_cursors->grabber);
1869 case RegionViewName:
1871 /* when the name is not an active item, the entire name highlight is for trimming */
1873 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1874 if (mouse_mode == MouseObject && is_drawable()) {
1875 set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1876 _over_region_trim_target = true;
1882 case AutomationTrackItem:
1883 if (is_drawable()) {
1884 Gdk::Cursor *cursor;
1885 switch (mouse_mode) {
1887 cursor = _cursors->selector;
1890 cursor = _cursors->zoom_in;
1893 cursor = _cursors->cross_hair;
1897 set_canvas_cursor (cursor);
1899 AutomationTimeAxisView* atv;
1900 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1901 clear_entered_track = false;
1902 set_entered_track (atv);
1908 case RangeMarkerBarItem:
1909 case TransportMarkerBarItem:
1910 case CdMarkerBarItem:
1913 if (is_drawable()) {
1914 set_canvas_cursor (_cursors->timebar);
1919 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1922 entered_marker = marker;
1923 marker->set_color_rgba (ARDOUR_UI::config()->get_canvasvar_EnteredMarker());
1925 case MeterMarkerItem:
1926 case TempoMarkerItem:
1927 if (is_drawable()) {
1928 set_canvas_cursor (_cursors->timebar);
1932 case FadeInHandleItem:
1933 if (mouse_mode == MouseObject && !internal_editing()) {
1934 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
1936 rect->set_fill_color (0xBBBBBBAA);
1938 set_canvas_cursor (_cursors->fade_in);
1942 case FadeOutHandleItem:
1943 if (mouse_mode == MouseObject && !internal_editing()) {
1944 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
1946 rect->set_fill_color (0xBBBBBBAA);
1948 set_canvas_cursor (_cursors->fade_out);
1951 case FeatureLineItem:
1953 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1954 line->set_outline_color (0xFF0000FF);
1959 if ( get_smart_mode() ) {
1960 set_canvas_cursor ();
1968 /* second pass to handle entered track status in a comprehensible way.
1971 switch (item_type) {
1973 case AutomationLineItem:
1974 case ControlPointItem:
1975 /* these do not affect the current entered track state */
1976 clear_entered_track = false;
1979 case AutomationTrackItem:
1980 /* handled above already */
1984 set_entered_track (0);
1992 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent*, ItemType item_type)
2002 switch (item_type) {
2003 case ControlPointItem:
2004 cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
2005 if (cp->line().the_list()->interpolation() != AutomationList::Discrete) {
2006 if (cp->line().npoints() > 1 && !cp->get_selected()) {
2007 cp->set_visible (false);
2011 if (is_drawable()) {
2012 set_canvas_cursor (current_canvas_cursor);
2015 _verbose_cursor->hide ();
2018 case RegionViewNameHighlight:
2019 case LeftFrameHandle:
2020 case RightFrameHandle:
2021 case StartSelectionTrimItem:
2022 case EndSelectionTrimItem:
2023 case PlayheadCursorItem:
2025 _over_region_trim_target = false;
2027 if (is_drawable()) {
2028 set_canvas_cursor (current_canvas_cursor);
2033 case AutomationLineItem:
2034 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
2036 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
2038 line->set_outline_color (al->get_line_color());
2041 if (is_drawable()) {
2042 set_canvas_cursor (current_canvas_cursor);
2046 case RegionViewName:
2047 /* see enter_handler() for notes */
2048 _over_region_trim_target = false;
2050 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
2051 if (is_drawable() && mouse_mode == MouseObject) {
2052 set_canvas_cursor (current_canvas_cursor);
2057 case RangeMarkerBarItem:
2058 case TransportMarkerBarItem:
2059 case CdMarkerBarItem:
2063 if (is_drawable()) {
2064 set_canvas_cursor (current_canvas_cursor);
2069 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2073 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
2074 location_flags_changed (loc, this);
2077 case MeterMarkerItem:
2078 case TempoMarkerItem:
2080 if (is_drawable()) {
2081 set_canvas_cursor (current_canvas_cursor);
2086 case FadeInHandleItem:
2087 case FadeOutHandleItem:
2088 rv = static_cast<RegionView*>(item->get_data ("regionview"));
2090 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
2092 rect->set_fill_color (rv->get_fill_color());
2095 set_canvas_cursor (current_canvas_cursor);
2098 case AutomationTrackItem:
2099 if (is_drawable()) {
2100 set_canvas_cursor (current_canvas_cursor);
2101 clear_entered_track = true;
2102 Glib::signal_idle().connect (sigc::mem_fun(*this, &Editor::left_automation_track));
2105 case FeatureLineItem:
2107 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
2108 line->set_outline_color (ARDOUR_UI::config()->get_canvasvar_ZeroLine());
2120 Editor::left_automation_track ()
2122 if (clear_entered_track) {
2123 set_entered_track (0);
2124 clear_entered_track = false;
2130 Editor::scrub (framepos_t frame, double current_x)
2134 if (scrubbing_direction == 0) {
2136 _session->request_locate (frame, false);
2137 _session->request_transport_speed (0.1);
2138 scrubbing_direction = 1;
2142 if (last_scrub_x > current_x) {
2144 /* pointer moved to the left */
2146 if (scrubbing_direction > 0) {
2148 /* we reversed direction to go backwards */
2151 scrub_reverse_distance += (int) (last_scrub_x - current_x);
2155 /* still moving to the left (backwards) */
2157 scrub_reversals = 0;
2158 scrub_reverse_distance = 0;
2160 delta = 0.01 * (last_scrub_x - current_x);
2161 _session->request_transport_speed_nonzero (_session->transport_speed() - delta);
2165 /* pointer moved to the right */
2167 if (scrubbing_direction < 0) {
2168 /* we reversed direction to go forward */
2171 scrub_reverse_distance += (int) (current_x - last_scrub_x);
2174 /* still moving to the right */
2176 scrub_reversals = 0;
2177 scrub_reverse_distance = 0;
2179 delta = 0.01 * (current_x - last_scrub_x);
2180 _session->request_transport_speed_nonzero (_session->transport_speed() + delta);
2184 /* if there have been more than 2 opposite motion moves detected, or one that moves
2185 back more than 10 pixels, reverse direction
2188 if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
2190 if (scrubbing_direction > 0) {
2191 /* was forwards, go backwards */
2192 _session->request_transport_speed (-0.1);
2193 scrubbing_direction = -1;
2195 /* was backwards, go forwards */
2196 _session->request_transport_speed (0.1);
2197 scrubbing_direction = 1;
2200 scrub_reverse_distance = 0;
2201 scrub_reversals = 0;
2205 last_scrub_x = current_x;
2209 Editor::motion_handler (ArdourCanvas::Item* /*item*/, GdkEvent* event, bool from_autoscroll)
2211 _last_motion_y = event->motion.y;
2213 if (event->motion.is_hint) {
2216 /* We call this so that MOTION_NOTIFY events continue to be
2217 delivered to the canvas. We need to do this because we set
2218 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
2219 the density of the events, at the expense of a round-trip
2220 to the server. Given that this will mostly occur on cases
2221 where DISPLAY = :0.0, and given the cost of what the motion
2222 event might do, its a good tradeoff.
2225 _track_canvas->get_pointer (x, y);
2228 if (current_stepping_trackview) {
2229 /* don't keep the persistent stepped trackview if the mouse moves */
2230 current_stepping_trackview = 0;
2231 step_timeout.disconnect ();
2234 if (_session && _session->actively_recording()) {
2235 /* Sorry. no dragging stuff around while we record */
2239 JoinObjectRangeState const old = _join_object_range_state;
2240 update_join_object_range_location (event->motion.x, event->motion.y);
2242 if (!_internal_editing && _join_object_range_state != old) {
2243 set_canvas_cursor ();
2246 if (!_internal_editing && _over_region_trim_target) {
2247 set_canvas_cursor_for_region_view (event->motion.x, entered_regionview);
2250 bool handled = false;
2251 if (_drags->active ()) {
2252 handled = _drags->motion_handler (event, from_autoscroll);
2259 track_canvas_motion (event);
2264 Editor::can_remove_control_point (ArdourCanvas::Item* item)
2266 ControlPoint* control_point;
2268 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2269 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2273 AutomationLine& line = control_point->line ();
2274 if (dynamic_cast<AudioRegionGainLine*> (&line)) {
2275 /* we shouldn't remove the first or last gain point in region gain lines */
2276 if (line.is_last_point(*control_point) || line.is_first_point(*control_point)) {
2285 Editor::remove_control_point (ArdourCanvas::Item* item)
2287 if (!can_remove_control_point (item)) {
2291 ControlPoint* control_point;
2293 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2294 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2298 control_point->line().remove_point (*control_point);
2302 Editor::edit_control_point (ArdourCanvas::Item* item)
2304 ControlPoint* p = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"));
2307 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2311 ControlPointDialog d (p);
2314 if (d.run () != RESPONSE_ACCEPT) {
2318 p->line().modify_point_y (*p, d.get_y_fraction ());
2322 Editor::edit_notes (TimeAxisViewItem& tavi)
2324 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(&tavi);
2330 MidiRegionView::Selection const & s = mrv->selection();
2336 EditNoteDialog* d = new EditNoteDialog (&(*s.begin())->region_view(), s);
2340 d->signal_response().connect (sigc::bind (sigc::mem_fun (*this, &Editor::note_edit_done), d));
2344 Editor::note_edit_done (int r, EditNoteDialog* d)
2351 Editor::visible_order_range (int* low, int* high) const
2353 *low = TimeAxisView::max_order ();
2356 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
2358 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
2360 if (!rtv->hidden()) {
2362 if (*high < rtv->order()) {
2363 *high = rtv->order ();
2366 if (*low > rtv->order()) {
2367 *low = rtv->order ();
2374 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
2376 /* Either add to or set the set the region selection, unless
2377 this is an alignment click (control used)
2380 if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
2381 TimeAxisView* tv = &rv.get_time_axis_view();
2382 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
2384 if (rtv && rtv->is_track()) {
2385 speed = rtv->track()->speed();
2388 framepos_t where = get_preferred_edit_position();
2392 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
2394 align_region (rv.region(), SyncPoint, (framepos_t) (where * speed));
2396 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
2398 align_region (rv.region(), End, (framepos_t) (where * speed));
2402 align_region (rv.region(), Start, (framepos_t) (where * speed));
2409 Editor::collect_new_region_view (RegionView* rv)
2411 latest_regionviews.push_back (rv);
2415 Editor::collect_and_select_new_region_view (RegionView* rv)
2418 latest_regionviews.push_back (rv);
2422 Editor::cancel_selection ()
2424 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2425 (*i)->hide_selection ();
2428 selection->clear ();
2429 clicked_selection = 0;
2433 Editor::cancel_time_selection ()
2435 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2436 (*i)->hide_selection ();
2438 selection->time.clear ();
2439 clicked_selection = 0;
2443 Editor::point_trim (GdkEvent* event, framepos_t new_bound)
2445 RegionView* rv = clicked_regionview;
2447 /* Choose action dependant on which button was pressed */
2448 switch (event->button.button) {
2450 begin_reversible_command (_("start point trim"));
2452 if (selection->selected (rv)) {
2453 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
2454 i != selection->regions.by_layer().end(); ++i)
2456 if (!(*i)->region()->locked()) {
2457 (*i)->region()->clear_changes ();
2458 (*i)->region()->trim_front (new_bound);
2459 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2464 if (!rv->region()->locked()) {
2465 rv->region()->clear_changes ();
2466 rv->region()->trim_front (new_bound);
2467 _session->add_command(new StatefulDiffCommand (rv->region()));
2471 commit_reversible_command();
2475 begin_reversible_command (_("End point trim"));
2477 if (selection->selected (rv)) {
2479 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
2481 if (!(*i)->region()->locked()) {
2482 (*i)->region()->clear_changes();
2483 (*i)->region()->trim_end (new_bound);
2484 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2490 if (!rv->region()->locked()) {
2491 rv->region()->clear_changes ();
2492 rv->region()->trim_end (new_bound);
2493 _session->add_command (new StatefulDiffCommand (rv->region()));
2497 commit_reversible_command();
2506 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* /*event*/)
2511 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2512 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2516 Location* location = find_location_from_marker (marker, is_start);
2517 location->set_hidden (true, this);
2522 Editor::reposition_zoom_rect (framepos_t start, framepos_t end)
2524 double x1 = sample_to_pixel (start);
2525 double x2 = sample_to_pixel (end);
2526 double y2 = _full_canvas_height - 1.0;
2528 zoom_rect->set (ArdourCanvas::Rect (x1, 1.0, x2, y2));
2533 Editor::mouse_rename_region (ArdourCanvas::Item* /*item*/, GdkEvent* /*event*/)
2535 using namespace Gtkmm2ext;
2537 ArdourPrompter prompter (false);
2539 prompter.set_prompt (_("Name for region:"));
2540 prompter.set_initial_text (clicked_regionview->region()->name());
2541 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
2542 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
2543 prompter.show_all ();
2544 switch (prompter.run ()) {
2545 case Gtk::RESPONSE_ACCEPT:
2547 prompter.get_result(str);
2549 clicked_regionview->region()->set_name (str);
2558 Editor::mouse_brush_insert_region (RegionView* rv, framepos_t pos)
2560 /* no brushing without a useful snap setting */
2562 switch (_snap_mode) {
2564 return; /* can't work because it allows region to be placed anywhere */
2569 switch (_snap_type) {
2577 /* don't brush a copy over the original */
2579 if (pos == rv->region()->position()) {
2583 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
2585 if (rtv == 0 || !rtv->is_track()) {
2589 boost::shared_ptr<Playlist> playlist = rtv->playlist();
2590 double speed = rtv->track()->speed();
2592 playlist->clear_changes ();
2593 boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region(), true));
2594 playlist->add_region (new_region, (framepos_t) (pos * speed));
2595 _session->add_command (new StatefulDiffCommand (playlist));
2597 // playlist is frozen, so we have to update manually XXX this is disgusting
2599 playlist->RegionAdded (new_region); /* EMIT SIGNAL */
2603 Editor::track_height_step_timeout ()
2605 if (get_microseconds() - last_track_height_step_timestamp < 250000) {
2606 current_stepping_trackview = 0;
2613 Editor::add_region_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2615 assert (region_view);
2617 if (!region_view->region()->playlist()) {
2621 _region_motion_group->raise_to_top ();
2623 if (Config->get_edit_mode() == Splice) {
2624 _drags->add (new RegionSpliceDrag (this, item, region_view, selection->regions.by_layer()));
2626 RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::select.property_id);
2627 _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), false, false));
2632 Editor::add_region_copy_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2634 assert (region_view);
2636 if (!region_view->region()->playlist()) {
2640 _region_motion_group->raise_to_top ();
2642 RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::select.property_id);
2643 _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), false, true));
2647 Editor::add_region_brush_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2649 assert (region_view);
2651 if (!region_view->region()->playlist()) {
2655 if (Config->get_edit_mode() == Splice) {
2659 RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::select.property_id);
2660 _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), true, false));
2662 begin_reversible_command (Operations::drag_region_brush);
2665 /** Start a grab where a time range is selected, track(s) are selected, and the
2666 * user clicks and drags a region with a modifier in order to create a new region containing
2667 * the section of the clicked region that lies within the time range.
2670 Editor::start_selection_grab (ArdourCanvas::Item* /*item*/, GdkEvent* event)
2672 if (clicked_regionview == 0) {
2676 /* lets try to create new Region for the selection */
2678 vector<boost::shared_ptr<Region> > new_regions;
2679 create_region_from_selection (new_regions);
2681 if (new_regions.empty()) {
2685 /* XXX fix me one day to use all new regions */
2687 boost::shared_ptr<Region> region (new_regions.front());
2689 /* add it to the current stream/playlist.
2691 tricky: the streamview for the track will add a new regionview. we will
2692 catch the signal it sends when it creates the regionview to
2693 set the regionview we want to then drag.
2696 latest_regionviews.clear();
2697 sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
2699 /* A selection grab currently creates two undo/redo operations, one for
2700 creating the new region and another for moving it.
2703 begin_reversible_command (Operations::selection_grab);
2705 boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
2707 playlist->clear_changes ();
2708 clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
2709 _session->add_command(new StatefulDiffCommand (playlist));
2711 commit_reversible_command ();
2715 if (latest_regionviews.empty()) {
2716 /* something went wrong */
2720 /* we need to deselect all other regionviews, and select this one
2721 i'm ignoring undo stuff, because the region creation will take care of it
2723 selection->set (latest_regionviews);
2725 _drags->set (new RegionMoveDrag (this, latest_regionviews.front()->get_canvas_group(), latest_regionviews.front(), latest_regionviews, false, false), event);
2731 if (_drags->active ()) {
2734 selection->clear ();
2739 Editor::set_internal_edit (bool yn)
2741 if (_internal_editing == yn) {
2745 _internal_editing = yn;
2748 pre_internal_mouse_mode = mouse_mode;
2749 pre_internal_snap_type = _snap_type;
2750 pre_internal_snap_mode = _snap_mode;
2752 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2753 (*i)->enter_internal_edit_mode ();
2756 set_snap_to (internal_snap_type);
2757 set_snap_mode (internal_snap_mode);
2761 internal_snap_mode = _snap_mode;
2762 internal_snap_type = _snap_type;
2764 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2765 (*i)->leave_internal_edit_mode ();
2768 if (mouse_mode == MouseDraw && pre_internal_mouse_mode != MouseDraw) {
2769 /* we were drawing .. flip back to something sensible */
2770 set_mouse_mode (pre_internal_mouse_mode);
2773 set_snap_to (pre_internal_snap_type);
2774 set_snap_mode (pre_internal_snap_mode);
2777 set_canvas_cursor ();
2780 /** Update _join_object_range_state which indicate whether we are over the top or bottom half of a region view,
2781 * used by the `join object/range' tool mode.
2784 Editor::update_join_object_range_location (double /*x*/, double y)
2786 /* XXX: actually, this decides based on whether the mouse is in the top
2787 or bottom half of a the waveform part RouteTimeAxisView;
2789 Note that entered_{track,regionview} is not always setup (e.g. if
2790 the mouse is over a TimeSelection), and to get a Region
2791 that we're over requires searching the playlist.
2794 if ( !get_smart_mode() ) {
2795 _join_object_range_state = JOIN_OBJECT_RANGE_NONE;
2799 if (mouse_mode == MouseObject) {
2800 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2801 } else if (mouse_mode == MouseRange) {
2802 _join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
2805 /* XXX: maybe we should make entered_track work in all cases, rather than resorting to this */
2806 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
2810 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
2815 rtv->canvas_display()->canvas_to_item (cx, cy);
2817 double const c = cy / (rtv->view()->child_height() - TimeAxisViewItem::NAME_HIGHLIGHT_SIZE);
2819 _join_object_range_state = c <= 0.5 ? JOIN_OBJECT_RANGE_RANGE : JOIN_OBJECT_RANGE_OBJECT;
2825 Editor::effective_mouse_mode () const
2827 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
2829 } else if (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE) {
2837 Editor::remove_midi_note (ArdourCanvas::Item* item, GdkEvent *)
2839 NoteBase* e = reinterpret_cast<NoteBase*> (item->get_data ("notebase"));
2842 e->region_view().delete_note (e->note ());
2846 Editor::set_canvas_cursor_for_region_view (double x, RegionView* rv)
2848 /* XXX: this check should not be necessary */
2855 ArdourCanvas::Group* g = rv->get_canvas_group ();
2856 ArdourCanvas::Group* p = g->parent ();
2858 /* Compute x in region view parent coordinates */
2860 p->canvas_to_item (x, dy);
2862 boost::optional<ArdourCanvas::Rect> item_bbox = g->bounding_box ();
2864 ArdourCanvas::Rect parent_bbox = g->item_to_parent (item_bbox.get ());
2866 /* Halfway across the region */
2867 double const h = (parent_bbox.x0 + parent_bbox.x1) / 2;
2869 Trimmable::CanTrim ct = rv->region()->can_trim ();
2871 if (ct & Trimmable::FrontTrimEarlier) {
2872 set_canvas_cursor (_cursors->left_side_trim);
2874 set_canvas_cursor (_cursors->left_side_trim_right_only);
2877 if (ct & Trimmable::EndTrimLater) {
2878 set_canvas_cursor (_cursors->right_side_trim);
2880 set_canvas_cursor (_cursors->right_side_trim_left_only);
2885 /** Obtain the pointer position in canvas coordinates */
2887 Editor::get_pointer_position (double& x, double& y) const
2890 _track_canvas->get_pointer (px, py);
2891 _track_canvas->window_to_canvas (px, py, x, y);