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) {
1302 if (event->type == GDK_2BUTTON_PRESS) {
1303 gdk_pointer_ungrab (GDK_CURRENT_TIME);
1304 return button_double_click_handler (item, event, item_type);
1309 Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->_track_canvas_viewport->get_window();
1311 if (canvas_window) {
1312 Glib::RefPtr<const Gdk::Window> pointer_window;
1315 Gdk::ModifierType mask;
1317 pointer_window = canvas_window->get_pointer (x, y, mask);
1319 if (pointer_window == _track_canvas->get_window()) {
1320 _track_canvas->window_to_canvas (x, y, wx, wy);
1324 pre_press_cursor = current_canvas_cursor;
1326 _track_canvas->grab_focus();
1328 if (_session && _session->actively_recording()) {
1332 if (internal_editing()) {
1333 bool leave_internal_edit_mode = false;
1335 switch (item_type) {
1340 if (!dynamic_cast<MidiRegionView*> (clicked_regionview) && !dynamic_cast<AutomationRegionView*> (clicked_regionview)) {
1341 leave_internal_edit_mode = true;
1345 case PlayheadCursorItem:
1347 case TempoMarkerItem:
1348 case MeterMarkerItem:
1352 case RangeMarkerBarItem:
1353 case CdMarkerBarItem:
1354 case TransportMarkerBarItem:
1356 /* button press on these events never does anything to
1357 change the editing mode.
1365 if (leave_internal_edit_mode) {
1366 ActionManager::do_action ("MouseMode", "toggle-internal-edit");
1370 button_selection (item, event, item_type);
1372 if (!_drags->active () &&
1373 (Keyboard::is_delete_event (&event->button) ||
1374 Keyboard::is_context_menu_event (&event->button) ||
1375 Keyboard::is_edit_event (&event->button))) {
1377 /* handled by button release */
1381 //not rolling, range mode click + join_play_range : locate the PH here
1382 if ( !_drags->active () && !_session->transport_rolling() && ( effective_mouse_mode() == MouseRange ) && Config->get_always_play_range() ) {
1383 framepos_t where = canvas_event_frame (event);
1385 _session->request_locate (where, false);
1388 switch (event->button.button) {
1390 return button_press_handler_1 (item, event, item_type);
1394 return button_press_handler_2 (item, event, item_type);
1401 return button_press_dispatch (&event->button);
1410 Editor::button_press_dispatch (GdkEventButton* ev)
1412 /* this function is intended only for buttons 4 and above.
1415 Gtkmm2ext::MouseButton b (ev->state, ev->button);
1416 return button_bindings->activate (b, Gtkmm2ext::Bindings::Press);
1420 Editor::button_release_dispatch (GdkEventButton* ev)
1422 /* this function is intended only for buttons 4 and above.
1425 Gtkmm2ext::MouseButton b (ev->state, ev->button);
1426 return button_bindings->activate (b, Gtkmm2ext::Bindings::Release);
1430 Editor::button_double_click_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type) {
1432 if (event->button.button != 1) {
1436 switch (item_type) {
1439 rv = clicked_regionview;
1440 rv->show_region_editor ();
1443 case PlayheadCursorItem:
1446 case RangeMarkerBarItem:
1447 case CdMarkerBarItem:
1449 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1452 rename_marker (marker);
1454 case TempoMarkerItem:
1455 edit_tempo_marker (item);
1457 case MeterMarkerItem:
1458 edit_meter_marker (item);
1463 case TransportMarkerBarItem:
1476 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1478 framepos_t where = canvas_event_frame (event);
1479 AutomationTimeAxisView* atv = 0;
1481 if (pre_press_cursor) {
1482 set_canvas_cursor (pre_press_cursor);
1483 pre_press_cursor = 0;
1486 /* no action if we're recording */
1488 if (_session && _session->actively_recording()) {
1492 /* see if we're finishing a drag */
1494 bool were_dragging = false;
1495 if (_drags->active ()) {
1496 bool const r = _drags->end_grab (event);
1498 /* grab dragged, so do nothing else */
1502 were_dragging = true;
1505 update_region_layering_order_editor ();
1507 /* edit events get handled here */
1509 if (!_drags->active () && Keyboard::is_edit_event (&event->button)) {
1510 switch (item_type) {
1512 show_region_properties ();
1515 case TempoMarkerItem:
1516 edit_tempo_marker (item);
1519 case MeterMarkerItem:
1520 edit_meter_marker (item);
1523 case RegionViewName:
1524 if (clicked_regionview->name_active()) {
1525 return mouse_rename_region (item, event);
1529 case ControlPointItem:
1530 edit_control_point (item);
1539 /* context menu events get handled here */
1540 if (Keyboard::is_context_menu_event (&event->button)) {
1542 context_click_event = *event;
1544 if (!_drags->active ()) {
1546 /* no matter which button pops up the context menu, tell the menu
1547 widget to use button 1 to drive menu selection.
1550 switch (item_type) {
1552 case FadeInHandleItem:
1554 case FadeOutHandleItem:
1555 popup_fade_context_menu (1, event->button.time, item, item_type);
1558 case StartCrossFadeItem:
1559 popup_xfade_in_context_menu (1, event->button.time, item, item_type);
1562 case EndCrossFadeItem:
1563 popup_xfade_out_context_menu (1, event->button.time, item, item_type);
1567 popup_track_context_menu (1, event->button.time, item_type, false);
1571 case RegionViewNameHighlight:
1572 case LeftFrameHandle:
1573 case RightFrameHandle:
1574 case RegionViewName:
1575 popup_track_context_menu (1, event->button.time, item_type, false);
1579 popup_track_context_menu (1, event->button.time, item_type, true);
1582 case AutomationTrackItem:
1583 popup_track_context_menu (1, event->button.time, item_type, false);
1587 case RangeMarkerBarItem:
1588 case TransportMarkerBarItem:
1589 case CdMarkerBarItem:
1593 popup_ruler_menu (where, item_type);
1597 marker_context_menu (&event->button, item);
1600 case TempoMarkerItem:
1601 tempo_or_meter_marker_context_menu (&event->button, item);
1604 case MeterMarkerItem:
1605 tempo_or_meter_marker_context_menu (&event->button, item);
1608 case CrossfadeViewItem:
1609 popup_track_context_menu (1, event->button.time, item_type, false);
1612 case ControlPointItem:
1613 popup_control_point_context_menu (item, event);
1624 /* delete events get handled here */
1626 Editing::MouseMode const eff = effective_mouse_mode ();
1628 if (!_drags->active () && Keyboard::is_delete_event (&event->button)) {
1630 switch (item_type) {
1631 case TempoMarkerItem:
1632 remove_tempo_marker (item);
1635 case MeterMarkerItem:
1636 remove_meter_marker (item);
1640 remove_marker (*item, event);
1644 if (eff == MouseObject) {
1645 remove_clicked_region ();
1649 case ControlPointItem:
1650 remove_control_point (item);
1654 remove_midi_note (item, event);
1663 switch (event->button.button) {
1666 switch (item_type) {
1667 /* see comments in button_press_handler */
1668 case PlayheadCursorItem:
1671 case AutomationLineItem:
1672 case StartSelectionTrimItem:
1673 case EndSelectionTrimItem:
1677 if (!_dragging_playhead) {
1678 snap_to_with_modifier (where, event, 0, true);
1679 mouse_add_new_marker (where);
1683 case CdMarkerBarItem:
1684 if (!_dragging_playhead) {
1685 // if we get here then a dragged range wasn't done
1686 snap_to_with_modifier (where, event, 0, true);
1687 mouse_add_new_marker (where, true);
1692 if (!_dragging_playhead) {
1693 snap_to_with_modifier (where, event);
1694 mouse_add_new_tempo_event (where);
1699 if (!_dragging_playhead) {
1700 mouse_add_new_meter_event (pixel_to_sample (event->button.x));
1711 switch (item_type) {
1712 case AutomationTrackItem:
1713 atv = dynamic_cast<AutomationTimeAxisView*>(clicked_axisview);
1715 atv->add_automation_event (event, where, event->button.y);
1725 switch (item_type) {
1728 /* check that we didn't drag before releasing, since
1729 its really annoying to create new control
1730 points when doing this.
1732 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (clicked_regionview);
1733 if (!were_dragging && arv) {
1734 arv->add_gain_point_event (item, event);
1740 case AutomationTrackItem:
1741 dynamic_cast<AutomationTimeAxisView*>(clicked_axisview)->
1742 add_automation_event (event, where, event->button.y);
1751 set_canvas_cursor (current_canvas_cursor);
1752 if (scrubbing_direction == 0) {
1753 /* no drag, just a click */
1754 switch (item_type) {
1756 play_selected_region ();
1762 /* make sure we stop */
1763 _session->request_transport_speed (0.0);
1772 /* do any (de)selection operations that should occur on button release */
1773 button_selection (item, event, item_type);
1782 switch (item_type) {
1784 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1786 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier|Keyboard::SecondaryModifier))) {
1789 // Button2 click is unused
1804 // x_style_paste (where, 1.0);
1825 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1832 switch (item_type) {
1833 case ControlPointItem:
1834 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1835 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1840 at_y = cp->get_y ();
1841 cp->i2w (at_x, at_y);
1845 fraction = 1.0 - (cp->get_y() / cp->line().height());
1847 if (is_drawable() && !_drags->active ()) {
1848 set_canvas_cursor (_cursors->fader);
1851 _verbose_cursor->set (cp->line().get_verbose_cursor_string (fraction), at_x, at_y);
1852 _verbose_cursor->show ();
1857 if (mouse_mode == MouseGain) {
1858 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1860 line->set_outline_color (ARDOUR_UI::config()->get_canvasvar_EnteredGainLine());
1862 if (is_drawable()) {
1863 set_canvas_cursor (_cursors->fader);
1868 case AutomationLineItem:
1869 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1870 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1872 line->set_outline_color (ARDOUR_UI::config()->get_canvasvar_EnteredAutomationLine());
1874 if (is_drawable()) {
1875 set_canvas_cursor (_cursors->fader);
1880 case RegionViewNameHighlight:
1881 if (is_drawable() && effective_mouse_mode() == MouseObject && entered_regionview) {
1882 set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1883 _over_region_trim_target = true;
1887 case LeftFrameHandle:
1888 case RightFrameHandle:
1889 if (is_drawable() && effective_mouse_mode() == MouseObject && !internal_editing() && entered_regionview) {
1890 set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1894 case StartSelectionTrimItem:
1895 if (is_drawable()) {
1896 set_canvas_cursor (_cursors->left_side_trim);
1899 case EndSelectionTrimItem:
1900 if (is_drawable()) {
1901 set_canvas_cursor (_cursors->right_side_trim);
1905 case PlayheadCursorItem:
1906 if (is_drawable()) {
1907 switch (_edit_point) {
1909 set_canvas_cursor (_cursors->grabber_edit_point);
1912 set_canvas_cursor (_cursors->grabber);
1918 case RegionViewName:
1920 /* when the name is not an active item, the entire name highlight is for trimming */
1922 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1923 if (mouse_mode == MouseObject && is_drawable()) {
1924 set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1925 _over_region_trim_target = true;
1931 case AutomationTrackItem:
1932 if (is_drawable()) {
1933 Gdk::Cursor *cursor;
1934 switch (mouse_mode) {
1936 cursor = _cursors->selector;
1939 cursor = _cursors->zoom_in;
1942 cursor = _cursors->cross_hair;
1946 set_canvas_cursor (cursor);
1948 AutomationTimeAxisView* atv;
1949 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1950 clear_entered_track = false;
1951 set_entered_track (atv);
1957 case RangeMarkerBarItem:
1958 case TransportMarkerBarItem:
1959 case CdMarkerBarItem:
1962 if (is_drawable()) {
1963 set_canvas_cursor (_cursors->timebar);
1968 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1971 entered_marker = marker;
1972 marker->set_color_rgba (ARDOUR_UI::config()->get_canvasvar_EnteredMarker());
1974 case MeterMarkerItem:
1975 case TempoMarkerItem:
1976 if (is_drawable()) {
1977 set_canvas_cursor (_cursors->timebar);
1981 case FadeInHandleItem:
1982 if (mouse_mode == MouseObject && !internal_editing()) {
1983 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
1985 RegionView* rv = static_cast<RegionView*>(item->get_data ("regionview"));
1986 rect->set_fill_color (rv->get_fill_color());
1987 set_canvas_cursor (_cursors->fade_in);
1992 case FadeOutHandleItem:
1993 if (mouse_mode == MouseObject && !internal_editing()) {
1994 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
1996 RegionView* rv = static_cast<RegionView*>(item->get_data ("regionview"));
1997 rect->set_fill_color (rv->get_fill_color ());
1998 set_canvas_cursor (_cursors->fade_out);
2002 case FeatureLineItem:
2004 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
2005 line->set_outline_color (0xFF0000FF);
2010 if ( get_smart_mode() ) {
2011 set_canvas_cursor ();
2019 /* second pass to handle entered track status in a comprehensible way.
2022 switch (item_type) {
2024 case AutomationLineItem:
2025 case ControlPointItem:
2026 /* these do not affect the current entered track state */
2027 clear_entered_track = false;
2030 case AutomationTrackItem:
2031 /* handled above already */
2035 set_entered_track (0);
2043 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent*, ItemType item_type)
2052 switch (item_type) {
2053 case ControlPointItem:
2054 if (is_drawable()) {
2055 set_canvas_cursor (current_canvas_cursor);
2058 _verbose_cursor->hide ();
2061 case RegionViewNameHighlight:
2062 case LeftFrameHandle:
2063 case RightFrameHandle:
2064 case StartSelectionTrimItem:
2065 case EndSelectionTrimItem:
2066 case PlayheadCursorItem:
2068 _over_region_trim_target = false;
2070 if (is_drawable()) {
2071 set_canvas_cursor (current_canvas_cursor);
2076 case AutomationLineItem:
2077 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
2079 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
2081 line->set_outline_color (al->get_line_color());
2084 if (is_drawable()) {
2085 set_canvas_cursor (current_canvas_cursor);
2089 case RegionViewName:
2090 /* see enter_handler() for notes */
2091 _over_region_trim_target = false;
2093 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
2094 if (is_drawable() && mouse_mode == MouseObject) {
2095 set_canvas_cursor (current_canvas_cursor);
2100 case RangeMarkerBarItem:
2101 case TransportMarkerBarItem:
2102 case CdMarkerBarItem:
2106 if (is_drawable()) {
2107 set_canvas_cursor (current_canvas_cursor);
2112 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2116 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
2117 location_flags_changed (loc, this);
2120 case MeterMarkerItem:
2121 case TempoMarkerItem:
2123 if (is_drawable()) {
2124 set_canvas_cursor (current_canvas_cursor);
2129 case FadeInHandleItem:
2130 case FadeOutHandleItem:
2131 rv = static_cast<RegionView*>(item->get_data ("regionview"));
2133 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
2135 rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_InactiveFadeHandle());
2138 set_canvas_cursor (current_canvas_cursor);
2141 case AutomationTrackItem:
2142 if (is_drawable()) {
2143 set_canvas_cursor (current_canvas_cursor);
2144 clear_entered_track = true;
2145 Glib::signal_idle().connect (sigc::mem_fun(*this, &Editor::left_automation_track));
2148 case FeatureLineItem:
2150 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
2151 line->set_outline_color (ARDOUR_UI::config()->get_canvasvar_ZeroLine());
2163 Editor::left_automation_track ()
2165 if (clear_entered_track) {
2166 set_entered_track (0);
2167 clear_entered_track = false;
2173 Editor::scrub (framepos_t frame, double current_x)
2177 if (scrubbing_direction == 0) {
2179 _session->request_locate (frame, false);
2180 _session->request_transport_speed (0.1);
2181 scrubbing_direction = 1;
2185 if (last_scrub_x > current_x) {
2187 /* pointer moved to the left */
2189 if (scrubbing_direction > 0) {
2191 /* we reversed direction to go backwards */
2194 scrub_reverse_distance += (int) (last_scrub_x - current_x);
2198 /* still moving to the left (backwards) */
2200 scrub_reversals = 0;
2201 scrub_reverse_distance = 0;
2203 delta = 0.01 * (last_scrub_x - current_x);
2204 _session->request_transport_speed_nonzero (_session->transport_speed() - delta);
2208 /* pointer moved to the right */
2210 if (scrubbing_direction < 0) {
2211 /* we reversed direction to go forward */
2214 scrub_reverse_distance += (int) (current_x - last_scrub_x);
2217 /* still moving to the right */
2219 scrub_reversals = 0;
2220 scrub_reverse_distance = 0;
2222 delta = 0.01 * (current_x - last_scrub_x);
2223 _session->request_transport_speed_nonzero (_session->transport_speed() + delta);
2227 /* if there have been more than 2 opposite motion moves detected, or one that moves
2228 back more than 10 pixels, reverse direction
2231 if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
2233 if (scrubbing_direction > 0) {
2234 /* was forwards, go backwards */
2235 _session->request_transport_speed (-0.1);
2236 scrubbing_direction = -1;
2238 /* was backwards, go forwards */
2239 _session->request_transport_speed (0.1);
2240 scrubbing_direction = 1;
2243 scrub_reverse_distance = 0;
2244 scrub_reversals = 0;
2248 last_scrub_x = current_x;
2252 Editor::motion_handler (ArdourCanvas::Item* /*item*/, GdkEvent* event, bool from_autoscroll)
2254 _last_motion_y = event->motion.y;
2256 if (event->motion.is_hint) {
2259 /* We call this so that MOTION_NOTIFY events continue to be
2260 delivered to the canvas. We need to do this because we set
2261 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
2262 the density of the events, at the expense of a round-trip
2263 to the server. Given that this will mostly occur on cases
2264 where DISPLAY = :0.0, and given the cost of what the motion
2265 event might do, its a good tradeoff.
2268 _track_canvas->get_pointer (x, y);
2271 if (current_stepping_trackview) {
2272 /* don't keep the persistent stepped trackview if the mouse moves */
2273 current_stepping_trackview = 0;
2274 step_timeout.disconnect ();
2277 if (_session && _session->actively_recording()) {
2278 /* Sorry. no dragging stuff around while we record */
2282 JoinObjectRangeState const old = _join_object_range_state;
2283 update_join_object_range_location (event->motion.x, event->motion.y);
2285 if (!_internal_editing && _join_object_range_state != old) {
2286 set_canvas_cursor ();
2289 if (!_internal_editing && _over_region_trim_target) {
2290 set_canvas_cursor_for_region_view (event->motion.x, entered_regionview);
2293 bool handled = false;
2294 if (_drags->active ()) {
2295 handled = _drags->motion_handler (event, from_autoscroll);
2302 track_canvas_motion (event);
2307 Editor::can_remove_control_point (ArdourCanvas::Item* item)
2309 ControlPoint* control_point;
2311 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2312 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2316 AutomationLine& line = control_point->line ();
2317 if (dynamic_cast<AudioRegionGainLine*> (&line)) {
2318 /* we shouldn't remove the first or last gain point in region gain lines */
2319 if (line.is_last_point(*control_point) || line.is_first_point(*control_point)) {
2328 Editor::remove_control_point (ArdourCanvas::Item* item)
2330 if (!can_remove_control_point (item)) {
2334 ControlPoint* control_point;
2336 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2337 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2341 control_point->line().remove_point (*control_point);
2345 Editor::edit_control_point (ArdourCanvas::Item* item)
2347 ControlPoint* p = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"));
2350 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2354 ControlPointDialog d (p);
2357 if (d.run () != RESPONSE_ACCEPT) {
2361 p->line().modify_point_y (*p, d.get_y_fraction ());
2365 Editor::edit_notes (TimeAxisViewItem& tavi)
2367 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(&tavi);
2373 MidiRegionView::Selection const & s = mrv->selection();
2379 EditNoteDialog* d = new EditNoteDialog (&(*s.begin())->region_view(), s);
2383 d->signal_response().connect (sigc::bind (sigc::mem_fun (*this, &Editor::note_edit_done), d));
2387 Editor::note_edit_done (int r, EditNoteDialog* d)
2394 Editor::visible_order_range (int* low, int* high) const
2396 *low = TimeAxisView::max_order ();
2399 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
2401 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
2403 if (!rtv->hidden()) {
2405 if (*high < rtv->order()) {
2406 *high = rtv->order ();
2409 if (*low > rtv->order()) {
2410 *low = rtv->order ();
2417 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
2419 /* Either add to or set the set the region selection, unless
2420 this is an alignment click (control used)
2423 if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
2424 TimeAxisView* tv = &rv.get_time_axis_view();
2425 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
2427 if (rtv && rtv->is_track()) {
2428 speed = rtv->track()->speed();
2431 framepos_t where = get_preferred_edit_position();
2435 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
2437 align_region (rv.region(), SyncPoint, (framepos_t) (where * speed));
2439 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
2441 align_region (rv.region(), End, (framepos_t) (where * speed));
2445 align_region (rv.region(), Start, (framepos_t) (where * speed));
2452 Editor::collect_new_region_view (RegionView* rv)
2454 latest_regionviews.push_back (rv);
2458 Editor::collect_and_select_new_region_view (RegionView* rv)
2461 latest_regionviews.push_back (rv);
2465 Editor::cancel_selection ()
2467 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2468 (*i)->hide_selection ();
2471 selection->clear ();
2472 clicked_selection = 0;
2476 Editor::cancel_time_selection ()
2478 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2479 (*i)->hide_selection ();
2481 selection->time.clear ();
2482 clicked_selection = 0;
2486 Editor::point_trim (GdkEvent* event, framepos_t new_bound)
2488 RegionView* rv = clicked_regionview;
2490 /* Choose action dependant on which button was pressed */
2491 switch (event->button.button) {
2493 begin_reversible_command (_("start point trim"));
2495 if (selection->selected (rv)) {
2496 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
2497 i != selection->regions.by_layer().end(); ++i)
2499 if (!(*i)->region()->locked()) {
2500 (*i)->region()->clear_changes ();
2501 (*i)->region()->trim_front (new_bound);
2502 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2507 if (!rv->region()->locked()) {
2508 rv->region()->clear_changes ();
2509 rv->region()->trim_front (new_bound);
2510 _session->add_command(new StatefulDiffCommand (rv->region()));
2514 commit_reversible_command();
2518 begin_reversible_command (_("End point trim"));
2520 if (selection->selected (rv)) {
2522 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
2524 if (!(*i)->region()->locked()) {
2525 (*i)->region()->clear_changes();
2526 (*i)->region()->trim_end (new_bound);
2527 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2533 if (!rv->region()->locked()) {
2534 rv->region()->clear_changes ();
2535 rv->region()->trim_end (new_bound);
2536 _session->add_command (new StatefulDiffCommand (rv->region()));
2540 commit_reversible_command();
2549 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* /*event*/)
2554 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2555 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2559 Location* location = find_location_from_marker (marker, is_start);
2560 location->set_hidden (true, this);
2565 Editor::reposition_zoom_rect (framepos_t start, framepos_t end)
2567 double x1 = sample_to_pixel (start);
2568 double x2 = sample_to_pixel (end);
2569 double y2 = _full_canvas_height - 1.0;
2571 zoom_rect->set (ArdourCanvas::Rect (x1, 1.0, x2, y2));
2576 Editor::mouse_rename_region (ArdourCanvas::Item* /*item*/, GdkEvent* /*event*/)
2578 using namespace Gtkmm2ext;
2580 ArdourPrompter prompter (false);
2582 prompter.set_prompt (_("Name for region:"));
2583 prompter.set_initial_text (clicked_regionview->region()->name());
2584 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
2585 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
2586 prompter.show_all ();
2587 switch (prompter.run ()) {
2588 case Gtk::RESPONSE_ACCEPT:
2590 prompter.get_result(str);
2592 clicked_regionview->region()->set_name (str);
2601 Editor::mouse_brush_insert_region (RegionView* rv, framepos_t pos)
2603 /* no brushing without a useful snap setting */
2605 switch (_snap_mode) {
2607 return; /* can't work because it allows region to be placed anywhere */
2612 switch (_snap_type) {
2620 /* don't brush a copy over the original */
2622 if (pos == rv->region()->position()) {
2626 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
2628 if (rtv == 0 || !rtv->is_track()) {
2632 boost::shared_ptr<Playlist> playlist = rtv->playlist();
2633 double speed = rtv->track()->speed();
2635 playlist->clear_changes ();
2636 boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region(), true));
2637 playlist->add_region (new_region, (framepos_t) (pos * speed));
2638 _session->add_command (new StatefulDiffCommand (playlist));
2640 // playlist is frozen, so we have to update manually XXX this is disgusting
2642 playlist->RegionAdded (new_region); /* EMIT SIGNAL */
2646 Editor::track_height_step_timeout ()
2648 if (get_microseconds() - last_track_height_step_timestamp < 250000) {
2649 current_stepping_trackview = 0;
2656 Editor::add_region_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2658 assert (region_view);
2660 if (!region_view->region()->playlist()) {
2664 _region_motion_group->raise_to_top ();
2666 if (Config->get_edit_mode() == Splice) {
2667 _drags->add (new RegionSpliceDrag (this, item, region_view, selection->regions.by_layer()));
2669 _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), false, false));
2674 Editor::add_region_copy_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2676 assert (region_view);
2678 if (!region_view->region()->playlist()) {
2682 _region_motion_group->raise_to_top ();
2684 _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), false, true));
2688 Editor::add_region_brush_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2690 assert (region_view);
2692 if (!region_view->region()->playlist()) {
2696 if (Config->get_edit_mode() == Splice) {
2700 _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), true, false));
2702 begin_reversible_command (Operations::drag_region_brush);
2705 /** Start a grab where a time range is selected, track(s) are selected, and the
2706 * user clicks and drags a region with a modifier in order to create a new region containing
2707 * the section of the clicked region that lies within the time range.
2710 Editor::start_selection_grab (ArdourCanvas::Item* /*item*/, GdkEvent* event)
2712 if (clicked_regionview == 0) {
2716 /* lets try to create new Region for the selection */
2718 vector<boost::shared_ptr<Region> > new_regions;
2719 create_region_from_selection (new_regions);
2721 if (new_regions.empty()) {
2725 /* XXX fix me one day to use all new regions */
2727 boost::shared_ptr<Region> region (new_regions.front());
2729 /* add it to the current stream/playlist.
2731 tricky: the streamview for the track will add a new regionview. we will
2732 catch the signal it sends when it creates the regionview to
2733 set the regionview we want to then drag.
2736 latest_regionviews.clear();
2737 sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
2739 /* A selection grab currently creates two undo/redo operations, one for
2740 creating the new region and another for moving it.
2743 begin_reversible_command (Operations::selection_grab);
2745 boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
2747 playlist->clear_changes ();
2748 clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
2749 _session->add_command(new StatefulDiffCommand (playlist));
2751 commit_reversible_command ();
2755 if (latest_regionviews.empty()) {
2756 /* something went wrong */
2760 /* we need to deselect all other regionviews, and select this one
2761 i'm ignoring undo stuff, because the region creation will take care of it
2763 selection->set (latest_regionviews);
2765 _drags->set (new RegionMoveDrag (this, latest_regionviews.front()->get_canvas_group(), latest_regionviews.front(), latest_regionviews, false, false), event);
2771 if (_drags->active ()) {
2774 selection->clear ();
2779 Editor::set_internal_edit (bool yn)
2781 if (_internal_editing == yn) {
2785 _internal_editing = yn;
2788 pre_internal_mouse_mode = mouse_mode;
2789 pre_internal_snap_type = _snap_type;
2790 pre_internal_snap_mode = _snap_mode;
2792 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2793 (*i)->enter_internal_edit_mode ();
2796 set_snap_to (internal_snap_type);
2797 set_snap_mode (internal_snap_mode);
2801 internal_snap_mode = _snap_mode;
2802 internal_snap_type = _snap_type;
2804 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2805 (*i)->leave_internal_edit_mode ();
2808 if (mouse_mode == MouseDraw && pre_internal_mouse_mode != MouseDraw) {
2809 /* we were drawing .. flip back to something sensible */
2810 set_mouse_mode (pre_internal_mouse_mode);
2813 set_snap_to (pre_internal_snap_type);
2814 set_snap_mode (pre_internal_snap_mode);
2817 set_canvas_cursor ();
2820 /** Update _join_object_range_state which indicate whether we are over the top or bottom half of a region view,
2821 * used by the `join object/range' tool mode.
2824 Editor::update_join_object_range_location (double /*x*/, double y)
2826 /* XXX: actually, this decides based on whether the mouse is in the top
2827 or bottom half of a the waveform part RouteTimeAxisView;
2829 Note that entered_{track,regionview} is not always setup (e.g. if
2830 the mouse is over a TimeSelection), and to get a Region
2831 that we're over requires searching the playlist.
2834 if ( !get_smart_mode() ) {
2835 _join_object_range_state = JOIN_OBJECT_RANGE_NONE;
2839 if (mouse_mode == MouseObject) {
2840 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2841 } else if (mouse_mode == MouseRange) {
2842 _join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
2845 /* XXX: maybe we should make entered_track work in all cases, rather than resorting to this */
2846 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
2850 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
2855 rtv->canvas_display()->canvas_to_item (cx, cy);
2857 double const c = cy / (rtv->view()->child_height() - TimeAxisViewItem::NAME_HIGHLIGHT_SIZE);
2859 _join_object_range_state = c <= 0.5 ? JOIN_OBJECT_RANGE_RANGE : JOIN_OBJECT_RANGE_OBJECT;
2865 Editor::effective_mouse_mode () const
2867 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
2869 } else if (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE) {
2877 Editor::remove_midi_note (ArdourCanvas::Item* item, GdkEvent *)
2879 NoteBase* e = reinterpret_cast<NoteBase*> (item->get_data ("notebase"));
2882 e->region_view().delete_note (e->note ());
2886 Editor::set_canvas_cursor_for_region_view (double x, RegionView* rv)
2888 /* XXX: this check should not be necessary */
2895 ArdourCanvas::Group* g = rv->get_canvas_group ();
2896 ArdourCanvas::Group* p = g->parent ();
2898 /* Compute x in region view parent coordinates */
2900 p->canvas_to_item (x, dy);
2902 boost::optional<ArdourCanvas::Rect> item_bbox = g->bounding_box ();
2904 ArdourCanvas::Rect parent_bbox = g->item_to_parent (item_bbox.get ());
2906 /* Halfway across the region */
2907 double const h = (parent_bbox.x0 + parent_bbox.x1) / 2;
2909 Trimmable::CanTrim ct = rv->region()->can_trim ();
2911 if (ct & Trimmable::FrontTrimEarlier) {
2912 set_canvas_cursor (_cursors->left_side_trim);
2914 set_canvas_cursor (_cursors->left_side_trim_right_only);
2917 if (ct & Trimmable::EndTrimLater) {
2918 set_canvas_cursor (_cursors->right_side_trim);
2920 set_canvas_cursor (_cursors->right_side_trim_left_only);
2925 /** Obtain the pointer position in canvas coordinates */
2927 Editor::get_pointer_position (double& x, double& y) const
2930 _track_canvas->get_pointer (px, py);
2931 _track_canvas->window_to_canvas (px, py, x, y);