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"));
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 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
891 if (!internal_editing()) {
892 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::RangeSelectModifier)) {
893 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionExtend), event);
895 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
905 if (internal_editing()) {
906 /* trim notes if we're in internal edit mode and near the ends of the note */
907 NoteBase* cn = reinterpret_cast<NoteBase*>(item->get_data ("notebase"));
909 if (cn && cn->big_enough_to_trim() && cn->mouse_near_ends()) {
910 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
912 _drags->set (new NoteDrag (this, item), event);
918 if (internal_editing()) {
919 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
920 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
934 if (internal_editing()) {
935 NoteBase* cn = reinterpret_cast<NoteBase*> (item->get_data ("notebase"));
937 if (cn->mouse_near_ends()) {
938 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
940 _drags->set (new NoteDrag (this, item), event);
950 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)) &&
951 event->type == GDK_BUTTON_PRESS) {
953 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
955 } else if (event->type == GDK_BUTTON_PRESS) {
958 case FadeInHandleItem:
960 _drags->set (new FadeInDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), selection->regions), event, _cursors->fade_in);
964 case FadeOutHandleItem:
966 _drags->set (new FadeOutDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), selection->regions), event, _cursors->fade_out);
970 case StartCrossFadeItem:
971 case EndCrossFadeItem:
972 /* we might allow user to grab inside the fade to trim a region with preserve_fade_anchor. for not this is not fully implemented */
973 // if (!clicked_regionview->region()->locked()) {
974 // _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer(), true), event);
979 case FeatureLineItem:
981 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier)) {
982 remove_transient(item);
986 _drags->set (new FeatureLineDrag (this, item), event);
992 if (dynamic_cast<AutomationRegionView*> (clicked_regionview)) {
993 /* click on an automation region view; do nothing here and let the ARV's signal handler
999 if (internal_editing ()) {
1003 /* click on a normal region view */
1004 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
1005 add_region_copy_drag (item, event, clicked_regionview);
1006 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
1007 add_region_brush_drag (item, event, clicked_regionview);
1009 add_region_drag (item, event, clicked_regionview);
1013 // if (!internal_editing() && (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE && !selection->regions.empty())) {
1014 // _drags->add (new SelectionDrag (this, clicked_axisview->get_selection_rect (clicked_selection)->rect, SelectionDrag::SelectionMove));
1017 _drags->start_grab (event);
1021 case RegionViewNameHighlight:
1022 case LeftFrameHandle:
1023 case RightFrameHandle:
1024 if (!clicked_regionview->region()->locked()) {
1025 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1030 case RegionViewName:
1032 /* rename happens on edit clicks */
1033 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer()), event);
1038 case ControlPointItem:
1039 _drags->set (new ControlPointDrag (this, item), event);
1043 case AutomationLineItem:
1044 _drags->set (new LineDrag (this, item), event);
1049 if (internal_editing()) {
1050 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
1051 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
1055 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
1059 case AutomationTrackItem:
1061 TimeAxisView* parent = clicked_axisview->get_parent ();
1062 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (clicked_axisview);
1064 if (parent && dynamic_cast<MidiTimeAxisView*> (parent) && atv->show_regions ()) {
1066 RouteTimeAxisView* p = dynamic_cast<RouteTimeAxisView*> (parent);
1068 boost::shared_ptr<Playlist> pl = p->track()->playlist ();
1069 if (pl->n_regions() == 0) {
1070 /* Parent has no regions; create one so that we have somewhere to put automation */
1071 _drags->set (new RegionCreateDrag (this, item, parent), event);
1073 /* See if there's a region before the click that we can extend, and extend it if so */
1074 framepos_t const t = canvas_event_frame (event);
1075 boost::shared_ptr<Region> prev = pl->find_next_region (t, End, -1);
1077 _drags->set (new RegionCreateDrag (this, item, parent), event);
1079 prev->set_length (t - prev->position ());
1083 /* rubberband drag to select automation points */
1084 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
1091 if ( get_smart_mode() ) {
1092 /* we're in "smart" joined mode, and we've clicked on a Selection */
1093 double const y = event->button.y;
1094 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
1096 /* if we're over an automation track, start a drag of its data */
1097 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
1099 _drags->set (new AutomationRangeDrag (this, atv, selection->time), event, _cursors->up_down);
1102 /* if we're over a track and a region, and in the `object' part of a region,
1103 put a selection around the region and drag both
1105 /* RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1106 if (rtv && _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
1107 boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track> (rtv->route ());
1109 boost::shared_ptr<Playlist> pl = t->playlist ();
1112 boost::shared_ptr<Region> r = pl->top_region_at (canvas_event_frame (event));
1114 RegionView* rv = rtv->view()->find_view (r);
1115 clicked_selection = select_range (rv->region()->position(),
1116 rv->region()->last_frame()+1);
1117 _drags->add (new SelectionDrag (this, item, SelectionDrag::SelectionMove));
1118 list<RegionView*> rvs;
1120 _drags->add (new RegionMoveDrag (this, item, rv, rvs, false, false));
1121 _drags->start_grab (event);
1145 switch (item_type) {
1147 _drags->set (new LineDrag (this, item), event);
1150 case ControlPointItem:
1151 _drags->set (new ControlPointDrag (this, item), event);
1157 AudioRegionView* arv = dynamic_cast<AudioRegionView *> (clicked_regionview);
1159 _drags->set (new AutomationRangeDrag (this, arv, selection->time), event, _cursors->up_down);
1160 _drags->start_grab (event);
1166 case AutomationLineItem:
1167 _drags->set (new LineDrag (this, item), event);
1177 if (event->type == GDK_BUTTON_PRESS) {
1178 _drags->set (new MouseZoomDrag (this, item), event);
1185 if (internal_editing() && item_type == NoteItem) {
1186 /* drag notes if we're in internal edit mode */
1187 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
1189 } else if (clicked_regionview) {
1191 _drags->set (new TimeFXDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1197 _drags->set (new ScrubDrag (this, item), event);
1198 scrub_reversals = 0;
1199 scrub_reverse_distance = 0;
1200 last_scrub_x = event->button.x;
1201 scrubbing_direction = 0;
1202 set_canvas_cursor (_cursors->transparent);
1214 Editor::button_press_handler_2 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1216 Editing::MouseMode const eff = effective_mouse_mode ();
1219 switch (item_type) {
1221 if (internal_editing ()) {
1222 /* no region drags in internal edit mode */
1226 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
1227 add_region_copy_drag (item, event, clicked_regionview);
1229 add_region_drag (item, event, clicked_regionview);
1231 _drags->start_grab (event);
1234 case ControlPointItem:
1235 _drags->set (new ControlPointDrag (this, item), event);
1243 switch (item_type) {
1244 case RegionViewNameHighlight:
1245 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1249 case LeftFrameHandle:
1250 case RightFrameHandle:
1251 if (!internal_editing ()) {
1252 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1257 case RegionViewName:
1258 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer()), event);
1272 /* relax till release */
1278 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1279 temporal_zoom_to_frame (false, canvas_event_frame (event));
1281 temporal_zoom_to_frame (true, canvas_event_frame(event));
1294 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1296 if (event->type == GDK_2BUTTON_PRESS) {
1297 _drags->mark_double_click ();
1301 if (event->type != GDK_BUTTON_PRESS) {
1305 Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->_track_canvas_viewport->get_window();
1307 if (canvas_window) {
1308 Glib::RefPtr<const Gdk::Window> pointer_window;
1311 Gdk::ModifierType mask;
1313 pointer_window = canvas_window->get_pointer (x, y, mask);
1315 if (pointer_window == _track_canvas->get_window()) {
1316 _track_canvas->window_to_canvas (x, y, wx, wy);
1320 pre_press_cursor = current_canvas_cursor;
1322 _track_canvas->grab_focus();
1324 if (_session && _session->actively_recording()) {
1328 if (internal_editing()) {
1329 bool leave_internal_edit_mode = false;
1331 switch (item_type) {
1336 if (!dynamic_cast<MidiRegionView*> (clicked_regionview) && !dynamic_cast<AutomationRegionView*> (clicked_regionview)) {
1337 leave_internal_edit_mode = true;
1341 case PlayheadCursorItem:
1343 case TempoMarkerItem:
1344 case MeterMarkerItem:
1348 case RangeMarkerBarItem:
1349 case CdMarkerBarItem:
1350 case TransportMarkerBarItem:
1352 /* button press on these events never does anything to
1353 change the editing mode.
1361 if (leave_internal_edit_mode) {
1362 ActionManager::do_action ("MouseMode", "toggle-internal-edit");
1366 button_selection (item, event, item_type);
1368 if (!_drags->active () &&
1369 (Keyboard::is_delete_event (&event->button) ||
1370 Keyboard::is_context_menu_event (&event->button) ||
1371 Keyboard::is_edit_event (&event->button))) {
1373 /* handled by button release */
1377 //not rolling, range mode click + join_play_range : locate the PH here
1378 if ( !_drags->active () && !_session->transport_rolling() && ( effective_mouse_mode() == MouseRange ) && Config->get_always_play_range() ) {
1379 framepos_t where = canvas_event_frame (event);
1381 _session->request_locate (where, false);
1384 switch (event->button.button) {
1386 return button_press_handler_1 (item, event, item_type);
1390 return button_press_handler_2 (item, event, item_type);
1397 return button_press_dispatch (&event->button);
1406 Editor::button_press_dispatch (GdkEventButton* ev)
1408 /* this function is intended only for buttons 4 and above.
1411 Gtkmm2ext::MouseButton b (ev->state, ev->button);
1412 return button_bindings->activate (b, Gtkmm2ext::Bindings::Press);
1416 Editor::button_release_dispatch (GdkEventButton* ev)
1418 /* this function is intended only for buttons 4 and above.
1421 Gtkmm2ext::MouseButton b (ev->state, ev->button);
1422 return button_bindings->activate (b, Gtkmm2ext::Bindings::Release);
1426 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1428 framepos_t where = canvas_event_frame (event);
1429 AutomationTimeAxisView* atv = 0;
1431 if (pre_press_cursor) {
1432 set_canvas_cursor (pre_press_cursor);
1433 pre_press_cursor = 0;
1436 /* no action if we're recording */
1438 if (_session && _session->actively_recording()) {
1442 /* see if we're finishing a drag */
1444 bool were_dragging = false;
1445 if (_drags->active ()) {
1446 bool const r = _drags->end_grab (event);
1448 /* grab dragged, so do nothing else */
1452 were_dragging = true;
1455 update_region_layering_order_editor ();
1457 /* edit events get handled here */
1459 if (!_drags->active () && Keyboard::is_edit_event (&event->button)) {
1460 switch (item_type) {
1462 show_region_properties ();
1465 case TempoMarkerItem:
1466 edit_tempo_marker (item);
1469 case MeterMarkerItem:
1470 edit_meter_marker (item);
1473 case RegionViewName:
1474 if (clicked_regionview->name_active()) {
1475 return mouse_rename_region (item, event);
1479 case ControlPointItem:
1480 edit_control_point (item);
1489 /* context menu events get handled here */
1490 if (Keyboard::is_context_menu_event (&event->button)) {
1492 context_click_event = *event;
1494 if (!_drags->active ()) {
1496 /* no matter which button pops up the context menu, tell the menu
1497 widget to use button 1 to drive menu selection.
1500 switch (item_type) {
1502 case FadeInHandleItem:
1504 case FadeOutHandleItem:
1505 popup_fade_context_menu (1, event->button.time, item, item_type);
1508 case StartCrossFadeItem:
1509 popup_xfade_in_context_menu (1, event->button.time, item, item_type);
1512 case EndCrossFadeItem:
1513 popup_xfade_out_context_menu (1, event->button.time, item, item_type);
1517 popup_track_context_menu (1, event->button.time, item_type, false);
1521 case RegionViewNameHighlight:
1522 case LeftFrameHandle:
1523 case RightFrameHandle:
1524 case RegionViewName:
1525 popup_track_context_menu (1, event->button.time, item_type, false);
1529 popup_track_context_menu (1, event->button.time, item_type, true);
1532 case AutomationTrackItem:
1533 popup_track_context_menu (1, event->button.time, item_type, false);
1537 case RangeMarkerBarItem:
1538 case TransportMarkerBarItem:
1539 case CdMarkerBarItem:
1543 popup_ruler_menu (where, item_type);
1547 marker_context_menu (&event->button, item);
1550 case TempoMarkerItem:
1551 tempo_or_meter_marker_context_menu (&event->button, item);
1554 case MeterMarkerItem:
1555 tempo_or_meter_marker_context_menu (&event->button, item);
1558 case CrossfadeViewItem:
1559 popup_track_context_menu (1, event->button.time, item_type, false);
1562 case ControlPointItem:
1563 popup_control_point_context_menu (item, event);
1574 /* delete events get handled here */
1576 Editing::MouseMode const eff = effective_mouse_mode ();
1578 if (!_drags->active () && Keyboard::is_delete_event (&event->button)) {
1580 switch (item_type) {
1581 case TempoMarkerItem:
1582 remove_tempo_marker (item);
1585 case MeterMarkerItem:
1586 remove_meter_marker (item);
1590 remove_marker (*item, event);
1594 if (eff == MouseObject) {
1595 remove_clicked_region ();
1599 case ControlPointItem:
1600 remove_control_point (item);
1604 remove_midi_note (item, event);
1613 switch (event->button.button) {
1616 switch (item_type) {
1617 /* see comments in button_press_handler */
1618 case PlayheadCursorItem:
1621 case AutomationLineItem:
1622 case StartSelectionTrimItem:
1623 case EndSelectionTrimItem:
1627 if (!_dragging_playhead) {
1628 snap_to_with_modifier (where, event, 0, true);
1629 mouse_add_new_marker (where);
1633 case CdMarkerBarItem:
1634 if (!_dragging_playhead) {
1635 // if we get here then a dragged range wasn't done
1636 snap_to_with_modifier (where, event, 0, true);
1637 mouse_add_new_marker (where, true);
1642 if (!_dragging_playhead) {
1643 snap_to_with_modifier (where, event);
1644 mouse_add_new_tempo_event (where);
1649 if (!_dragging_playhead) {
1650 mouse_add_new_meter_event (pixel_to_sample (event->button.x));
1661 switch (item_type) {
1662 case AutomationTrackItem:
1663 atv = dynamic_cast<AutomationTimeAxisView*>(clicked_axisview);
1665 atv->add_automation_event (event, where, event->button.y);
1675 switch (item_type) {
1678 /* check that we didn't drag before releasing, since
1679 its really annoying to create new control
1680 points when doing this.
1682 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (clicked_regionview);
1683 if (!were_dragging && arv) {
1684 arv->add_gain_point_event (item, event);
1690 case AutomationTrackItem:
1691 dynamic_cast<AutomationTimeAxisView*>(clicked_axisview)->
1692 add_automation_event (event, where, event->button.y);
1701 set_canvas_cursor (current_canvas_cursor);
1702 if (scrubbing_direction == 0) {
1703 /* no drag, just a click */
1704 switch (item_type) {
1706 play_selected_region ();
1712 /* make sure we stop */
1713 _session->request_transport_speed (0.0);
1722 /* do any (de)selection operations that should occur on button release */
1723 button_selection (item, event, item_type);
1732 switch (item_type) {
1734 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1736 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier|Keyboard::SecondaryModifier))) {
1739 // Button2 click is unused
1754 // x_style_paste (where, 1.0);
1775 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1782 switch (item_type) {
1783 case ControlPointItem:
1784 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1785 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1786 cp->set_visible (true);
1790 at_y = cp->get_y ();
1791 cp->i2w (at_x, at_y);
1795 fraction = 1.0 - (cp->get_y() / cp->line().height());
1797 if (is_drawable() && !_drags->active ()) {
1798 set_canvas_cursor (_cursors->fader);
1801 _verbose_cursor->set (cp->line().get_verbose_cursor_string (fraction), at_x, at_y);
1802 _verbose_cursor->show ();
1807 if (mouse_mode == MouseGain) {
1808 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1810 line->set_outline_color (ARDOUR_UI::config()->get_canvasvar_EnteredGainLine());
1812 if (is_drawable()) {
1813 set_canvas_cursor (_cursors->fader);
1818 case AutomationLineItem:
1819 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1820 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1822 line->set_outline_color (ARDOUR_UI::config()->get_canvasvar_EnteredAutomationLine());
1824 if (is_drawable()) {
1825 set_canvas_cursor (_cursors->fader);
1830 case RegionViewNameHighlight:
1831 if (is_drawable() && effective_mouse_mode() == MouseObject && entered_regionview) {
1832 set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1833 _over_region_trim_target = true;
1837 case LeftFrameHandle:
1838 case RightFrameHandle:
1839 if (is_drawable() && effective_mouse_mode() == MouseObject && !internal_editing() && entered_regionview) {
1840 set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1844 case StartSelectionTrimItem:
1845 if (is_drawable()) {
1846 set_canvas_cursor (_cursors->left_side_trim);
1849 case EndSelectionTrimItem:
1850 if (is_drawable()) {
1851 set_canvas_cursor (_cursors->right_side_trim);
1855 case PlayheadCursorItem:
1856 if (is_drawable()) {
1857 switch (_edit_point) {
1859 set_canvas_cursor (_cursors->grabber_edit_point);
1862 set_canvas_cursor (_cursors->grabber);
1868 case RegionViewName:
1870 /* when the name is not an active item, the entire name highlight is for trimming */
1872 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1873 if (mouse_mode == MouseObject && is_drawable()) {
1874 set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1875 _over_region_trim_target = true;
1881 case AutomationTrackItem:
1882 if (is_drawable()) {
1883 Gdk::Cursor *cursor;
1884 switch (mouse_mode) {
1886 cursor = _cursors->selector;
1889 cursor = _cursors->zoom_in;
1892 cursor = _cursors->cross_hair;
1896 set_canvas_cursor (cursor);
1898 AutomationTimeAxisView* atv;
1899 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1900 clear_entered_track = false;
1901 set_entered_track (atv);
1907 case RangeMarkerBarItem:
1908 case TransportMarkerBarItem:
1909 case CdMarkerBarItem:
1912 if (is_drawable()) {
1913 set_canvas_cursor (_cursors->timebar);
1918 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1921 entered_marker = marker;
1922 marker->set_color_rgba (ARDOUR_UI::config()->get_canvasvar_EnteredMarker());
1924 case MeterMarkerItem:
1925 case TempoMarkerItem:
1926 if (is_drawable()) {
1927 set_canvas_cursor (_cursors->timebar);
1931 case FadeInHandleItem:
1932 if (mouse_mode == MouseObject && !internal_editing()) {
1933 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
1935 rect->set_fill_color (0xBBBBBBAA);
1937 set_canvas_cursor (_cursors->fade_in);
1941 case FadeOutHandleItem:
1942 if (mouse_mode == MouseObject && !internal_editing()) {
1943 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
1945 rect->set_fill_color (0xBBBBBBAA);
1947 set_canvas_cursor (_cursors->fade_out);
1950 case FeatureLineItem:
1952 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1953 line->set_outline_color (0xFF0000FF);
1958 if ( get_smart_mode() ) {
1959 set_canvas_cursor ();
1967 /* second pass to handle entered track status in a comprehensible way.
1970 switch (item_type) {
1972 case AutomationLineItem:
1973 case ControlPointItem:
1974 /* these do not affect the current entered track state */
1975 clear_entered_track = false;
1978 case AutomationTrackItem:
1979 /* handled above already */
1983 set_entered_track (0);
1991 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent*, ItemType item_type)
2001 switch (item_type) {
2002 case ControlPointItem:
2003 cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
2004 if (cp->line().the_list()->interpolation() != AutomationList::Discrete) {
2005 if (cp->line().npoints() > 1 && !cp->get_selected()) {
2006 cp->set_visible (false);
2010 if (is_drawable()) {
2011 set_canvas_cursor (current_canvas_cursor);
2014 _verbose_cursor->hide ();
2017 case RegionViewNameHighlight:
2018 case LeftFrameHandle:
2019 case RightFrameHandle:
2020 case StartSelectionTrimItem:
2021 case EndSelectionTrimItem:
2022 case PlayheadCursorItem:
2024 _over_region_trim_target = false;
2026 if (is_drawable()) {
2027 set_canvas_cursor (current_canvas_cursor);
2032 case AutomationLineItem:
2033 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
2035 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
2037 line->set_outline_color (al->get_line_color());
2040 if (is_drawable()) {
2041 set_canvas_cursor (current_canvas_cursor);
2045 case RegionViewName:
2046 /* see enter_handler() for notes */
2047 _over_region_trim_target = false;
2049 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
2050 if (is_drawable() && mouse_mode == MouseObject) {
2051 set_canvas_cursor (current_canvas_cursor);
2056 case RangeMarkerBarItem:
2057 case TransportMarkerBarItem:
2058 case CdMarkerBarItem:
2062 if (is_drawable()) {
2063 set_canvas_cursor (current_canvas_cursor);
2068 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2072 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
2073 location_flags_changed (loc, this);
2076 case MeterMarkerItem:
2077 case TempoMarkerItem:
2079 if (is_drawable()) {
2080 set_canvas_cursor (current_canvas_cursor);
2085 case FadeInHandleItem:
2086 case FadeOutHandleItem:
2087 rv = static_cast<RegionView*>(item->get_data ("regionview"));
2089 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
2091 rect->set_fill_color (rv->get_fill_color());
2094 set_canvas_cursor (current_canvas_cursor);
2097 case AutomationTrackItem:
2098 if (is_drawable()) {
2099 set_canvas_cursor (current_canvas_cursor);
2100 clear_entered_track = true;
2101 Glib::signal_idle().connect (sigc::mem_fun(*this, &Editor::left_automation_track));
2104 case FeatureLineItem:
2106 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
2107 line->set_outline_color (ARDOUR_UI::config()->get_canvasvar_ZeroLine());
2119 Editor::left_automation_track ()
2121 if (clear_entered_track) {
2122 set_entered_track (0);
2123 clear_entered_track = false;
2129 Editor::scrub (framepos_t frame, double current_x)
2133 if (scrubbing_direction == 0) {
2135 _session->request_locate (frame, false);
2136 _session->request_transport_speed (0.1);
2137 scrubbing_direction = 1;
2141 if (last_scrub_x > current_x) {
2143 /* pointer moved to the left */
2145 if (scrubbing_direction > 0) {
2147 /* we reversed direction to go backwards */
2150 scrub_reverse_distance += (int) (last_scrub_x - current_x);
2154 /* still moving to the left (backwards) */
2156 scrub_reversals = 0;
2157 scrub_reverse_distance = 0;
2159 delta = 0.01 * (last_scrub_x - current_x);
2160 _session->request_transport_speed_nonzero (_session->transport_speed() - delta);
2164 /* pointer moved to the right */
2166 if (scrubbing_direction < 0) {
2167 /* we reversed direction to go forward */
2170 scrub_reverse_distance += (int) (current_x - last_scrub_x);
2173 /* still moving to the right */
2175 scrub_reversals = 0;
2176 scrub_reverse_distance = 0;
2178 delta = 0.01 * (current_x - last_scrub_x);
2179 _session->request_transport_speed_nonzero (_session->transport_speed() + delta);
2183 /* if there have been more than 2 opposite motion moves detected, or one that moves
2184 back more than 10 pixels, reverse direction
2187 if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
2189 if (scrubbing_direction > 0) {
2190 /* was forwards, go backwards */
2191 _session->request_transport_speed (-0.1);
2192 scrubbing_direction = -1;
2194 /* was backwards, go forwards */
2195 _session->request_transport_speed (0.1);
2196 scrubbing_direction = 1;
2199 scrub_reverse_distance = 0;
2200 scrub_reversals = 0;
2204 last_scrub_x = current_x;
2208 Editor::motion_handler (ArdourCanvas::Item* /*item*/, GdkEvent* event, bool from_autoscroll)
2210 _last_motion_y = event->motion.y;
2212 if (event->motion.is_hint) {
2215 /* We call this so that MOTION_NOTIFY events continue to be
2216 delivered to the canvas. We need to do this because we set
2217 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
2218 the density of the events, at the expense of a round-trip
2219 to the server. Given that this will mostly occur on cases
2220 where DISPLAY = :0.0, and given the cost of what the motion
2221 event might do, its a good tradeoff.
2224 _track_canvas->get_pointer (x, y);
2227 if (current_stepping_trackview) {
2228 /* don't keep the persistent stepped trackview if the mouse moves */
2229 current_stepping_trackview = 0;
2230 step_timeout.disconnect ();
2233 if (_session && _session->actively_recording()) {
2234 /* Sorry. no dragging stuff around while we record */
2238 JoinObjectRangeState const old = _join_object_range_state;
2239 update_join_object_range_location (event->motion.x, event->motion.y);
2241 if (!_internal_editing && _join_object_range_state != old) {
2242 set_canvas_cursor ();
2245 if (!_internal_editing && _over_region_trim_target) {
2246 set_canvas_cursor_for_region_view (event->motion.x, entered_regionview);
2249 bool handled = false;
2250 if (_drags->active ()) {
2251 handled = _drags->motion_handler (event, from_autoscroll);
2258 track_canvas_motion (event);
2263 Editor::can_remove_control_point (ArdourCanvas::Item* item)
2265 ControlPoint* control_point;
2267 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2268 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2272 AutomationLine& line = control_point->line ();
2273 if (dynamic_cast<AudioRegionGainLine*> (&line)) {
2274 /* we shouldn't remove the first or last gain point in region gain lines */
2275 if (line.is_last_point(*control_point) || line.is_first_point(*control_point)) {
2284 Editor::remove_control_point (ArdourCanvas::Item* item)
2286 if (!can_remove_control_point (item)) {
2290 ControlPoint* control_point;
2292 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2293 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2297 control_point->line().remove_point (*control_point);
2301 Editor::edit_control_point (ArdourCanvas::Item* item)
2303 ControlPoint* p = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"));
2306 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2310 ControlPointDialog d (p);
2313 if (d.run () != RESPONSE_ACCEPT) {
2317 p->line().modify_point_y (*p, d.get_y_fraction ());
2321 Editor::edit_notes (TimeAxisViewItem& tavi)
2323 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(&tavi);
2329 MidiRegionView::Selection const & s = mrv->selection();
2335 EditNoteDialog* d = new EditNoteDialog (&(*s.begin())->region_view(), s);
2339 d->signal_response().connect (sigc::bind (sigc::mem_fun (*this, &Editor::note_edit_done), d));
2343 Editor::note_edit_done (int r, EditNoteDialog* d)
2350 Editor::visible_order_range (int* low, int* high) const
2352 *low = TimeAxisView::max_order ();
2355 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
2357 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
2359 if (!rtv->hidden()) {
2361 if (*high < rtv->order()) {
2362 *high = rtv->order ();
2365 if (*low > rtv->order()) {
2366 *low = rtv->order ();
2373 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
2375 /* Either add to or set the set the region selection, unless
2376 this is an alignment click (control used)
2379 if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
2380 TimeAxisView* tv = &rv.get_time_axis_view();
2381 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
2383 if (rtv && rtv->is_track()) {
2384 speed = rtv->track()->speed();
2387 framepos_t where = get_preferred_edit_position();
2391 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
2393 align_region (rv.region(), SyncPoint, (framepos_t) (where * speed));
2395 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
2397 align_region (rv.region(), End, (framepos_t) (where * speed));
2401 align_region (rv.region(), Start, (framepos_t) (where * speed));
2408 Editor::collect_new_region_view (RegionView* rv)
2410 latest_regionviews.push_back (rv);
2414 Editor::collect_and_select_new_region_view (RegionView* rv)
2417 latest_regionviews.push_back (rv);
2421 Editor::cancel_selection ()
2423 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2424 (*i)->hide_selection ();
2427 selection->clear ();
2428 clicked_selection = 0;
2432 Editor::cancel_time_selection ()
2434 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2435 (*i)->hide_selection ();
2437 selection->time.clear ();
2438 clicked_selection = 0;
2442 Editor::point_trim (GdkEvent* event, framepos_t new_bound)
2444 RegionView* rv = clicked_regionview;
2446 /* Choose action dependant on which button was pressed */
2447 switch (event->button.button) {
2449 begin_reversible_command (_("start point trim"));
2451 if (selection->selected (rv)) {
2452 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
2453 i != selection->regions.by_layer().end(); ++i)
2455 if (!(*i)->region()->locked()) {
2456 (*i)->region()->clear_changes ();
2457 (*i)->region()->trim_front (new_bound);
2458 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2463 if (!rv->region()->locked()) {
2464 rv->region()->clear_changes ();
2465 rv->region()->trim_front (new_bound);
2466 _session->add_command(new StatefulDiffCommand (rv->region()));
2470 commit_reversible_command();
2474 begin_reversible_command (_("End point trim"));
2476 if (selection->selected (rv)) {
2478 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
2480 if (!(*i)->region()->locked()) {
2481 (*i)->region()->clear_changes();
2482 (*i)->region()->trim_end (new_bound);
2483 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2489 if (!rv->region()->locked()) {
2490 rv->region()->clear_changes ();
2491 rv->region()->trim_end (new_bound);
2492 _session->add_command (new StatefulDiffCommand (rv->region()));
2496 commit_reversible_command();
2505 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* /*event*/)
2510 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2511 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2515 Location* location = find_location_from_marker (marker, is_start);
2516 location->set_hidden (true, this);
2521 Editor::reposition_zoom_rect (framepos_t start, framepos_t end)
2523 double x1 = sample_to_pixel (start);
2524 double x2 = sample_to_pixel (end);
2525 double y2 = _full_canvas_height - 1.0;
2527 zoom_rect->set (ArdourCanvas::Rect (x1, 1.0, x2, y2));
2532 Editor::mouse_rename_region (ArdourCanvas::Item* /*item*/, GdkEvent* /*event*/)
2534 using namespace Gtkmm2ext;
2536 ArdourPrompter prompter (false);
2538 prompter.set_prompt (_("Name for region:"));
2539 prompter.set_initial_text (clicked_regionview->region()->name());
2540 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
2541 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
2542 prompter.show_all ();
2543 switch (prompter.run ()) {
2544 case Gtk::RESPONSE_ACCEPT:
2546 prompter.get_result(str);
2548 clicked_regionview->region()->set_name (str);
2557 Editor::mouse_brush_insert_region (RegionView* rv, framepos_t pos)
2559 /* no brushing without a useful snap setting */
2561 switch (_snap_mode) {
2563 return; /* can't work because it allows region to be placed anywhere */
2568 switch (_snap_type) {
2576 /* don't brush a copy over the original */
2578 if (pos == rv->region()->position()) {
2582 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
2584 if (rtv == 0 || !rtv->is_track()) {
2588 boost::shared_ptr<Playlist> playlist = rtv->playlist();
2589 double speed = rtv->track()->speed();
2591 playlist->clear_changes ();
2592 boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region(), true));
2593 playlist->add_region (new_region, (framepos_t) (pos * speed));
2594 _session->add_command (new StatefulDiffCommand (playlist));
2596 // playlist is frozen, so we have to update manually XXX this is disgusting
2598 playlist->RegionAdded (new_region); /* EMIT SIGNAL */
2602 Editor::track_height_step_timeout ()
2604 if (get_microseconds() - last_track_height_step_timestamp < 250000) {
2605 current_stepping_trackview = 0;
2612 Editor::add_region_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2614 assert (region_view);
2616 if (!region_view->region()->playlist()) {
2620 _region_motion_group->raise_to_top ();
2622 if (Config->get_edit_mode() == Splice) {
2623 _drags->add (new RegionSpliceDrag (this, item, region_view, selection->regions.by_layer()));
2625 _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), false, false));
2630 Editor::add_region_copy_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 _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), false, true));
2644 Editor::add_region_brush_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2646 assert (region_view);
2648 if (!region_view->region()->playlist()) {
2652 if (Config->get_edit_mode() == Splice) {
2656 _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), true, false));
2658 begin_reversible_command (Operations::drag_region_brush);
2661 /** Start a grab where a time range is selected, track(s) are selected, and the
2662 * user clicks and drags a region with a modifier in order to create a new region containing
2663 * the section of the clicked region that lies within the time range.
2666 Editor::start_selection_grab (ArdourCanvas::Item* /*item*/, GdkEvent* event)
2668 if (clicked_regionview == 0) {
2672 /* lets try to create new Region for the selection */
2674 vector<boost::shared_ptr<Region> > new_regions;
2675 create_region_from_selection (new_regions);
2677 if (new_regions.empty()) {
2681 /* XXX fix me one day to use all new regions */
2683 boost::shared_ptr<Region> region (new_regions.front());
2685 /* add it to the current stream/playlist.
2687 tricky: the streamview for the track will add a new regionview. we will
2688 catch the signal it sends when it creates the regionview to
2689 set the regionview we want to then drag.
2692 latest_regionviews.clear();
2693 sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
2695 /* A selection grab currently creates two undo/redo operations, one for
2696 creating the new region and another for moving it.
2699 begin_reversible_command (Operations::selection_grab);
2701 boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
2703 playlist->clear_changes ();
2704 clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
2705 _session->add_command(new StatefulDiffCommand (playlist));
2707 commit_reversible_command ();
2711 if (latest_regionviews.empty()) {
2712 /* something went wrong */
2716 /* we need to deselect all other regionviews, and select this one
2717 i'm ignoring undo stuff, because the region creation will take care of it
2719 selection->set (latest_regionviews);
2721 _drags->set (new RegionMoveDrag (this, latest_regionviews.front()->get_canvas_group(), latest_regionviews.front(), latest_regionviews, false, false), event);
2727 if (_drags->active ()) {
2730 selection->clear ();
2735 Editor::set_internal_edit (bool yn)
2737 if (_internal_editing == yn) {
2741 _internal_editing = yn;
2744 pre_internal_mouse_mode = mouse_mode;
2745 pre_internal_snap_type = _snap_type;
2746 pre_internal_snap_mode = _snap_mode;
2748 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2749 (*i)->enter_internal_edit_mode ();
2752 set_snap_to (internal_snap_type);
2753 set_snap_mode (internal_snap_mode);
2757 internal_snap_mode = _snap_mode;
2758 internal_snap_type = _snap_type;
2760 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2761 (*i)->leave_internal_edit_mode ();
2764 if (mouse_mode == MouseDraw && pre_internal_mouse_mode != MouseDraw) {
2765 /* we were drawing .. flip back to something sensible */
2766 set_mouse_mode (pre_internal_mouse_mode);
2769 set_snap_to (pre_internal_snap_type);
2770 set_snap_mode (pre_internal_snap_mode);
2773 set_canvas_cursor ();
2776 /** Update _join_object_range_state which indicate whether we are over the top or bottom half of a region view,
2777 * used by the `join object/range' tool mode.
2780 Editor::update_join_object_range_location (double /*x*/, double y)
2782 /* XXX: actually, this decides based on whether the mouse is in the top
2783 or bottom half of a the waveform part RouteTimeAxisView;
2785 Note that entered_{track,regionview} is not always setup (e.g. if
2786 the mouse is over a TimeSelection), and to get a Region
2787 that we're over requires searching the playlist.
2790 if ( !get_smart_mode() ) {
2791 _join_object_range_state = JOIN_OBJECT_RANGE_NONE;
2795 if (mouse_mode == MouseObject) {
2796 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2797 } else if (mouse_mode == MouseRange) {
2798 _join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
2801 /* XXX: maybe we should make entered_track work in all cases, rather than resorting to this */
2802 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
2806 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
2811 rtv->canvas_display()->canvas_to_item (cx, cy);
2813 double const c = cy / (rtv->view()->child_height() - TimeAxisViewItem::NAME_HIGHLIGHT_SIZE);
2815 _join_object_range_state = c <= 0.5 ? JOIN_OBJECT_RANGE_RANGE : JOIN_OBJECT_RANGE_OBJECT;
2821 Editor::effective_mouse_mode () const
2823 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
2825 } else if (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE) {
2833 Editor::remove_midi_note (ArdourCanvas::Item* item, GdkEvent *)
2835 NoteBase* e = reinterpret_cast<NoteBase*> (item->get_data ("notebase"));
2838 e->region_view().delete_note (e->note ());
2842 Editor::set_canvas_cursor_for_region_view (double x, RegionView* rv)
2844 /* XXX: this check should not be necessary */
2851 ArdourCanvas::Group* g = rv->get_canvas_group ();
2852 ArdourCanvas::Group* p = g->parent ();
2854 /* Compute x in region view parent coordinates */
2856 p->canvas_to_item (x, dy);
2858 boost::optional<ArdourCanvas::Rect> item_bbox = g->bounding_box ();
2860 ArdourCanvas::Rect parent_bbox = g->item_to_parent (item_bbox.get ());
2862 /* Halfway across the region */
2863 double const h = (parent_bbox.x0 + parent_bbox.x1) / 2;
2865 Trimmable::CanTrim ct = rv->region()->can_trim ();
2867 if (ct & Trimmable::FrontTrimEarlier) {
2868 set_canvas_cursor (_cursors->left_side_trim);
2870 set_canvas_cursor (_cursors->left_side_trim_right_only);
2873 if (ct & Trimmable::EndTrimLater) {
2874 set_canvas_cursor (_cursors->right_side_trim);
2876 set_canvas_cursor (_cursors->right_side_trim_left_only);
2881 /** Obtain the pointer position in canvas coordinates */
2883 Editor::get_pointer_position (double& x, double& y) const
2886 _track_canvas->get_pointer (px, py);
2887 _track_canvas->window_to_canvas (px, py, x, y);