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) {
1272 if (event->type == GDK_2BUTTON_PRESS) {
1273 gdk_pointer_ungrab (GDK_CURRENT_TIME);
1274 return button_double_click_handler (item, event, item_type);
1279 Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->track_canvas->get_window();
1281 if (canvas_window) {
1282 Glib::RefPtr<const Gdk::Window> pointer_window;
1285 Gdk::ModifierType mask;
1287 pointer_window = canvas_window->get_pointer (x, y, mask);
1289 if (pointer_window == track_canvas->get_bin_window()) {
1290 track_canvas->window_to_world (x, y, wx, wy);
1294 pre_press_cursor = current_canvas_cursor;
1296 track_canvas->grab_focus();
1298 if (_session && _session->actively_recording()) {
1302 if (internal_editing()) {
1303 bool leave_internal_edit_mode = false;
1305 switch (item_type) {
1310 if (!dynamic_cast<MidiRegionView*> (clicked_regionview) && !dynamic_cast<AutomationRegionView*> (clicked_regionview)) {
1311 leave_internal_edit_mode = true;
1315 case PlayheadCursorItem:
1317 case TempoMarkerItem:
1318 case MeterMarkerItem:
1322 case RangeMarkerBarItem:
1323 case CdMarkerBarItem:
1324 case TransportMarkerBarItem:
1326 /* button press on these events never does anything to
1327 change the editing mode.
1335 if (leave_internal_edit_mode) {
1336 ActionManager::do_action ("MouseMode", "toggle-internal-edit");
1340 button_selection (item, event, item_type);
1342 if (!_drags->active () &&
1343 (Keyboard::is_delete_event (&event->button) ||
1344 Keyboard::is_context_menu_event (&event->button) ||
1345 Keyboard::is_edit_event (&event->button))) {
1347 /* handled by button release */
1351 //not rolling, range mode click + join_play_range : locate the PH here
1352 if ( !_drags->active () && !_session->transport_rolling() && ( effective_mouse_mode() == MouseRange ) && Config->get_always_play_range() ) {
1353 framepos_t where = event_frame (event, 0, 0);
1355 _session->request_locate (where, false);
1358 switch (event->button.button) {
1360 return button_press_handler_1 (item, event, item_type);
1364 return button_press_handler_2 (item, event, item_type);
1371 return button_press_dispatch (&event->button);
1380 Editor::button_press_dispatch (GdkEventButton* ev)
1382 /* this function is intended only for buttons 4 and above.
1385 Gtkmm2ext::MouseButton b (ev->state, ev->button);
1386 return button_bindings->activate (b, Gtkmm2ext::Bindings::Press);
1390 Editor::button_release_dispatch (GdkEventButton* ev)
1392 /* this function is intended only for buttons 4 and above.
1395 Gtkmm2ext::MouseButton b (ev->state, ev->button);
1396 return button_bindings->activate (b, Gtkmm2ext::Bindings::Release);
1400 Editor::button_double_click_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type) {
1402 if (event->button.button != 1) {
1406 switch (item_type) {
1409 rv = clicked_regionview;
1410 rv->show_region_editor ();
1413 case PlayheadCursorItem:
1416 case RangeMarkerBarItem:
1417 case CdMarkerBarItem:
1419 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1422 rename_marker (marker);
1424 case TempoMarkerItem:
1425 edit_tempo_marker (item);
1427 case MeterMarkerItem:
1428 edit_meter_marker (item);
1433 case TransportMarkerBarItem:
1446 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1448 framepos_t where = event_frame (event, 0, 0);
1449 AutomationTimeAxisView* atv = 0;
1451 if (pre_press_cursor) {
1452 set_canvas_cursor (pre_press_cursor);
1453 pre_press_cursor = 0;
1456 /* no action if we're recording */
1458 if (_session && _session->actively_recording()) {
1462 /* see if we're finishing a drag */
1464 bool were_dragging = false;
1465 if (_drags->active ()) {
1466 bool const r = _drags->end_grab (event);
1468 /* grab dragged, so do nothing else */
1472 were_dragging = true;
1475 update_region_layering_order_editor ();
1477 /* edit events get handled here */
1479 if (!_drags->active () && Keyboard::is_edit_event (&event->button)) {
1480 switch (item_type) {
1482 show_region_properties ();
1485 case TempoMarkerItem:
1486 edit_tempo_marker (item);
1489 case MeterMarkerItem:
1490 edit_meter_marker (item);
1493 case RegionViewName:
1494 if (clicked_regionview->name_active()) {
1495 return mouse_rename_region (item, event);
1499 case ControlPointItem:
1500 edit_control_point (item);
1509 /* context menu events get handled here */
1510 if (Keyboard::is_context_menu_event (&event->button)) {
1512 context_click_event = *event;
1514 if (!_drags->active ()) {
1516 /* no matter which button pops up the context menu, tell the menu
1517 widget to use button 1 to drive menu selection.
1520 switch (item_type) {
1522 case FadeInHandleItem:
1524 case FadeOutHandleItem:
1525 popup_fade_context_menu (1, event->button.time, item, item_type);
1528 case StartCrossFadeItem:
1529 popup_xfade_in_context_menu (1, event->button.time, item, item_type);
1532 case EndCrossFadeItem:
1533 popup_xfade_out_context_menu (1, event->button.time, item, item_type);
1537 popup_track_context_menu (1, event->button.time, item_type, false);
1541 case RegionViewNameHighlight:
1542 case LeftFrameHandle:
1543 case RightFrameHandle:
1544 case RegionViewName:
1545 popup_track_context_menu (1, event->button.time, item_type, false);
1549 popup_track_context_menu (1, event->button.time, item_type, true);
1552 case AutomationTrackItem:
1553 popup_track_context_menu (1, event->button.time, item_type, false);
1557 case RangeMarkerBarItem:
1558 case TransportMarkerBarItem:
1559 case CdMarkerBarItem:
1563 popup_ruler_menu (where, item_type);
1567 marker_context_menu (&event->button, item);
1570 case TempoMarkerItem:
1571 tempo_or_meter_marker_context_menu (&event->button, item);
1574 case MeterMarkerItem:
1575 tempo_or_meter_marker_context_menu (&event->button, item);
1578 case CrossfadeViewItem:
1579 popup_track_context_menu (1, event->button.time, item_type, false);
1582 case ControlPointItem:
1583 popup_control_point_context_menu (item, event);
1594 /* delete events get handled here */
1596 Editing::MouseMode const eff = effective_mouse_mode ();
1598 if (!_drags->active () && Keyboard::is_delete_event (&event->button)) {
1600 switch (item_type) {
1601 case TempoMarkerItem:
1602 remove_tempo_marker (item);
1605 case MeterMarkerItem:
1606 remove_meter_marker (item);
1610 remove_marker (*item, event);
1614 if (eff == MouseObject) {
1615 remove_clicked_region ();
1619 case ControlPointItem:
1620 remove_control_point (item);
1624 remove_midi_note (item, event);
1633 switch (event->button.button) {
1636 switch (item_type) {
1637 /* see comments in button_press_handler */
1638 case PlayheadCursorItem:
1641 case AutomationLineItem:
1642 case StartSelectionTrimItem:
1643 case EndSelectionTrimItem:
1647 if (!_dragging_playhead) {
1648 snap_to_with_modifier (where, event, 0, true);
1649 mouse_add_new_marker (where);
1653 case CdMarkerBarItem:
1654 if (!_dragging_playhead) {
1655 // if we get here then a dragged range wasn't done
1656 snap_to_with_modifier (where, event, 0, true);
1657 mouse_add_new_marker (where, true);
1662 if (!_dragging_playhead) {
1663 snap_to_with_modifier (where, event);
1664 mouse_add_new_tempo_event (where);
1669 if (!_dragging_playhead) {
1670 mouse_add_new_meter_event (pixel_to_frame (event->button.x));
1681 switch (item_type) {
1682 case AutomationTrackItem:
1683 atv = dynamic_cast<AutomationTimeAxisView*>(clicked_axisview);
1685 atv->add_automation_event (event, where, event->button.y);
1695 switch (item_type) {
1698 /* check that we didn't drag before releasing, since
1699 its really annoying to create new control
1700 points when doing this.
1702 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (clicked_regionview);
1703 if (!were_dragging && arv) {
1704 arv->add_gain_point_event (item, event);
1710 case AutomationTrackItem:
1711 dynamic_cast<AutomationTimeAxisView*>(clicked_axisview)->
1712 add_automation_event (event, where, event->button.y);
1721 set_canvas_cursor (current_canvas_cursor);
1722 if (scrubbing_direction == 0) {
1723 /* no drag, just a click */
1724 switch (item_type) {
1726 play_selected_region ();
1732 /* make sure we stop */
1733 _session->request_transport_speed (0.0);
1742 /* do any (de)selection operations that should occur on button release */
1743 button_selection (item, event, item_type);
1752 switch (item_type) {
1754 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1756 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier|Keyboard::SecondaryModifier))) {
1759 // Button2 click is unused
1774 // x_style_paste (where, 1.0);
1795 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1802 switch (item_type) {
1803 case ControlPointItem:
1804 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1805 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1806 cp->set_visible (true);
1810 at_y = cp->get_y ();
1811 cp->i2w (at_x, at_y);
1815 fraction = 1.0 - (cp->get_y() / cp->line().height());
1817 if (is_drawable() && !_drags->active ()) {
1818 set_canvas_cursor (_cursors->fader);
1821 _verbose_cursor->set (cp->line().get_verbose_cursor_string (fraction), at_x, at_y);
1822 _verbose_cursor->show ();
1827 if (mouse_mode == MouseGain) {
1828 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1830 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredGainLine.get();
1831 if (is_drawable()) {
1832 set_canvas_cursor (_cursors->fader);
1837 case AutomationLineItem:
1838 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1839 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1841 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredAutomationLine.get();
1843 if (is_drawable()) {
1844 set_canvas_cursor (_cursors->fader);
1849 case RegionViewNameHighlight:
1850 if (is_drawable() && effective_mouse_mode() == MouseObject && entered_regionview) {
1851 set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1852 _over_region_trim_target = true;
1856 case LeftFrameHandle:
1857 case RightFrameHandle:
1858 if (is_drawable() && effective_mouse_mode() == MouseObject && !internal_editing() && entered_regionview) {
1859 set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1863 case StartSelectionTrimItem:
1864 if (is_drawable()) {
1865 set_canvas_cursor (_cursors->left_side_trim);
1868 case EndSelectionTrimItem:
1869 if (is_drawable()) {
1870 set_canvas_cursor (_cursors->right_side_trim);
1874 case PlayheadCursorItem:
1875 if (is_drawable()) {
1876 switch (_edit_point) {
1878 set_canvas_cursor (_cursors->grabber_edit_point);
1881 set_canvas_cursor (_cursors->grabber);
1887 case RegionViewName:
1889 /* when the name is not an active item, the entire name highlight is for trimming */
1891 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1892 if (mouse_mode == MouseObject && is_drawable()) {
1893 set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1894 _over_region_trim_target = true;
1900 case AutomationTrackItem:
1901 if (is_drawable()) {
1902 Gdk::Cursor *cursor;
1903 switch (mouse_mode) {
1905 cursor = _cursors->selector;
1908 cursor = _cursors->zoom_in;
1911 cursor = _cursors->cross_hair;
1915 set_canvas_cursor (cursor);
1917 AutomationTimeAxisView* atv;
1918 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1919 clear_entered_track = false;
1920 set_entered_track (atv);
1926 case RangeMarkerBarItem:
1927 case TransportMarkerBarItem:
1928 case CdMarkerBarItem:
1931 if (is_drawable()) {
1932 set_canvas_cursor (_cursors->timebar);
1937 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1940 entered_marker = marker;
1941 marker->set_color_rgba (ARDOUR_UI::config()->canvasvar_EnteredMarker.get());
1943 case MeterMarkerItem:
1944 case TempoMarkerItem:
1945 if (is_drawable()) {
1946 set_canvas_cursor (_cursors->timebar);
1950 case FadeInHandleItem:
1951 if (mouse_mode == MouseObject && !internal_editing()) {
1952 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1954 rect->property_fill_color_rgba() = 0xBBBBBBAA;
1956 set_canvas_cursor (_cursors->fade_in);
1960 case FadeOutHandleItem:
1961 if (mouse_mode == MouseObject && !internal_editing()) {
1962 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1964 rect->property_fill_color_rgba() = 0xBBBBBBAA;
1966 set_canvas_cursor (_cursors->fade_out);
1969 case FeatureLineItem:
1971 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1972 line->property_fill_color_rgba() = 0xFF0000FF;
1976 if ( get_smart_mode() ) {
1977 set_canvas_cursor ();
1985 /* second pass to handle entered track status in a comprehensible way.
1988 switch (item_type) {
1990 case AutomationLineItem:
1991 case ControlPointItem:
1992 /* these do not affect the current entered track state */
1993 clear_entered_track = false;
1996 case AutomationTrackItem:
1997 /* handled above already */
2001 set_entered_track (0);
2009 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent*, ItemType item_type)
2019 switch (item_type) {
2020 case ControlPointItem:
2021 cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
2022 if (cp->line().the_list()->interpolation() != AutomationList::Discrete) {
2023 if (cp->line().npoints() > 1 && !cp->get_selected()) {
2024 cp->set_visible (false);
2028 if (is_drawable()) {
2029 set_canvas_cursor (current_canvas_cursor);
2032 _verbose_cursor->hide ();
2035 case RegionViewNameHighlight:
2036 case LeftFrameHandle:
2037 case RightFrameHandle:
2038 case StartSelectionTrimItem:
2039 case EndSelectionTrimItem:
2040 case PlayheadCursorItem:
2042 _over_region_trim_target = false;
2044 if (is_drawable()) {
2045 set_canvas_cursor (current_canvas_cursor);
2050 case AutomationLineItem:
2051 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
2053 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
2055 line->property_fill_color_rgba() = al->get_line_color();
2057 if (is_drawable()) {
2058 set_canvas_cursor (current_canvas_cursor);
2062 case RegionViewName:
2063 /* see enter_handler() for notes */
2064 _over_region_trim_target = false;
2066 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
2067 if (is_drawable() && mouse_mode == MouseObject) {
2068 set_canvas_cursor (current_canvas_cursor);
2073 case RangeMarkerBarItem:
2074 case TransportMarkerBarItem:
2075 case CdMarkerBarItem:
2079 if (is_drawable()) {
2080 set_canvas_cursor (current_canvas_cursor);
2085 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2089 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
2090 location_flags_changed (loc, this);
2093 case MeterMarkerItem:
2094 case TempoMarkerItem:
2096 if (is_drawable()) {
2097 set_canvas_cursor (current_canvas_cursor);
2102 case FadeInHandleItem:
2103 case FadeOutHandleItem:
2104 rv = static_cast<RegionView*>(item->get_data ("regionview"));
2106 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
2108 rect->property_fill_color_rgba() = rv->get_fill_color();
2111 set_canvas_cursor (current_canvas_cursor);
2114 case AutomationTrackItem:
2115 if (is_drawable()) {
2116 set_canvas_cursor (current_canvas_cursor);
2117 clear_entered_track = true;
2118 Glib::signal_idle().connect (sigc::mem_fun(*this, &Editor::left_automation_track));
2121 case FeatureLineItem:
2123 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
2124 line->property_fill_color_rgba() = (guint) ARDOUR_UI::config()->canvasvar_ZeroLine.get();;
2136 Editor::left_automation_track ()
2138 if (clear_entered_track) {
2139 set_entered_track (0);
2140 clear_entered_track = false;
2146 Editor::scrub (framepos_t frame, double current_x)
2150 if (scrubbing_direction == 0) {
2152 _session->request_locate (frame, false);
2153 _session->request_transport_speed (0.1);
2154 scrubbing_direction = 1;
2158 if (last_scrub_x > current_x) {
2160 /* pointer moved to the left */
2162 if (scrubbing_direction > 0) {
2164 /* we reversed direction to go backwards */
2167 scrub_reverse_distance += (int) (last_scrub_x - current_x);
2171 /* still moving to the left (backwards) */
2173 scrub_reversals = 0;
2174 scrub_reverse_distance = 0;
2176 delta = 0.01 * (last_scrub_x - current_x);
2177 _session->request_transport_speed_nonzero (_session->transport_speed() - delta);
2181 /* pointer moved to the right */
2183 if (scrubbing_direction < 0) {
2184 /* we reversed direction to go forward */
2187 scrub_reverse_distance += (int) (current_x - last_scrub_x);
2190 /* still moving to the right */
2192 scrub_reversals = 0;
2193 scrub_reverse_distance = 0;
2195 delta = 0.01 * (current_x - last_scrub_x);
2196 _session->request_transport_speed_nonzero (_session->transport_speed() + delta);
2200 /* if there have been more than 2 opposite motion moves detected, or one that moves
2201 back more than 10 pixels, reverse direction
2204 if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
2206 if (scrubbing_direction > 0) {
2207 /* was forwards, go backwards */
2208 _session->request_transport_speed (-0.1);
2209 scrubbing_direction = -1;
2211 /* was backwards, go forwards */
2212 _session->request_transport_speed (0.1);
2213 scrubbing_direction = 1;
2216 scrub_reverse_distance = 0;
2217 scrub_reversals = 0;
2221 last_scrub_x = current_x;
2225 Editor::motion_handler (ArdourCanvas::Item* /*item*/, GdkEvent* event, bool from_autoscroll)
2227 _last_motion_y = event->motion.y;
2229 if (event->motion.is_hint) {
2232 /* We call this so that MOTION_NOTIFY events continue to be
2233 delivered to the canvas. We need to do this because we set
2234 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
2235 the density of the events, at the expense of a round-trip
2236 to the server. Given that this will mostly occur on cases
2237 where DISPLAY = :0.0, and given the cost of what the motion
2238 event might do, its a good tradeoff.
2241 track_canvas->get_pointer (x, y);
2244 if (current_stepping_trackview) {
2245 /* don't keep the persistent stepped trackview if the mouse moves */
2246 current_stepping_trackview = 0;
2247 step_timeout.disconnect ();
2250 if (_session && _session->actively_recording()) {
2251 /* Sorry. no dragging stuff around while we record */
2255 JoinObjectRangeState const old = _join_object_range_state;
2256 update_join_object_range_location (event->motion.x, event->motion.y);
2258 if (!_internal_editing && _join_object_range_state != old) {
2259 set_canvas_cursor ();
2262 if (!_internal_editing && _over_region_trim_target) {
2263 set_canvas_cursor_for_region_view (event->motion.x, entered_regionview);
2266 bool handled = false;
2267 if (_drags->active ()) {
2268 handled = _drags->motion_handler (event, from_autoscroll);
2275 track_canvas_motion (event);
2280 Editor::can_remove_control_point (ArdourCanvas::Item* item)
2282 ControlPoint* control_point;
2284 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2285 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2289 AutomationLine& line = control_point->line ();
2290 if (dynamic_cast<AudioRegionGainLine*> (&line)) {
2291 /* we shouldn't remove the first or last gain point in region gain lines */
2292 if (line.is_last_point(*control_point) || line.is_first_point(*control_point)) {
2301 Editor::remove_control_point (ArdourCanvas::Item* item)
2303 if (!can_remove_control_point (item)) {
2307 ControlPoint* control_point;
2309 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2310 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2314 control_point->line().remove_point (*control_point);
2318 Editor::edit_control_point (ArdourCanvas::Item* item)
2320 ControlPoint* p = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"));
2323 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2327 ControlPointDialog d (p);
2330 if (d.run () != RESPONSE_ACCEPT) {
2334 p->line().modify_point_y (*p, d.get_y_fraction ());
2338 Editor::edit_notes (TimeAxisViewItem& tavi)
2340 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(&tavi);
2346 MidiRegionView::Selection const & s = mrv->selection();
2352 EditNoteDialog* d = new EditNoteDialog (&(*s.begin())->region_view(), s);
2356 d->signal_response().connect (sigc::bind (sigc::mem_fun (*this, &Editor::note_edit_done), d));
2360 Editor::note_edit_done (int r, EditNoteDialog* d)
2367 Editor::visible_order_range (int* low, int* high) const
2369 *low = TimeAxisView::max_order ();
2372 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
2374 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
2376 if (!rtv->hidden()) {
2378 if (*high < rtv->order()) {
2379 *high = rtv->order ();
2382 if (*low > rtv->order()) {
2383 *low = rtv->order ();
2390 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
2392 /* Either add to or set the set the region selection, unless
2393 this is an alignment click (control used)
2396 if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
2397 TimeAxisView* tv = &rv.get_time_axis_view();
2398 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
2400 if (rtv && rtv->is_track()) {
2401 speed = rtv->track()->speed();
2404 framepos_t where = get_preferred_edit_position();
2408 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
2410 align_region (rv.region(), SyncPoint, (framepos_t) (where * speed));
2412 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
2414 align_region (rv.region(), End, (framepos_t) (where * speed));
2418 align_region (rv.region(), Start, (framepos_t) (where * speed));
2425 Editor::collect_new_region_view (RegionView* rv)
2427 latest_regionviews.push_back (rv);
2431 Editor::collect_and_select_new_region_view (RegionView* rv)
2434 latest_regionviews.push_back (rv);
2438 Editor::cancel_selection ()
2440 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2441 (*i)->hide_selection ();
2444 selection->clear ();
2445 clicked_selection = 0;
2449 Editor::cancel_time_selection ()
2451 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2452 (*i)->hide_selection ();
2454 selection->time.clear ();
2455 clicked_selection = 0;
2459 Editor::point_trim (GdkEvent* event, framepos_t new_bound)
2461 RegionView* rv = clicked_regionview;
2463 /* Choose action dependant on which button was pressed */
2464 switch (event->button.button) {
2466 begin_reversible_command (_("start point trim"));
2468 if (selection->selected (rv)) {
2469 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
2470 i != selection->regions.by_layer().end(); ++i)
2472 if (!(*i)->region()->locked()) {
2473 (*i)->region()->clear_changes ();
2474 (*i)->region()->trim_front (new_bound);
2475 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2480 if (!rv->region()->locked()) {
2481 rv->region()->clear_changes ();
2482 rv->region()->trim_front (new_bound);
2483 _session->add_command(new StatefulDiffCommand (rv->region()));
2487 commit_reversible_command();
2491 begin_reversible_command (_("End point trim"));
2493 if (selection->selected (rv)) {
2495 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
2497 if (!(*i)->region()->locked()) {
2498 (*i)->region()->clear_changes();
2499 (*i)->region()->trim_end (new_bound);
2500 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2506 if (!rv->region()->locked()) {
2507 rv->region()->clear_changes ();
2508 rv->region()->trim_end (new_bound);
2509 _session->add_command (new StatefulDiffCommand (rv->region()));
2513 commit_reversible_command();
2522 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* /*event*/)
2527 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2528 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2532 Location* location = find_location_from_marker (marker, is_start);
2533 location->set_hidden (true, this);
2538 Editor::reposition_zoom_rect (framepos_t start, framepos_t end)
2540 double x1 = frame_to_pixel (start);
2541 double x2 = frame_to_pixel (end);
2542 double y2 = full_canvas_height - 1.0;
2544 zoom_rect->property_x1() = x1;
2545 zoom_rect->property_y1() = 1.0;
2546 zoom_rect->property_x2() = x2;
2547 zoom_rect->property_y2() = y2;
2552 Editor::mouse_rename_region (ArdourCanvas::Item* /*item*/, GdkEvent* /*event*/)
2554 using namespace Gtkmm2ext;
2556 ArdourPrompter prompter (false);
2558 prompter.set_prompt (_("Name for region:"));
2559 prompter.set_initial_text (clicked_regionview->region()->name());
2560 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
2561 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
2562 prompter.show_all ();
2563 switch (prompter.run ()) {
2564 case Gtk::RESPONSE_ACCEPT:
2566 prompter.get_result(str);
2568 clicked_regionview->region()->set_name (str);
2577 Editor::mouse_brush_insert_region (RegionView* rv, framepos_t pos)
2579 /* no brushing without a useful snap setting */
2581 switch (_snap_mode) {
2583 return; /* can't work because it allows region to be placed anywhere */
2588 switch (_snap_type) {
2596 /* don't brush a copy over the original */
2598 if (pos == rv->region()->position()) {
2602 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
2604 if (rtv == 0 || !rtv->is_track()) {
2608 boost::shared_ptr<Playlist> playlist = rtv->playlist();
2609 double speed = rtv->track()->speed();
2611 playlist->clear_changes ();
2612 boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region(), true));
2613 playlist->add_region (new_region, (framepos_t) (pos * speed));
2614 _session->add_command (new StatefulDiffCommand (playlist));
2616 // playlist is frozen, so we have to update manually XXX this is disgusting
2618 playlist->RegionAdded (new_region); /* EMIT SIGNAL */
2622 Editor::track_height_step_timeout ()
2624 if (get_microseconds() - last_track_height_step_timestamp < 250000) {
2625 current_stepping_trackview = 0;
2632 Editor::add_region_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2634 assert (region_view);
2636 if (!region_view->region()->playlist()) {
2640 _region_motion_group->raise_to_top ();
2642 if (Config->get_edit_mode() == Splice) {
2643 _drags->add (new RegionSpliceDrag (this, item, region_view, selection->regions.by_layer()));
2645 _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), false, false));
2648 /* sync the canvas to what we think is its current state */
2649 update_canvas_now();
2653 Editor::add_region_copy_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2655 assert (region_view);
2657 if (!region_view->region()->playlist()) {
2661 _region_motion_group->raise_to_top ();
2663 _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), false, true));
2667 Editor::add_region_brush_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2669 assert (region_view);
2671 if (!region_view->region()->playlist()) {
2675 if (Config->get_edit_mode() == Splice) {
2679 _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), true, false));
2681 begin_reversible_command (Operations::drag_region_brush);
2684 /** Start a grab where a time range is selected, track(s) are selected, and the
2685 * user clicks and drags a region with a modifier in order to create a new region containing
2686 * the section of the clicked region that lies within the time range.
2689 Editor::start_selection_grab (ArdourCanvas::Item* /*item*/, GdkEvent* event)
2691 if (clicked_regionview == 0) {
2695 /* lets try to create new Region for the selection */
2697 vector<boost::shared_ptr<Region> > new_regions;
2698 create_region_from_selection (new_regions);
2700 if (new_regions.empty()) {
2704 /* XXX fix me one day to use all new regions */
2706 boost::shared_ptr<Region> region (new_regions.front());
2708 /* add it to the current stream/playlist.
2710 tricky: the streamview for the track will add a new regionview. we will
2711 catch the signal it sends when it creates the regionview to
2712 set the regionview we want to then drag.
2715 latest_regionviews.clear();
2716 sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
2718 /* A selection grab currently creates two undo/redo operations, one for
2719 creating the new region and another for moving it.
2722 begin_reversible_command (Operations::selection_grab);
2724 boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
2726 playlist->clear_changes ();
2727 clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
2728 _session->add_command(new StatefulDiffCommand (playlist));
2730 commit_reversible_command ();
2734 if (latest_regionviews.empty()) {
2735 /* something went wrong */
2739 /* we need to deselect all other regionviews, and select this one
2740 i'm ignoring undo stuff, because the region creation will take care of it
2742 selection->set (latest_regionviews);
2744 _drags->set (new RegionMoveDrag (this, latest_regionviews.front()->get_canvas_group(), latest_regionviews.front(), latest_regionviews, false, false), event);
2750 if (_drags->active ()) {
2753 selection->clear ();
2758 Editor::set_internal_edit (bool yn)
2760 if (_internal_editing == yn) {
2764 _internal_editing = yn;
2767 pre_internal_mouse_mode = mouse_mode;
2768 pre_internal_snap_type = _snap_type;
2769 pre_internal_snap_mode = _snap_mode;
2771 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2772 (*i)->enter_internal_edit_mode ();
2775 set_snap_to (internal_snap_type);
2776 set_snap_mode (internal_snap_mode);
2780 internal_snap_mode = _snap_mode;
2781 internal_snap_type = _snap_type;
2783 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2784 (*i)->leave_internal_edit_mode ();
2787 if (mouse_mode == MouseDraw && pre_internal_mouse_mode != MouseDraw) {
2788 /* we were drawing .. flip back to something sensible */
2789 set_mouse_mode (pre_internal_mouse_mode);
2792 set_snap_to (pre_internal_snap_type);
2793 set_snap_mode (pre_internal_snap_mode);
2796 set_canvas_cursor ();
2799 /** Update _join_object_range_state which indicate whether we are over the top or bottom half of a region view,
2800 * used by the `join object/range' tool mode.
2803 Editor::update_join_object_range_location (double /*x*/, double y)
2805 /* XXX: actually, this decides based on whether the mouse is in the top
2806 or bottom half of a the waveform part RouteTimeAxisView;
2808 Note that entered_{track,regionview} is not always setup (e.g. if
2809 the mouse is over a TimeSelection), and to get a Region
2810 that we're over requires searching the playlist.
2813 if ( !get_smart_mode() ) {
2814 _join_object_range_state = JOIN_OBJECT_RANGE_NONE;
2818 if (mouse_mode == MouseObject) {
2819 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2820 } else if (mouse_mode == MouseRange) {
2821 _join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
2824 /* XXX: maybe we should make entered_track work in all cases, rather than resorting to this */
2825 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y + vertical_adjustment.get_value() - canvas_timebars_vsize);
2829 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
2834 rtv->canvas_display()->w2i (cx, cy);
2836 double const c = cy / (rtv->view()->child_height() - TimeAxisViewItem::NAME_HIGHLIGHT_SIZE);
2838 _join_object_range_state = c <= 0.5 ? JOIN_OBJECT_RANGE_RANGE : JOIN_OBJECT_RANGE_OBJECT;
2844 Editor::effective_mouse_mode () const
2846 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
2848 } else if (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE) {
2856 Editor::remove_midi_note (ArdourCanvas::Item* item, GdkEvent *)
2858 ArdourCanvas::CanvasNoteEvent* e = dynamic_cast<ArdourCanvas::CanvasNoteEvent*> (item);
2861 e->region_view().delete_note (e->note ());
2865 Editor::set_canvas_cursor_for_region_view (double x, RegionView* rv)
2869 ArdourCanvas::Group* g = rv->get_canvas_group ();
2870 ArdourCanvas::Group* p = g->get_parent_group ();
2872 /* Compute x in region view parent coordinates */
2876 double x1, x2, y1, y2;
2877 g->get_bounds (x1, y1, x2, y2);
2879 /* Halfway across the region */
2880 double const h = (x1 + x2) / 2;
2882 Trimmable::CanTrim ct = rv->region()->can_trim ();
2884 if (ct & Trimmable::FrontTrimEarlier) {
2885 set_canvas_cursor (_cursors->left_side_trim);
2887 set_canvas_cursor (_cursors->left_side_trim_right_only);
2890 if (ct & Trimmable::EndTrimLater) {
2891 set_canvas_cursor (_cursors->right_side_trim);
2893 set_canvas_cursor (_cursors->right_side_trim_left_only);
2898 /** Obtain the pointer position in world coordinates */
2900 Editor::get_pointer_position (double& x, double& y) const
2903 track_canvas->get_pointer (px, py);
2904 track_canvas->window_to_world (px, py, x, y);