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.
28 #include "pbd/error.h"
29 #include "pbd/enumwriter.h"
30 #include "pbd/memento_command.h"
31 #include "pbd/basename.h"
32 #include "pbd/stateful_diff_command.h"
34 #include "gtkmm2ext/bindings.h"
35 #include "gtkmm2ext/utils.h"
36 #include "gtkmm2ext/tearoff.h"
38 #include "ardour_ui.h"
40 #include "canvas-note.h"
42 #include "time_axis_view.h"
43 #include "audio_time_axis.h"
44 #include "audio_region_view.h"
45 #include "midi_region_view.h"
47 #include "streamview.h"
48 #include "region_gain_line.h"
49 #include "automation_time_axis.h"
50 #include "control_point.h"
53 #include "selection.h"
56 #include "rgb_macros.h"
57 #include "control_point_dialog.h"
58 #include "editor_drag.h"
59 #include "automation_region_view.h"
60 #include "edit_note_dialog.h"
61 #include "mouse_cursors.h"
62 #include "editor_cursors.h"
63 #include "verbose_cursor.h"
65 #include "ardour/audioregion.h"
66 #include "ardour/operations.h"
67 #include "ardour/playlist.h"
68 #include "ardour/profile.h"
69 #include "ardour/region_factory.h"
70 #include "ardour/route.h"
71 #include "ardour/session.h"
72 #include "ardour/types.h"
79 using namespace ARDOUR;
82 using namespace Editing;
83 using Gtkmm2ext::Keyboard;
86 Editor::mouse_frame (framepos_t& where, bool& in_track_canvas) const
88 /* gdk_window_get_pointer() has X11's XQueryPointer semantics in that it only
89 pays attentions to subwindows. this means that menu windows are ignored, and
90 if the pointer is in a menu, the return window from the call will be the
91 the regular subwindow *under* the menu.
93 this matters quite a lot if the pointer is moving around in a menu that overlaps
94 the track canvas because we will believe that we are within the track canvas
95 when we are not. therefore, we track enter/leave events for the track canvas
96 and allow that to override the result of gdk_window_get_pointer().
99 if (!within_track_canvas) {
105 Gdk::ModifierType mask;
106 Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->track_canvas->get_window();
107 Glib::RefPtr<const Gdk::Window> pointer_window;
109 if (!canvas_window) {
113 pointer_window = canvas_window->get_pointer (x, y, mask);
115 if (pointer_window == track_canvas->get_bin_window()) {
118 in_track_canvas = true;
121 in_track_canvas = false;
126 event.type = GDK_BUTTON_RELEASE;
130 where = event_frame (&event, 0, 0);
135 Editor::event_frame (GdkEvent const * event, double* pcx, double* pcy) const
149 switch (event->type) {
150 case GDK_BUTTON_RELEASE:
151 case GDK_BUTTON_PRESS:
152 case GDK_2BUTTON_PRESS:
153 case GDK_3BUTTON_PRESS:
154 *pcx = event->button.x;
155 *pcy = event->button.y;
156 _trackview_group->w2i(*pcx, *pcy);
158 case GDK_MOTION_NOTIFY:
159 *pcx = event->motion.x;
160 *pcy = event->motion.y;
161 _trackview_group->w2i(*pcx, *pcy);
163 case GDK_ENTER_NOTIFY:
164 case GDK_LEAVE_NOTIFY:
165 track_canvas->w2c(event->crossing.x, event->crossing.y, *pcx, *pcy);
168 case GDK_KEY_RELEASE:
169 // track_canvas->w2c(event->key.x, event->key.y, *pcx, *pcy);
172 warning << string_compose (_("Editor::event_frame() used on unhandled event type %1"), event->type) << endmsg;
176 /* note that pixel_to_frame() never returns less than zero, so even if the pixel
177 position is negative (as can be the case with motion events in particular),
178 the frame location is always positive.
181 return pixel_to_frame (*pcx);
185 Editor::which_grabber_cursor ()
187 Gdk::Cursor* c = _cursors->grabber;
189 if (_internal_editing) {
190 switch (mouse_mode) {
192 c = _cursors->midi_pencil;
196 c = _cursors->grabber_note;
200 c = _cursors->midi_resize;
204 c = _cursors->grabber_note;
213 switch (_edit_point) {
215 c = _cursors->grabber_edit_point;
218 boost::shared_ptr<Movable> m = _movable.lock();
219 if (m && m->locked()) {
220 c = _cursors->speaker;
230 Editor::set_current_trimmable (boost::shared_ptr<Trimmable> t)
232 boost::shared_ptr<Trimmable> st = _trimmable.lock();
234 if (!st || st == t) {
236 set_canvas_cursor ();
241 Editor::set_current_movable (boost::shared_ptr<Movable> m)
243 boost::shared_ptr<Movable> sm = _movable.lock();
245 if (!sm || sm != m) {
247 set_canvas_cursor ();
252 Editor::set_canvas_cursor ()
254 switch (mouse_mode) {
256 current_canvas_cursor = _cursors->selector;
257 if (_internal_editing) {
258 current_canvas_cursor = which_grabber_cursor();
263 current_canvas_cursor = which_grabber_cursor();
267 current_canvas_cursor = _cursors->midi_pencil;
271 current_canvas_cursor = _cursors->cross_hair;
275 if (Keyboard::the_keyboard().key_is_down (GDK_Control_L)) {
276 current_canvas_cursor = _cursors->zoom_out;
278 current_canvas_cursor = _cursors->zoom_in;
283 current_canvas_cursor = _cursors->time_fx; // just use playhead
287 current_canvas_cursor = _cursors->speaker;
291 if (!_internal_editing) {
292 switch (_join_object_range_state) {
293 case JOIN_OBJECT_RANGE_NONE:
295 case JOIN_OBJECT_RANGE_OBJECT:
296 current_canvas_cursor = which_grabber_cursor ();
298 case JOIN_OBJECT_RANGE_RANGE:
299 current_canvas_cursor = _cursors->selector;
304 /* up-down cursor as a cue that automation can be dragged up and down when in join object/range mode */
305 if (!_internal_editing && get_smart_mode() ) {
307 get_pointer_position (x, y);
308 ArdourCanvas::Item* i = track_canvas->get_item_at (x, y);
309 if (i && i->property_parent() && (*i->property_parent()).get_data (X_("timeselection"))) {
310 pair<TimeAxisView*, int> tvp = trackview_by_y_position (_last_motion_y + vertical_adjustment.get_value() - canvas_timebars_vsize);
311 if (dynamic_cast<AutomationTimeAxisView*> (tvp.first)) {
312 current_canvas_cursor = _cursors->up_down;
317 set_canvas_cursor (current_canvas_cursor, true);
321 Editor::mouse_mode_object_range_toggled()
323 MouseMode m = mouse_mode;
325 Glib::RefPtr<Action> act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-range"));
327 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
329 if (tact->get_active())
330 m = MouseObject; //Smart mode turned to ON, force editing to Object mode
332 set_mouse_mode(m, true); //call this so the button styles can get updated
336 Editor::set_mouse_mode (MouseMode m, bool force)
338 if (_drags->active ()) {
342 if (!force && m == mouse_mode) {
346 Glib::RefPtr<Action> act;
350 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-range"));
354 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-object"));
358 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-draw"));
362 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-gain"));
366 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-zoom"));
370 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-timefx"));
374 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-audition"));
380 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
383 /* go there and back to ensure that the toggled handler is called to set up mouse_mode */
384 tact->set_active (false);
385 tact->set_active (true);
387 //NOTE: this will result in a call to mouse_mode_toggled which does the heavy lifting
391 Editor::mouse_mode_toggled (MouseMode m)
393 Glib::RefPtr<Action> act;
394 Glib::RefPtr<ToggleAction> tact;
398 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-range"));
402 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-object"));
406 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-draw"));
410 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-gain"));
414 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-zoom"));
418 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-timefx"));
422 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-audition"));
428 tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
431 if (!tact->get_active()) {
432 /* this was just the notification that the old mode has been
433 * left. we'll get called again with the new mode active in a
441 act = ActionManager::get_action (X_("MouseMode"), X_("toggle-internal-edit"));
442 tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
443 tact->set_active (true);
449 if (_session && mouse_mode == MouseAudition) {
450 /* stop transport and reset default speed to avoid oddness with
452 _session->request_transport_speed (0.0, true);
459 //TODO: set button styles for smart buttons
461 if ( smart_mode_action->get_active() ) {
462 if( mouse_mode == MouseObject ) { //smart active and object active
463 smart_mode_button.set_active(1);
464 smart_mode_button.set_name("smart mode button");
465 mouse_move_button.set_name("smart mode button");
466 } else { //smart active but object inactive
467 smart_mode_button.set_active(0);
468 smart_mode_button.set_name("smart mode button");
469 mouse_move_button.set_name("mouse mode button");
472 smart_mode_button.set_active(0);
473 smart_mode_button.set_name("mouse mode button");
474 mouse_move_button.set_name("mouse mode button");
478 set_canvas_cursor ();
479 set_gain_envelope_visibility ();
481 MouseModeChanged (); /* EMIT SIGNAL */
485 Editor::step_mouse_mode (bool next)
487 switch (current_mouse_mode()) {
490 if (Profile->get_sae()) {
491 set_mouse_mode (MouseZoom);
493 set_mouse_mode (MouseRange);
496 set_mouse_mode (MouseTimeFX);
501 if (next) set_mouse_mode (MouseDraw);
502 else set_mouse_mode (MouseObject);
506 if (next) set_mouse_mode (MouseZoom);
507 else set_mouse_mode (MouseRange);
512 if (Profile->get_sae()) {
513 set_mouse_mode (MouseTimeFX);
515 set_mouse_mode (MouseGain);
518 if (Profile->get_sae()) {
519 set_mouse_mode (MouseObject);
521 set_mouse_mode (MouseDraw);
527 if (next) set_mouse_mode (MouseTimeFX);
528 else set_mouse_mode (MouseZoom);
533 set_mouse_mode (MouseAudition);
535 if (Profile->get_sae()) {
536 set_mouse_mode (MouseZoom);
538 set_mouse_mode (MouseGain);
544 if (next) set_mouse_mode (MouseObject);
545 else set_mouse_mode (MouseTimeFX);
551 Editor::toggle_internal_editing_from_double_click (GdkEvent* event)
553 if (_drags->active()) {
554 _drags->end_grab (event);
557 ActionManager::do_action ("MouseMode", "toggle-internal-edit");
559 /* prevent reversion of edit cursor on button release */
561 pre_press_cursor = 0;
567 Editor::button_selection (ArdourCanvas::Item* /*item*/, GdkEvent* event, ItemType item_type)
569 /* in object/audition/timefx/gain-automation mode,
570 any button press sets the selection if the object
571 can be selected. this is a bit of hack, because
572 we want to avoid this if the mouse operation is a
575 note: not dbl-click or triple-click
577 Also note that there is no region selection in internal edit mode, otherwise
578 for operations operating on the selection (e.g. cut) it is not obvious whether
579 to cut notes or regions.
582 if (((mouse_mode != MouseObject) &&
583 (mouse_mode != MouseAudition || item_type != RegionItem) &&
584 (mouse_mode != MouseTimeFX || item_type != RegionItem) &&
585 (mouse_mode != MouseGain) &&
586 (mouse_mode != MouseDraw)) ||
587 ((event->type != GDK_BUTTON_PRESS && event->type != GDK_BUTTON_RELEASE) || event->button.button > 3) ||
588 (internal_editing() && mouse_mode != MouseTimeFX)) {
593 if (event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) {
595 if ((event->button.state & Keyboard::RelevantModifierKeyMask) && event->button.button != 1) {
597 /* almost no selection action on modified button-2 or button-3 events */
599 if (item_type != RegionItem && event->button.button != 2) {
605 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
606 bool press = (event->type == GDK_BUTTON_PRESS);
610 if (!get_smart_mode() || (_join_object_range_state != JOIN_OBJECT_RANGE_RANGE)) {
612 if (mouse_mode != MouseRange) {
613 set_selected_regionview_from_click (press, op);
615 /* don't change the selection unless the
616 clicked track is not currently selected. if
617 so, "collapse" the selection to just this
620 if (!selection->selected (clicked_axisview)) {
621 set_selected_track_as_side_effect (Selection::Set);
625 if (mouse_mode != MouseRange) {
626 set_selected_regionview_from_click (press, op);
632 case RegionViewNameHighlight:
634 case LeftFrameHandle:
635 case RightFrameHandle:
636 if ( mouse_mode != MouseRange ) {
637 set_selected_regionview_from_click (press, op);
638 } else if (event->type == GDK_BUTTON_PRESS) {
639 set_selected_track_as_side_effect (op);
643 case FadeInHandleItem:
645 case FadeOutHandleItem:
647 case StartCrossFadeItem:
648 case EndCrossFadeItem:
649 if ( mouse_mode != MouseRange ) {
650 set_selected_regionview_from_click (press, op);
651 } else if (event->type == GDK_BUTTON_PRESS) {
652 set_selected_track_as_side_effect (op);
656 case ControlPointItem:
657 set_selected_track_as_side_effect (op);
658 if ( mouse_mode != MouseRange ) {
659 set_selected_control_point_from_click (press, op);
664 /* for context click, select track */
665 if (event->button.button == 3) {
666 selection->clear_tracks ();
667 set_selected_track_as_side_effect (op);
671 case AutomationTrackItem:
672 set_selected_track_as_side_effect (op);
681 Editor::button_press_handler_1 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
683 /* single mouse clicks on any of these item types operate
684 independent of mouse mode, mostly because they are
685 not on the main track canvas or because we want
690 case PlayheadCursorItem:
691 _drags->set (new CursorDrag (this, item, true), event);
695 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
696 hide_marker (item, event);
698 _drags->set (new MarkerDrag (this, item), event);
702 case TempoMarkerItem:
704 TempoMarker* m = reinterpret_cast<TempoMarker*> (item->get_data ("marker"));
706 if (m->tempo().movable ()) {
708 new TempoMarkerDrag (
711 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
721 case MeterMarkerItem:
723 MeterMarker* m = reinterpret_cast<MeterMarker*> (item->get_data ("marker"));
725 if (m->meter().movable ()) {
727 new MeterMarkerDrag (
730 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
741 _drags->set (new VideoTimeLineDrag (this, item), event);
748 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
749 _drags->set (new CursorDrag (this, &playhead_cursor->canvas_item, false), event);
755 case RangeMarkerBarItem:
756 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
757 _drags->set (new CursorDrag (this, &playhead_cursor->canvas_item, false), event);
759 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateRangeMarker), event);
764 case CdMarkerBarItem:
765 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
766 _drags->set (new CursorDrag (this, &playhead_cursor->canvas_item, false), event);
768 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateCDMarker), event);
773 case TransportMarkerBarItem:
774 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
775 _drags->set (new CursorDrag (this, &playhead_cursor->canvas_item, false), event);
777 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateTransportMarker), event);
786 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
787 /* special case: allow trim of range selections in joined object mode;
788 in theory eff should equal MouseRange in this case, but it doesn't
789 because entering the range selection canvas item results in entered_regionview
790 being set to 0, so update_join_object_range_location acts as if we aren't
793 if (item_type == StartSelectionTrimItem) {
794 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
795 } else if (item_type == EndSelectionTrimItem) {
796 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
800 Editing::MouseMode eff = effective_mouse_mode ();
802 /* special case: allow drag of region fade in/out in object mode with join object/range enabled */
803 if (item_type == FadeInHandleItem || item_type == FadeOutHandleItem) {
807 /* there is no Range mode when in internal edit mode */
808 if (eff == MouseRange && internal_editing()) {
815 case StartSelectionTrimItem:
816 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
819 case EndSelectionTrimItem:
820 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
824 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
825 start_selection_grab (item, event);
826 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
827 /* grab selection for moving */
828 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionMove), event);
830 double const y = event->button.y + vertical_adjustment.get_value() - canvas_timebars_vsize;
831 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
833 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
834 if ( get_smart_mode() && atv) {
835 /* smart "join" mode: drag automation */
836 _drags->set (new AutomationRangeDrag (this, atv, selection->time), event, _cursors->up_down);
838 /* this was debated, but decided the more common action was to
839 make a new selection */
840 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
847 if (internal_editing()) {
848 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
849 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
853 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::RangeSelectModifier)) {
854 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionExtend), event);
856 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
862 case RegionViewNameHighlight:
863 if (!clicked_regionview->region()->locked()) {
864 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
870 if (!internal_editing()) {
871 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::RangeSelectModifier)) {
872 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionExtend), event);
874 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
884 if (internal_editing()) {
885 /* trim notes if we're in internal edit mode and near the ends of the note */
886 ArdourCanvas::CanvasNote* cn = dynamic_cast<ArdourCanvas::CanvasNote*> (item);
887 if (cn && cn->big_enough_to_trim() && cn->mouse_near_ends()) {
888 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
890 _drags->set (new NoteDrag (this, item), event);
896 if (internal_editing()) {
897 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
898 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
912 if (internal_editing()) {
913 ArdourCanvas::CanvasNoteEvent* cn = dynamic_cast<ArdourCanvas::CanvasNoteEvent*> (item);
914 if (cn->mouse_near_ends()) {
915 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
917 _drags->set (new NoteDrag (this, item), event);
927 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)) &&
928 event->type == GDK_BUTTON_PRESS) {
930 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
932 } else if (event->type == GDK_BUTTON_PRESS) {
935 case FadeInHandleItem:
937 _drags->set (new FadeInDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), selection->regions), event, _cursors->fade_in);
941 case FadeOutHandleItem:
943 _drags->set (new FadeOutDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), selection->regions), event, _cursors->fade_out);
947 case StartCrossFadeItem:
948 case EndCrossFadeItem:
949 /* we might allow user to grab inside the fade to trim a region with preserve_fade_anchor. for not this is not fully implemented */
950 // if (!clicked_regionview->region()->locked()) {
951 // _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer(), true), event);
956 case FeatureLineItem:
958 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier)) {
959 remove_transient(item);
963 _drags->set (new FeatureLineDrag (this, item), event);
969 if (dynamic_cast<AutomationRegionView*> (clicked_regionview)) {
970 /* click on an automation region view; do nothing here and let the ARV's signal handler
976 if (internal_editing ()) {
980 /* click on a normal region view */
981 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
982 add_region_copy_drag (item, event, clicked_regionview);
983 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
984 add_region_brush_drag (item, event, clicked_regionview);
986 add_region_drag (item, event, clicked_regionview);
990 // if (!internal_editing() && (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE && !selection->regions.empty())) {
991 // _drags->add (new SelectionDrag (this, clicked_axisview->get_selection_rect (clicked_selection)->rect, SelectionDrag::SelectionMove));
994 _drags->start_grab (event);
997 case RegionViewNameHighlight:
998 case LeftFrameHandle:
999 case RightFrameHandle:
1000 if (!clicked_regionview->region()->locked()) {
1001 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1006 case RegionViewName:
1008 /* rename happens on edit clicks */
1009 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer()), event);
1014 case ControlPointItem:
1015 _drags->set (new ControlPointDrag (this, item), event);
1019 case AutomationLineItem:
1020 _drags->set (new LineDrag (this, item), event);
1025 if (internal_editing()) {
1026 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
1027 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
1031 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
1035 case AutomationTrackItem:
1037 TimeAxisView* parent = clicked_axisview->get_parent ();
1038 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (clicked_axisview);
1040 if (parent && dynamic_cast<MidiTimeAxisView*> (parent) && atv->show_regions ()) {
1042 RouteTimeAxisView* p = dynamic_cast<RouteTimeAxisView*> (parent);
1044 boost::shared_ptr<Playlist> pl = p->track()->playlist ();
1045 if (pl->n_regions() == 0) {
1046 /* Parent has no regions; create one so that we have somewhere to put automation */
1047 _drags->set (new RegionCreateDrag (this, item, parent), event);
1049 /* See if there's a region before the click that we can extend, and extend it if so */
1050 framepos_t const t = event_frame (event);
1051 boost::shared_ptr<Region> prev = pl->find_next_region (t, End, -1);
1053 _drags->set (new RegionCreateDrag (this, item, parent), event);
1055 prev->set_length (t - prev->position ());
1059 /* rubberband drag to select automation points */
1060 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
1067 if ( get_smart_mode() ) {
1068 /* we're in "smart" joined mode, and we've clicked on a Selection */
1069 double const y = event->button.y + vertical_adjustment.get_value() - canvas_timebars_vsize;
1070 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
1072 /* if we're over an automation track, start a drag of its data */
1073 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
1075 _drags->set (new AutomationRangeDrag (this, atv, selection->time), event, _cursors->up_down);
1078 /* if we're over a track and a region, and in the `object' part of a region,
1079 put a selection around the region and drag both
1081 /* RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1082 if (rtv && _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
1083 boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track> (rtv->route ());
1085 boost::shared_ptr<Playlist> pl = t->playlist ();
1088 boost::shared_ptr<Region> r = pl->top_region_at (event_frame (event));
1090 RegionView* rv = rtv->view()->find_view (r);
1091 clicked_selection = select_range (rv->region()->position(),
1092 rv->region()->last_frame()+1);
1093 _drags->add (new SelectionDrag (this, item, SelectionDrag::SelectionMove));
1094 list<RegionView*> rvs;
1096 _drags->add (new RegionMoveDrag (this, item, rv, rvs, false, false));
1097 _drags->start_grab (event);
1120 switch (item_type) {
1122 _drags->set (new LineDrag (this, item), event);
1125 case ControlPointItem:
1126 _drags->set (new ControlPointDrag (this, item), event);
1132 AudioRegionView* arv = dynamic_cast<AudioRegionView *> (clicked_regionview);
1134 _drags->set (new AutomationRangeDrag (this, arv, selection->time), event, _cursors->up_down);
1135 _drags->start_grab (event);
1141 case AutomationLineItem:
1142 _drags->set (new LineDrag (this, item), event);
1152 if (event->type == GDK_BUTTON_PRESS) {
1153 _drags->set (new MouseZoomDrag (this, item), event);
1160 if (internal_editing() && item_type == NoteItem) {
1161 /* drag notes if we're in internal edit mode */
1162 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
1164 } else if (clicked_regionview) {
1166 _drags->set (new TimeFXDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1172 _drags->set (new ScrubDrag (this, item), event);
1173 scrub_reversals = 0;
1174 scrub_reverse_distance = 0;
1175 last_scrub_x = event->button.x;
1176 scrubbing_direction = 0;
1177 set_canvas_cursor (_cursors->transparent);
1189 Editor::button_press_handler_2 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1191 Editing::MouseMode const eff = effective_mouse_mode ();
1194 switch (item_type) {
1196 if (internal_editing ()) {
1197 /* no region drags in internal edit mode */
1201 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
1202 add_region_copy_drag (item, event, clicked_regionview);
1204 add_region_drag (item, event, clicked_regionview);
1206 _drags->start_grab (event);
1209 case ControlPointItem:
1210 _drags->set (new ControlPointDrag (this, item), event);
1218 switch (item_type) {
1219 case RegionViewNameHighlight:
1220 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1224 case LeftFrameHandle:
1225 case RightFrameHandle:
1226 if (!internal_editing ()) {
1227 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1232 case RegionViewName:
1233 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer()), event);
1247 /* relax till release */
1253 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1254 temporal_zoom_to_frame (false, event_frame (event));
1256 temporal_zoom_to_frame (true, event_frame(event));
1269 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1271 if (event->type != GDK_BUTTON_PRESS) {
1275 Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->track_canvas->get_window();
1277 if (canvas_window) {
1278 Glib::RefPtr<const Gdk::Window> pointer_window;
1281 Gdk::ModifierType mask;
1283 pointer_window = canvas_window->get_pointer (x, y, mask);
1285 if (pointer_window == track_canvas->get_bin_window()) {
1286 track_canvas->window_to_world (x, y, wx, wy);
1290 pre_press_cursor = current_canvas_cursor;
1292 track_canvas->grab_focus();
1294 if (_session && _session->actively_recording()) {
1298 if (internal_editing()) {
1299 bool leave_internal_edit_mode = false;
1301 switch (item_type) {
1306 if (!dynamic_cast<MidiRegionView*> (clicked_regionview) && !dynamic_cast<AutomationRegionView*> (clicked_regionview)) {
1307 leave_internal_edit_mode = true;
1311 case PlayheadCursorItem:
1313 case TempoMarkerItem:
1314 case MeterMarkerItem:
1318 case RangeMarkerBarItem:
1319 case CdMarkerBarItem:
1320 case TransportMarkerBarItem:
1322 /* button press on these events never does anything to
1323 change the editing mode.
1331 if (leave_internal_edit_mode) {
1332 ActionManager::do_action ("MouseMode", "toggle-internal-edit");
1336 button_selection (item, event, item_type);
1338 if (!_drags->active () &&
1339 (Keyboard::is_delete_event (&event->button) ||
1340 Keyboard::is_context_menu_event (&event->button) ||
1341 Keyboard::is_edit_event (&event->button))) {
1343 /* handled by button release */
1347 //not rolling, range mode click + join_play_range : locate the PH here
1348 if ( !_drags->active () && !_session->transport_rolling() && ( effective_mouse_mode() == MouseRange ) && Config->get_always_play_range() ) {
1349 framepos_t where = event_frame (event, 0, 0);
1351 _session->request_locate (where, false);
1354 switch (event->button.button) {
1356 return button_press_handler_1 (item, event, item_type);
1360 return button_press_handler_2 (item, event, item_type);
1367 return button_press_dispatch (&event->button);
1376 Editor::button_press_dispatch (GdkEventButton* ev)
1378 /* this function is intended only for buttons 4 and above.
1381 Gtkmm2ext::MouseButton b (ev->state, ev->button);
1382 return button_bindings->activate (b, Gtkmm2ext::Bindings::Press);
1386 Editor::button_release_dispatch (GdkEventButton* ev)
1388 /* this function is intended only for buttons 4 and above.
1391 Gtkmm2ext::MouseButton b (ev->state, ev->button);
1392 return button_bindings->activate (b, Gtkmm2ext::Bindings::Release);
1396 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1398 framepos_t where = event_frame (event, 0, 0);
1399 AutomationTimeAxisView* atv = 0;
1401 if (pre_press_cursor) {
1402 set_canvas_cursor (pre_press_cursor);
1403 pre_press_cursor = 0;
1406 /* no action if we're recording */
1408 if (_session && _session->actively_recording()) {
1412 /* see if we're finishing a drag */
1414 bool were_dragging = false;
1415 if (_drags->active ()) {
1416 bool const r = _drags->end_grab (event);
1418 /* grab dragged, so do nothing else */
1422 were_dragging = true;
1425 update_region_layering_order_editor ();
1427 /* edit events get handled here */
1429 if (!_drags->active () && Keyboard::is_edit_event (&event->button)) {
1430 switch (item_type) {
1432 show_region_properties ();
1435 case TempoMarkerItem:
1436 edit_tempo_marker (item);
1439 case MeterMarkerItem:
1440 edit_meter_marker (item);
1443 case RegionViewName:
1444 if (clicked_regionview->name_active()) {
1445 return mouse_rename_region (item, event);
1449 case ControlPointItem:
1450 edit_control_point (item);
1459 /* context menu events get handled here */
1460 if (Keyboard::is_context_menu_event (&event->button)) {
1462 context_click_event = *event;
1464 if (!_drags->active ()) {
1466 /* no matter which button pops up the context menu, tell the menu
1467 widget to use button 1 to drive menu selection.
1470 switch (item_type) {
1472 case FadeInHandleItem:
1474 case FadeOutHandleItem:
1475 popup_fade_context_menu (1, event->button.time, item, item_type);
1478 case StartCrossFadeItem:
1479 popup_xfade_in_context_menu (1, event->button.time, item, item_type);
1482 case EndCrossFadeItem:
1483 popup_xfade_out_context_menu (1, event->button.time, item, item_type);
1487 popup_track_context_menu (1, event->button.time, item_type, false);
1491 case RegionViewNameHighlight:
1492 case LeftFrameHandle:
1493 case RightFrameHandle:
1494 case RegionViewName:
1495 popup_track_context_menu (1, event->button.time, item_type, false);
1499 popup_track_context_menu (1, event->button.time, item_type, true);
1502 case AutomationTrackItem:
1503 popup_track_context_menu (1, event->button.time, item_type, false);
1507 case RangeMarkerBarItem:
1508 case TransportMarkerBarItem:
1509 case CdMarkerBarItem:
1513 popup_ruler_menu (where, item_type);
1517 marker_context_menu (&event->button, item);
1520 case TempoMarkerItem:
1521 tempo_or_meter_marker_context_menu (&event->button, item);
1524 case MeterMarkerItem:
1525 tempo_or_meter_marker_context_menu (&event->button, item);
1528 case CrossfadeViewItem:
1529 popup_track_context_menu (1, event->button.time, item_type, false);
1532 case ControlPointItem:
1533 popup_control_point_context_menu (item, event);
1544 /* delete events get handled here */
1546 Editing::MouseMode const eff = effective_mouse_mode ();
1548 if (!_drags->active () && Keyboard::is_delete_event (&event->button)) {
1550 switch (item_type) {
1551 case TempoMarkerItem:
1552 remove_tempo_marker (item);
1555 case MeterMarkerItem:
1556 remove_meter_marker (item);
1560 remove_marker (*item, event);
1564 if (eff == MouseObject) {
1565 remove_clicked_region ();
1569 case ControlPointItem:
1570 remove_control_point (item);
1574 remove_midi_note (item, event);
1583 switch (event->button.button) {
1586 switch (item_type) {
1587 /* see comments in button_press_handler */
1588 case PlayheadCursorItem:
1591 case AutomationLineItem:
1592 case StartSelectionTrimItem:
1593 case EndSelectionTrimItem:
1597 if (!_dragging_playhead) {
1598 snap_to_with_modifier (where, event, 0, true);
1599 mouse_add_new_marker (where);
1603 case CdMarkerBarItem:
1604 if (!_dragging_playhead) {
1605 // if we get here then a dragged range wasn't done
1606 snap_to_with_modifier (where, event, 0, true);
1607 mouse_add_new_marker (where, true);
1612 if (!_dragging_playhead) {
1613 snap_to_with_modifier (where, event);
1614 mouse_add_new_tempo_event (where);
1619 if (!_dragging_playhead) {
1620 mouse_add_new_meter_event (pixel_to_frame (event->button.x));
1631 switch (item_type) {
1632 case AutomationTrackItem:
1633 atv = dynamic_cast<AutomationTimeAxisView*>(clicked_axisview);
1635 atv->add_automation_event (event, where, event->button.y);
1645 switch (item_type) {
1648 /* check that we didn't drag before releasing, since
1649 its really annoying to create new control
1650 points when doing this.
1652 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (clicked_regionview);
1653 if (!were_dragging && arv) {
1654 arv->add_gain_point_event (item, event);
1660 case AutomationTrackItem:
1661 dynamic_cast<AutomationTimeAxisView*>(clicked_axisview)->
1662 add_automation_event (event, where, event->button.y);
1671 set_canvas_cursor (current_canvas_cursor);
1672 if (scrubbing_direction == 0) {
1673 /* no drag, just a click */
1674 switch (item_type) {
1676 play_selected_region ();
1682 /* make sure we stop */
1683 _session->request_transport_speed (0.0);
1692 /* do any (de)selection operations that should occur on button release */
1693 button_selection (item, event, item_type);
1702 switch (item_type) {
1704 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1706 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier|Keyboard::SecondaryModifier))) {
1709 // Button2 click is unused
1724 // x_style_paste (where, 1.0);
1745 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1752 switch (item_type) {
1753 case ControlPointItem:
1754 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1755 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1756 cp->set_visible (true);
1760 at_y = cp->get_y ();
1761 cp->i2w (at_x, at_y);
1765 fraction = 1.0 - (cp->get_y() / cp->line().height());
1767 if (is_drawable() && !_drags->active ()) {
1768 set_canvas_cursor (_cursors->fader);
1771 _verbose_cursor->set (cp->line().get_verbose_cursor_string (fraction), at_x, at_y);
1772 _verbose_cursor->show ();
1777 if (mouse_mode == MouseGain) {
1778 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1780 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredGainLine.get();
1781 if (is_drawable()) {
1782 set_canvas_cursor (_cursors->fader);
1787 case AutomationLineItem:
1788 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1789 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1791 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredAutomationLine.get();
1793 if (is_drawable()) {
1794 set_canvas_cursor (_cursors->fader);
1799 case RegionViewNameHighlight:
1800 if (is_drawable() && effective_mouse_mode() == MouseObject && entered_regionview) {
1801 set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1802 _over_region_trim_target = true;
1806 case LeftFrameHandle:
1807 case RightFrameHandle:
1808 if (is_drawable() && effective_mouse_mode() == MouseObject && !internal_editing() && entered_regionview) {
1809 set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1813 case StartSelectionTrimItem:
1814 if (is_drawable()) {
1815 set_canvas_cursor (_cursors->left_side_trim);
1818 case EndSelectionTrimItem:
1819 if (is_drawable()) {
1820 set_canvas_cursor (_cursors->right_side_trim);
1824 case PlayheadCursorItem:
1825 if (is_drawable()) {
1826 switch (_edit_point) {
1828 set_canvas_cursor (_cursors->grabber_edit_point);
1831 set_canvas_cursor (_cursors->grabber);
1837 case RegionViewName:
1839 /* when the name is not an active item, the entire name highlight is for trimming */
1841 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1842 if (mouse_mode == MouseObject && is_drawable()) {
1843 set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1844 _over_region_trim_target = true;
1850 case AutomationTrackItem:
1851 if (is_drawable()) {
1852 Gdk::Cursor *cursor;
1853 switch (mouse_mode) {
1855 cursor = _cursors->selector;
1858 cursor = _cursors->zoom_in;
1861 cursor = _cursors->cross_hair;
1865 set_canvas_cursor (cursor);
1867 AutomationTimeAxisView* atv;
1868 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1869 clear_entered_track = false;
1870 set_entered_track (atv);
1876 case RangeMarkerBarItem:
1877 case TransportMarkerBarItem:
1878 case CdMarkerBarItem:
1881 if (is_drawable()) {
1882 set_canvas_cursor (_cursors->timebar);
1887 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1890 entered_marker = marker;
1891 marker->set_color_rgba (ARDOUR_UI::config()->canvasvar_EnteredMarker.get());
1893 case MeterMarkerItem:
1894 case TempoMarkerItem:
1895 if (is_drawable()) {
1896 set_canvas_cursor (_cursors->timebar);
1900 case FadeInHandleItem:
1901 if (mouse_mode == MouseObject && !internal_editing()) {
1902 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1904 rect->property_fill_color_rgba() = 0xBBBBBBAA;
1906 set_canvas_cursor (_cursors->fade_in);
1910 case FadeOutHandleItem:
1911 if (mouse_mode == MouseObject && !internal_editing()) {
1912 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1914 rect->property_fill_color_rgba() = 0xBBBBBBAA;
1916 set_canvas_cursor (_cursors->fade_out);
1919 case FeatureLineItem:
1921 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1922 line->property_fill_color_rgba() = 0xFF0000FF;
1926 if ( get_smart_mode() ) {
1927 set_canvas_cursor ();
1935 /* second pass to handle entered track status in a comprehensible way.
1938 switch (item_type) {
1940 case AutomationLineItem:
1941 case ControlPointItem:
1942 /* these do not affect the current entered track state */
1943 clear_entered_track = false;
1946 case AutomationTrackItem:
1947 /* handled above already */
1951 set_entered_track (0);
1959 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent*, ItemType item_type)
1969 switch (item_type) {
1970 case ControlPointItem:
1971 cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
1972 if (cp->line().the_list()->interpolation() != AutomationList::Discrete) {
1973 if (cp->line().npoints() > 1 && !cp->get_selected()) {
1974 cp->set_visible (false);
1978 if (is_drawable()) {
1979 set_canvas_cursor (current_canvas_cursor);
1982 _verbose_cursor->hide ();
1985 case RegionViewNameHighlight:
1986 case LeftFrameHandle:
1987 case RightFrameHandle:
1988 case StartSelectionTrimItem:
1989 case EndSelectionTrimItem:
1990 case PlayheadCursorItem:
1992 _over_region_trim_target = false;
1994 if (is_drawable()) {
1995 set_canvas_cursor (current_canvas_cursor);
2000 case AutomationLineItem:
2001 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
2003 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
2005 line->property_fill_color_rgba() = al->get_line_color();
2007 if (is_drawable()) {
2008 set_canvas_cursor (current_canvas_cursor);
2012 case RegionViewName:
2013 /* see enter_handler() for notes */
2014 _over_region_trim_target = false;
2016 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
2017 if (is_drawable() && mouse_mode == MouseObject) {
2018 set_canvas_cursor (current_canvas_cursor);
2023 case RangeMarkerBarItem:
2024 case TransportMarkerBarItem:
2025 case CdMarkerBarItem:
2029 if (is_drawable()) {
2030 set_canvas_cursor (current_canvas_cursor);
2035 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2039 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
2040 location_flags_changed (loc, this);
2043 case MeterMarkerItem:
2044 case TempoMarkerItem:
2046 if (is_drawable()) {
2047 set_canvas_cursor (current_canvas_cursor);
2052 case FadeInHandleItem:
2053 case FadeOutHandleItem:
2054 rv = static_cast<RegionView*>(item->get_data ("regionview"));
2056 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
2058 rect->property_fill_color_rgba() = rv->get_fill_color();
2061 set_canvas_cursor (current_canvas_cursor);
2064 case AutomationTrackItem:
2065 if (is_drawable()) {
2066 set_canvas_cursor (current_canvas_cursor);
2067 clear_entered_track = true;
2068 Glib::signal_idle().connect (sigc::mem_fun(*this, &Editor::left_automation_track));
2071 case FeatureLineItem:
2073 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
2074 line->property_fill_color_rgba() = (guint) ARDOUR_UI::config()->canvasvar_ZeroLine.get();;
2086 Editor::left_automation_track ()
2088 if (clear_entered_track) {
2089 set_entered_track (0);
2090 clear_entered_track = false;
2096 Editor::scrub (framepos_t frame, double current_x)
2100 if (scrubbing_direction == 0) {
2102 _session->request_locate (frame, false);
2103 _session->request_transport_speed (0.1);
2104 scrubbing_direction = 1;
2108 if (last_scrub_x > current_x) {
2110 /* pointer moved to the left */
2112 if (scrubbing_direction > 0) {
2114 /* we reversed direction to go backwards */
2117 scrub_reverse_distance += (int) (last_scrub_x - current_x);
2121 /* still moving to the left (backwards) */
2123 scrub_reversals = 0;
2124 scrub_reverse_distance = 0;
2126 delta = 0.01 * (last_scrub_x - current_x);
2127 _session->request_transport_speed_nonzero (_session->transport_speed() - delta);
2131 /* pointer moved to the right */
2133 if (scrubbing_direction < 0) {
2134 /* we reversed direction to go forward */
2137 scrub_reverse_distance += (int) (current_x - last_scrub_x);
2140 /* still moving to the right */
2142 scrub_reversals = 0;
2143 scrub_reverse_distance = 0;
2145 delta = 0.01 * (current_x - last_scrub_x);
2146 _session->request_transport_speed_nonzero (_session->transport_speed() + delta);
2150 /* if there have been more than 2 opposite motion moves detected, or one that moves
2151 back more than 10 pixels, reverse direction
2154 if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
2156 if (scrubbing_direction > 0) {
2157 /* was forwards, go backwards */
2158 _session->request_transport_speed (-0.1);
2159 scrubbing_direction = -1;
2161 /* was backwards, go forwards */
2162 _session->request_transport_speed (0.1);
2163 scrubbing_direction = 1;
2166 scrub_reverse_distance = 0;
2167 scrub_reversals = 0;
2171 last_scrub_x = current_x;
2175 Editor::motion_handler (ArdourCanvas::Item* /*item*/, GdkEvent* event, bool from_autoscroll)
2177 _last_motion_y = event->motion.y;
2179 if (event->motion.is_hint) {
2182 /* We call this so that MOTION_NOTIFY events continue to be
2183 delivered to the canvas. We need to do this because we set
2184 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
2185 the density of the events, at the expense of a round-trip
2186 to the server. Given that this will mostly occur on cases
2187 where DISPLAY = :0.0, and given the cost of what the motion
2188 event might do, its a good tradeoff.
2191 track_canvas->get_pointer (x, y);
2194 if (current_stepping_trackview) {
2195 /* don't keep the persistent stepped trackview if the mouse moves */
2196 current_stepping_trackview = 0;
2197 step_timeout.disconnect ();
2200 if (_session && _session->actively_recording()) {
2201 /* Sorry. no dragging stuff around while we record */
2205 JoinObjectRangeState const old = _join_object_range_state;
2206 update_join_object_range_location (event->motion.x, event->motion.y);
2208 if (!_internal_editing && _join_object_range_state != old) {
2209 set_canvas_cursor ();
2212 if (!_internal_editing && _over_region_trim_target) {
2213 set_canvas_cursor_for_region_view (event->motion.x, entered_regionview);
2216 bool handled = false;
2217 if (_drags->active ()) {
2218 handled = _drags->motion_handler (event, from_autoscroll);
2225 track_canvas_motion (event);
2230 Editor::can_remove_control_point (ArdourCanvas::Item* item)
2232 ControlPoint* control_point;
2234 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2235 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2239 AutomationLine& line = control_point->line ();
2240 if (dynamic_cast<AudioRegionGainLine*> (&line)) {
2241 /* we shouldn't remove the first or last gain point in region gain lines */
2242 if (line.is_last_point(*control_point) || line.is_first_point(*control_point)) {
2251 Editor::remove_control_point (ArdourCanvas::Item* item)
2253 if (!can_remove_control_point (item)) {
2257 ControlPoint* control_point;
2259 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2260 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2264 control_point->line().remove_point (*control_point);
2268 Editor::edit_control_point (ArdourCanvas::Item* item)
2270 ControlPoint* p = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"));
2273 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2277 ControlPointDialog d (p);
2280 if (d.run () != RESPONSE_ACCEPT) {
2284 p->line().modify_point_y (*p, d.get_y_fraction ());
2288 Editor::edit_notes (TimeAxisViewItem& tavi)
2290 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(&tavi);
2296 MidiRegionView::Selection const & s = mrv->selection();
2302 EditNoteDialog* d = new EditNoteDialog (&(*s.begin())->region_view(), s);
2306 d->signal_response().connect (sigc::bind (sigc::mem_fun (*this, &Editor::note_edit_done), d));
2310 Editor::note_edit_done (int r, EditNoteDialog* d)
2317 Editor::visible_order_range (int* low, int* high) const
2319 *low = TimeAxisView::max_order ();
2322 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
2324 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
2326 if (!rtv->hidden()) {
2328 if (*high < rtv->order()) {
2329 *high = rtv->order ();
2332 if (*low > rtv->order()) {
2333 *low = rtv->order ();
2340 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
2342 /* Either add to or set the set the region selection, unless
2343 this is an alignment click (control used)
2346 if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
2347 TimeAxisView* tv = &rv.get_time_axis_view();
2348 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
2350 if (rtv && rtv->is_track()) {
2351 speed = rtv->track()->speed();
2354 framepos_t where = get_preferred_edit_position();
2358 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
2360 align_region (rv.region(), SyncPoint, (framepos_t) (where * speed));
2362 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
2364 align_region (rv.region(), End, (framepos_t) (where * speed));
2368 align_region (rv.region(), Start, (framepos_t) (where * speed));
2375 Editor::collect_new_region_view (RegionView* rv)
2377 latest_regionviews.push_back (rv);
2381 Editor::collect_and_select_new_region_view (RegionView* rv)
2384 latest_regionviews.push_back (rv);
2388 Editor::cancel_selection ()
2390 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2391 (*i)->hide_selection ();
2394 selection->clear ();
2395 clicked_selection = 0;
2399 Editor::cancel_time_selection ()
2401 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2402 (*i)->hide_selection ();
2404 selection->time.clear ();
2405 clicked_selection = 0;
2409 Editor::point_trim (GdkEvent* event, framepos_t new_bound)
2411 RegionView* rv = clicked_regionview;
2413 /* Choose action dependant on which button was pressed */
2414 switch (event->button.button) {
2416 begin_reversible_command (_("start point trim"));
2418 if (selection->selected (rv)) {
2419 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
2420 i != selection->regions.by_layer().end(); ++i)
2422 if (!(*i)->region()->locked()) {
2423 (*i)->region()->clear_changes ();
2424 (*i)->region()->trim_front (new_bound);
2425 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2430 if (!rv->region()->locked()) {
2431 rv->region()->clear_changes ();
2432 rv->region()->trim_front (new_bound);
2433 _session->add_command(new StatefulDiffCommand (rv->region()));
2437 commit_reversible_command();
2441 begin_reversible_command (_("End point trim"));
2443 if (selection->selected (rv)) {
2445 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
2447 if (!(*i)->region()->locked()) {
2448 (*i)->region()->clear_changes();
2449 (*i)->region()->trim_end (new_bound);
2450 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2456 if (!rv->region()->locked()) {
2457 rv->region()->clear_changes ();
2458 rv->region()->trim_end (new_bound);
2459 _session->add_command (new StatefulDiffCommand (rv->region()));
2463 commit_reversible_command();
2472 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* /*event*/)
2477 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2478 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2482 Location* location = find_location_from_marker (marker, is_start);
2483 location->set_hidden (true, this);
2488 Editor::reposition_zoom_rect (framepos_t start, framepos_t end)
2490 double x1 = frame_to_pixel (start);
2491 double x2 = frame_to_pixel (end);
2492 double y2 = full_canvas_height - 1.0;
2494 zoom_rect->property_x1() = x1;
2495 zoom_rect->property_y1() = 1.0;
2496 zoom_rect->property_x2() = x2;
2497 zoom_rect->property_y2() = y2;
2502 Editor::mouse_rename_region (ArdourCanvas::Item* /*item*/, GdkEvent* /*event*/)
2504 using namespace Gtkmm2ext;
2506 ArdourPrompter prompter (false);
2508 prompter.set_prompt (_("Name for region:"));
2509 prompter.set_initial_text (clicked_regionview->region()->name());
2510 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
2511 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
2512 prompter.show_all ();
2513 switch (prompter.run ()) {
2514 case Gtk::RESPONSE_ACCEPT:
2516 prompter.get_result(str);
2518 clicked_regionview->region()->set_name (str);
2527 Editor::mouse_brush_insert_region (RegionView* rv, framepos_t pos)
2529 /* no brushing without a useful snap setting */
2531 switch (_snap_mode) {
2533 return; /* can't work because it allows region to be placed anywhere */
2538 switch (_snap_type) {
2546 /* don't brush a copy over the original */
2548 if (pos == rv->region()->position()) {
2552 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
2554 if (rtv == 0 || !rtv->is_track()) {
2558 boost::shared_ptr<Playlist> playlist = rtv->playlist();
2559 double speed = rtv->track()->speed();
2561 playlist->clear_changes ();
2562 boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region(), true));
2563 playlist->add_region (new_region, (framepos_t) (pos * speed));
2564 _session->add_command (new StatefulDiffCommand (playlist));
2566 // playlist is frozen, so we have to update manually XXX this is disgusting
2568 playlist->RegionAdded (new_region); /* EMIT SIGNAL */
2572 Editor::track_height_step_timeout ()
2574 if (get_microseconds() - last_track_height_step_timestamp < 250000) {
2575 current_stepping_trackview = 0;
2582 Editor::add_region_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2584 assert (region_view);
2586 if (!region_view->region()->playlist()) {
2590 _region_motion_group->raise_to_top ();
2592 if (Config->get_edit_mode() == Splice) {
2593 _drags->add (new RegionSpliceDrag (this, item, region_view, selection->regions.by_layer()));
2595 _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), false, false));
2598 /* sync the canvas to what we think is its current state */
2599 update_canvas_now();
2603 Editor::add_region_copy_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2605 assert (region_view);
2607 if (!region_view->region()->playlist()) {
2611 _region_motion_group->raise_to_top ();
2613 _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), false, true));
2617 Editor::add_region_brush_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2619 assert (region_view);
2621 if (!region_view->region()->playlist()) {
2625 if (Config->get_edit_mode() == Splice) {
2629 _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), true, false));
2631 begin_reversible_command (Operations::drag_region_brush);
2634 /** Start a grab where a time range is selected, track(s) are selected, and the
2635 * user clicks and drags a region with a modifier in order to create a new region containing
2636 * the section of the clicked region that lies within the time range.
2639 Editor::start_selection_grab (ArdourCanvas::Item* /*item*/, GdkEvent* event)
2641 if (clicked_regionview == 0) {
2645 /* lets try to create new Region for the selection */
2647 vector<boost::shared_ptr<Region> > new_regions;
2648 create_region_from_selection (new_regions);
2650 if (new_regions.empty()) {
2654 /* XXX fix me one day to use all new regions */
2656 boost::shared_ptr<Region> region (new_regions.front());
2658 /* add it to the current stream/playlist.
2660 tricky: the streamview for the track will add a new regionview. we will
2661 catch the signal it sends when it creates the regionview to
2662 set the regionview we want to then drag.
2665 latest_regionviews.clear();
2666 sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
2668 /* A selection grab currently creates two undo/redo operations, one for
2669 creating the new region and another for moving it.
2672 begin_reversible_command (Operations::selection_grab);
2674 boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
2676 playlist->clear_changes ();
2677 clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
2678 _session->add_command(new StatefulDiffCommand (playlist));
2680 commit_reversible_command ();
2684 if (latest_regionviews.empty()) {
2685 /* something went wrong */
2689 /* we need to deselect all other regionviews, and select this one
2690 i'm ignoring undo stuff, because the region creation will take care of it
2692 selection->set (latest_regionviews);
2694 _drags->set (new RegionMoveDrag (this, latest_regionviews.front()->get_canvas_group(), latest_regionviews.front(), latest_regionviews, false, false), event);
2700 if (_drags->active ()) {
2703 selection->clear ();
2708 Editor::set_internal_edit (bool yn)
2710 if (_internal_editing == yn) {
2714 _internal_editing = yn;
2717 pre_internal_mouse_mode = mouse_mode;
2718 pre_internal_snap_type = _snap_type;
2719 pre_internal_snap_mode = _snap_mode;
2721 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2722 (*i)->enter_internal_edit_mode ();
2725 set_snap_to (internal_snap_type);
2726 set_snap_mode (internal_snap_mode);
2730 internal_snap_mode = _snap_mode;
2731 internal_snap_type = _snap_type;
2733 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2734 (*i)->leave_internal_edit_mode ();
2737 if (mouse_mode == MouseDraw && pre_internal_mouse_mode != MouseDraw) {
2738 /* we were drawing .. flip back to something sensible */
2739 set_mouse_mode (pre_internal_mouse_mode);
2742 set_snap_to (pre_internal_snap_type);
2743 set_snap_mode (pre_internal_snap_mode);
2746 set_canvas_cursor ();
2749 /** Update _join_object_range_state which indicate whether we are over the top or bottom half of a region view,
2750 * used by the `join object/range' tool mode.
2753 Editor::update_join_object_range_location (double /*x*/, double y)
2755 /* XXX: actually, this decides based on whether the mouse is in the top
2756 or bottom half of a the waveform part RouteTimeAxisView;
2758 Note that entered_{track,regionview} is not always setup (e.g. if
2759 the mouse is over a TimeSelection), and to get a Region
2760 that we're over requires searching the playlist.
2763 if ( !get_smart_mode() ) {
2764 _join_object_range_state = JOIN_OBJECT_RANGE_NONE;
2768 if (mouse_mode == MouseObject) {
2769 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2770 } else if (mouse_mode == MouseRange) {
2771 _join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
2774 /* XXX: maybe we should make entered_track work in all cases, rather than resorting to this */
2775 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y + vertical_adjustment.get_value() - canvas_timebars_vsize);
2779 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
2784 rtv->canvas_display()->w2i (cx, cy);
2786 double const c = cy / (rtv->view()->child_height() - TimeAxisViewItem::NAME_HIGHLIGHT_SIZE);
2788 _join_object_range_state = c <= 0.5 ? JOIN_OBJECT_RANGE_RANGE : JOIN_OBJECT_RANGE_OBJECT;
2794 Editor::effective_mouse_mode () const
2796 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
2798 } else if (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE) {
2806 Editor::remove_midi_note (ArdourCanvas::Item* item, GdkEvent *)
2808 ArdourCanvas::CanvasNoteEvent* e = dynamic_cast<ArdourCanvas::CanvasNoteEvent*> (item);
2811 e->region_view().delete_note (e->note ());
2815 Editor::set_canvas_cursor_for_region_view (double x, RegionView* rv)
2819 ArdourCanvas::Group* g = rv->get_canvas_group ();
2820 ArdourCanvas::Group* p = g->get_parent_group ();
2822 /* Compute x in region view parent coordinates */
2826 double x1, x2, y1, y2;
2827 g->get_bounds (x1, y1, x2, y2);
2829 /* Halfway across the region */
2830 double const h = (x1 + x2) / 2;
2832 Trimmable::CanTrim ct = rv->region()->can_trim ();
2834 if (ct & Trimmable::FrontTrimEarlier) {
2835 set_canvas_cursor (_cursors->left_side_trim);
2837 set_canvas_cursor (_cursors->left_side_trim_right_only);
2840 if (ct & Trimmable::EndTrimLater) {
2841 set_canvas_cursor (_cursors->right_side_trim);
2843 set_canvas_cursor (_cursors->right_side_trim_left_only);
2848 /** Obtain the pointer position in world coordinates */
2850 Editor::get_pointer_position (double& x, double& y) const
2853 track_canvas->get_pointer (px, py);
2854 track_canvas->window_to_world (px, py, x, y);