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_OBJECT)) {
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 RegionSelection s = get_equivalent_regions (selection->regions, Properties::select.property_id);
865 _drags->set (new TrimDrag (this, item, clicked_regionview, s.by_layer()), event);
871 if (!internal_editing()) {
872 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::RangeSelectModifier)) {
873 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionExtend), event);
875 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
885 if (internal_editing()) {
886 /* trim notes if we're in internal edit mode and near the ends of the note */
887 ArdourCanvas::CanvasNote* cn = dynamic_cast<ArdourCanvas::CanvasNote*> (item);
888 if (cn && cn->big_enough_to_trim() && cn->mouse_near_ends()) {
889 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
891 _drags->set (new NoteDrag (this, item), event);
897 if (internal_editing()) {
898 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
899 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
913 if (internal_editing()) {
914 ArdourCanvas::CanvasNoteEvent* cn = dynamic_cast<ArdourCanvas::CanvasNoteEvent*> (item);
915 if (cn->mouse_near_ends()) {
916 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
918 _drags->set (new NoteDrag (this, item), event);
928 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)) &&
929 event->type == GDK_BUTTON_PRESS) {
931 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
933 } else if (event->type == GDK_BUTTON_PRESS) {
936 case FadeInHandleItem:
938 RegionSelection s = get_equivalent_regions (selection->regions, Properties::select.property_id);
939 _drags->set (new FadeInDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), s), event, _cursors->fade_in);
943 case FadeOutHandleItem:
945 RegionSelection s = get_equivalent_regions (selection->regions, Properties::select.property_id);
946 _drags->set (new FadeOutDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), s), event, _cursors->fade_out);
950 case StartCrossFadeItem:
951 case EndCrossFadeItem:
952 /* we might allow user to grab inside the fade to trim a region with preserve_fade_anchor. for not this is not fully implemented */
953 // if (!clicked_regionview->region()->locked()) {
954 // RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
955 // _drags->set (new TrimDrag (this, item, clicked_regionview, s.by_layer(), true), event);
960 case FeatureLineItem:
962 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier)) {
963 remove_transient(item);
967 _drags->set (new FeatureLineDrag (this, item), event);
973 if (dynamic_cast<AutomationRegionView*> (clicked_regionview)) {
974 /* click on an automation region view; do nothing here and let the ARV's signal handler
980 if (internal_editing ()) {
981 if (event->type == GDK_2BUTTON_PRESS && event->button.button == 1) {
982 Glib::RefPtr<Action> act = ActionManager::get_action (X_("MouseMode"), X_("toggle-internal-edit"));
988 /* click on a normal region view */
989 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
990 add_region_copy_drag (item, event, clicked_regionview);
991 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
992 add_region_brush_drag (item, event, clicked_regionview);
994 add_region_drag (item, event, clicked_regionview);
998 // if (!internal_editing() && (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE && !selection->regions.empty())) {
999 // _drags->add (new SelectionDrag (this, clicked_axisview->get_selection_rect (clicked_selection)->rect, SelectionDrag::SelectionMove));
1002 _drags->start_grab (event);
1005 case RegionViewNameHighlight:
1006 case LeftFrameHandle:
1007 case RightFrameHandle:
1008 if (!clicked_regionview->region()->locked()) {
1009 RegionSelection s = get_equivalent_regions (selection->regions, Properties::select.property_id);
1010 _drags->set (new TrimDrag (this, item, clicked_regionview, s.by_layer()), event);
1015 case RegionViewName:
1017 /* rename happens on edit clicks */
1018 RegionSelection s = get_equivalent_regions (selection->regions, Properties::select.property_id);
1019 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, s.by_layer()), event);
1024 case ControlPointItem:
1025 _drags->set (new ControlPointDrag (this, item), event);
1029 case AutomationLineItem:
1030 _drags->set (new LineDrag (this, item), event);
1035 if (internal_editing()) {
1036 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
1037 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
1041 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
1045 case AutomationTrackItem:
1047 TimeAxisView* parent = clicked_axisview->get_parent ();
1048 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (clicked_axisview);
1050 if (parent && dynamic_cast<MidiTimeAxisView*> (parent) && atv->show_regions ()) {
1052 RouteTimeAxisView* p = dynamic_cast<RouteTimeAxisView*> (parent);
1054 boost::shared_ptr<Playlist> pl = p->track()->playlist ();
1055 if (pl->n_regions() == 0) {
1056 /* Parent has no regions; create one so that we have somewhere to put automation */
1057 _drags->set (new RegionCreateDrag (this, item, parent), event);
1059 /* See if there's a region before the click that we can extend, and extend it if so */
1060 framepos_t const t = event_frame (event);
1061 boost::shared_ptr<Region> prev = pl->find_next_region (t, End, -1);
1063 _drags->set (new RegionCreateDrag (this, item, parent), event);
1065 prev->set_length (t - prev->position ());
1069 /* rubberband drag to select automation points */
1070 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
1077 if ( get_smart_mode() ) {
1078 /* we're in "smart" joined mode, and we've clicked on a Selection */
1079 double const y = event->button.y + vertical_adjustment.get_value() - canvas_timebars_vsize;
1080 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
1082 /* if we're over an automation track, start a drag of its data */
1083 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
1085 _drags->set (new AutomationRangeDrag (this, atv, selection->time), event, _cursors->up_down);
1088 /* if we're over a track and a region, and in the `object' part of a region,
1089 put a selection around the region and drag both
1091 /* RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1092 if (rtv && _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
1093 boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track> (rtv->route ());
1095 boost::shared_ptr<Playlist> pl = t->playlist ();
1098 boost::shared_ptr<Region> r = pl->top_region_at (event_frame (event));
1100 RegionView* rv = rtv->view()->find_view (r);
1101 clicked_selection = select_range (rv->region()->position(),
1102 rv->region()->last_frame()+1);
1103 _drags->add (new SelectionDrag (this, item, SelectionDrag::SelectionMove));
1104 list<RegionView*> rvs;
1106 _drags->add (new RegionMoveDrag (this, item, rv, rvs, false, false));
1107 _drags->start_grab (event);
1130 switch (item_type) {
1132 _drags->set (new LineDrag (this, item), event);
1135 case ControlPointItem:
1136 _drags->set (new ControlPointDrag (this, item), event);
1142 AudioRegionView* arv = dynamic_cast<AudioRegionView *> (clicked_regionview);
1144 _drags->set (new AutomationRangeDrag (this, arv, selection->time), event, _cursors->up_down);
1145 _drags->start_grab (event);
1151 case AutomationLineItem:
1152 _drags->set (new LineDrag (this, item), event);
1162 if (event->type == GDK_BUTTON_PRESS) {
1163 _drags->set (new MouseZoomDrag (this, item), event);
1170 if (internal_editing() && item_type == NoteItem) {
1171 /* drag notes if we're in internal edit mode */
1172 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
1174 } else if (clicked_regionview) {
1176 _drags->set (new TimeFXDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1182 _drags->set (new ScrubDrag (this, item), event);
1183 scrub_reversals = 0;
1184 scrub_reverse_distance = 0;
1185 last_scrub_x = event->button.x;
1186 scrubbing_direction = 0;
1187 set_canvas_cursor (_cursors->transparent);
1199 Editor::button_press_handler_2 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1201 Editing::MouseMode const eff = effective_mouse_mode ();
1204 switch (item_type) {
1206 if (internal_editing ()) {
1207 /* no region drags in internal edit mode */
1211 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
1212 add_region_copy_drag (item, event, clicked_regionview);
1214 add_region_drag (item, event, clicked_regionview);
1216 _drags->start_grab (event);
1219 case ControlPointItem:
1220 _drags->set (new ControlPointDrag (this, item), event);
1228 switch (item_type) {
1229 case RegionViewNameHighlight:
1230 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1234 case LeftFrameHandle:
1235 case RightFrameHandle:
1236 if (!internal_editing ()) {
1237 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1242 case RegionViewName:
1243 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer()), event);
1257 /* relax till release */
1263 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1264 temporal_zoom_to_frame (false, event_frame (event));
1266 temporal_zoom_to_frame (true, event_frame(event));
1279 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1281 if (event->type != GDK_BUTTON_PRESS) {
1285 Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->track_canvas->get_window();
1287 if (canvas_window) {
1288 Glib::RefPtr<const Gdk::Window> pointer_window;
1291 Gdk::ModifierType mask;
1293 pointer_window = canvas_window->get_pointer (x, y, mask);
1295 if (pointer_window == track_canvas->get_bin_window()) {
1296 track_canvas->window_to_world (x, y, wx, wy);
1300 pre_press_cursor = current_canvas_cursor;
1302 track_canvas->grab_focus();
1304 if (_session && _session->actively_recording()) {
1308 if (internal_editing()) {
1309 bool leave_internal_edit_mode = false;
1311 switch (item_type) {
1316 if (!dynamic_cast<MidiRegionView*> (clicked_regionview) && !dynamic_cast<AutomationRegionView*> (clicked_regionview)) {
1317 leave_internal_edit_mode = true;
1321 case PlayheadCursorItem:
1323 case TempoMarkerItem:
1324 case MeterMarkerItem:
1328 case RangeMarkerBarItem:
1329 case CdMarkerBarItem:
1330 case TransportMarkerBarItem:
1332 /* button press on these events never does anything to
1333 change the editing mode.
1341 if (leave_internal_edit_mode) {
1342 ActionManager::do_action ("MouseMode", "toggle-internal-edit");
1346 button_selection (item, event, item_type);
1348 if (!_drags->active () &&
1349 (Keyboard::is_delete_event (&event->button) ||
1350 Keyboard::is_context_menu_event (&event->button) ||
1351 Keyboard::is_edit_event (&event->button))) {
1353 /* handled by button release */
1357 //not rolling, range mode click + join_play_range : locate the PH here
1358 if ( !_drags->active () && !_session->transport_rolling() && ( effective_mouse_mode() == MouseRange ) && Config->get_always_play_range() ) {
1359 framepos_t where = event_frame (event, 0, 0);
1361 _session->request_locate (where, false);
1364 switch (event->button.button) {
1366 return button_press_handler_1 (item, event, item_type);
1370 return button_press_handler_2 (item, event, item_type);
1377 return button_press_dispatch (&event->button);
1386 Editor::button_press_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::Press);
1396 Editor::button_release_dispatch (GdkEventButton* ev)
1398 /* this function is intended only for buttons 4 and above.
1401 Gtkmm2ext::MouseButton b (ev->state, ev->button);
1402 return button_bindings->activate (b, Gtkmm2ext::Bindings::Release);
1406 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1408 framepos_t where = event_frame (event, 0, 0);
1409 AutomationTimeAxisView* atv = 0;
1411 if (pre_press_cursor) {
1412 set_canvas_cursor (pre_press_cursor);
1413 pre_press_cursor = 0;
1416 /* no action if we're recording */
1418 if (_session && _session->actively_recording()) {
1422 /* see if we're finishing a drag */
1424 bool were_dragging = false;
1425 if (_drags->active ()) {
1426 bool const r = _drags->end_grab (event);
1428 /* grab dragged, so do nothing else */
1432 were_dragging = true;
1435 update_region_layering_order_editor ();
1437 /* edit events get handled here */
1439 if (!_drags->active () && Keyboard::is_edit_event (&event->button)) {
1440 switch (item_type) {
1442 show_region_properties ();
1445 case TempoMarkerItem:
1446 edit_tempo_marker (item);
1449 case MeterMarkerItem:
1450 edit_meter_marker (item);
1453 case RegionViewName:
1454 if (clicked_regionview->name_active()) {
1455 return mouse_rename_region (item, event);
1459 case ControlPointItem:
1460 edit_control_point (item);
1465 ArdourCanvas::CanvasNoteEvent* e = dynamic_cast<ArdourCanvas::CanvasNoteEvent*> (item);
1467 edit_notes (e->region_view().selection ());
1477 /* context menu events get handled here */
1478 if (Keyboard::is_context_menu_event (&event->button)) {
1480 context_click_event = *event;
1482 if (!_drags->active ()) {
1484 /* no matter which button pops up the context menu, tell the menu
1485 widget to use button 1 to drive menu selection.
1488 switch (item_type) {
1490 case FadeInHandleItem:
1492 case FadeOutHandleItem:
1493 popup_fade_context_menu (1, event->button.time, item, item_type);
1496 case StartCrossFadeItem:
1497 popup_xfade_in_context_menu (1, event->button.time, item, item_type);
1500 case EndCrossFadeItem:
1501 popup_xfade_out_context_menu (1, event->button.time, item, item_type);
1505 popup_track_context_menu (1, event->button.time, item_type, false);
1509 case RegionViewNameHighlight:
1510 case LeftFrameHandle:
1511 case RightFrameHandle:
1512 case RegionViewName:
1513 popup_track_context_menu (1, event->button.time, item_type, false);
1517 popup_track_context_menu (1, event->button.time, item_type, true);
1520 case AutomationTrackItem:
1521 popup_track_context_menu (1, event->button.time, item_type, false);
1525 case RangeMarkerBarItem:
1526 case TransportMarkerBarItem:
1527 case CdMarkerBarItem:
1531 popup_ruler_menu (where, item_type);
1535 marker_context_menu (&event->button, item);
1538 case TempoMarkerItem:
1539 tempo_or_meter_marker_context_menu (&event->button, item);
1542 case MeterMarkerItem:
1543 tempo_or_meter_marker_context_menu (&event->button, item);
1546 case CrossfadeViewItem:
1547 popup_track_context_menu (1, event->button.time, item_type, false);
1550 case ControlPointItem:
1551 popup_control_point_context_menu (item, event);
1562 /* delete events get handled here */
1564 Editing::MouseMode const eff = effective_mouse_mode ();
1566 if (!_drags->active () && Keyboard::is_delete_event (&event->button)) {
1568 switch (item_type) {
1569 case TempoMarkerItem:
1570 remove_tempo_marker (item);
1573 case MeterMarkerItem:
1574 remove_meter_marker (item);
1578 remove_marker (*item, event);
1582 if (eff == MouseObject) {
1583 remove_clicked_region ();
1587 case ControlPointItem:
1588 remove_control_point (item);
1592 remove_midi_note (item, event);
1601 switch (event->button.button) {
1604 switch (item_type) {
1605 /* see comments in button_press_handler */
1606 case PlayheadCursorItem:
1609 case AutomationLineItem:
1610 case StartSelectionTrimItem:
1611 case EndSelectionTrimItem:
1615 if (!_dragging_playhead) {
1616 snap_to_with_modifier (where, event, 0, true);
1617 mouse_add_new_marker (where);
1621 case CdMarkerBarItem:
1622 if (!_dragging_playhead) {
1623 // if we get here then a dragged range wasn't done
1624 snap_to_with_modifier (where, event, 0, true);
1625 mouse_add_new_marker (where, true);
1630 if (!_dragging_playhead) {
1631 snap_to_with_modifier (where, event);
1632 mouse_add_new_tempo_event (where);
1637 if (!_dragging_playhead) {
1638 mouse_add_new_meter_event (pixel_to_frame (event->button.x));
1649 switch (item_type) {
1650 case AutomationTrackItem:
1651 atv = dynamic_cast<AutomationTimeAxisView*>(clicked_axisview);
1653 atv->add_automation_event (event, where, event->button.y);
1663 switch (item_type) {
1666 /* check that we didn't drag before releasing, since
1667 its really annoying to create new control
1668 points when doing this.
1670 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (clicked_regionview);
1671 if (!were_dragging && arv) {
1672 arv->add_gain_point_event (item, event);
1678 case AutomationTrackItem:
1679 dynamic_cast<AutomationTimeAxisView*>(clicked_axisview)->
1680 add_automation_event (event, where, event->button.y);
1689 set_canvas_cursor (current_canvas_cursor);
1690 if (scrubbing_direction == 0) {
1691 /* no drag, just a click */
1692 switch (item_type) {
1694 play_selected_region ();
1700 /* make sure we stop */
1701 _session->request_transport_speed (0.0);
1710 /* do any (de)selection operations that should occur on button release */
1711 button_selection (item, event, item_type);
1720 switch (item_type) {
1722 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1724 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier|Keyboard::SecondaryModifier))) {
1727 // Button2 click is unused
1742 // x_style_paste (where, 1.0);
1763 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1770 switch (item_type) {
1771 case ControlPointItem:
1772 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1773 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1774 cp->set_visible (true);
1778 at_y = cp->get_y ();
1779 cp->i2w (at_x, at_y);
1783 fraction = 1.0 - (cp->get_y() / cp->line().height());
1785 if (is_drawable() && !_drags->active ()) {
1786 set_canvas_cursor (_cursors->fader);
1789 _verbose_cursor->set (cp->line().get_verbose_cursor_string (fraction), at_x, at_y);
1790 _verbose_cursor->show ();
1795 if (mouse_mode == MouseGain) {
1796 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1798 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredGainLine.get();
1799 if (is_drawable()) {
1800 set_canvas_cursor (_cursors->fader);
1805 case AutomationLineItem:
1806 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1807 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1809 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredAutomationLine.get();
1811 if (is_drawable()) {
1812 set_canvas_cursor (_cursors->fader);
1817 case RegionViewNameHighlight:
1818 if (is_drawable() && effective_mouse_mode() == MouseObject && entered_regionview) {
1819 set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1820 _over_region_trim_target = true;
1824 case LeftFrameHandle:
1825 case RightFrameHandle:
1826 if (is_drawable() && effective_mouse_mode() == MouseObject && !internal_editing() && entered_regionview) {
1827 set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1831 case StartSelectionTrimItem:
1832 if (is_drawable()) {
1833 set_canvas_cursor (_cursors->left_side_trim);
1836 case EndSelectionTrimItem:
1837 if (is_drawable()) {
1838 set_canvas_cursor (_cursors->right_side_trim);
1842 case PlayheadCursorItem:
1843 if (is_drawable()) {
1844 switch (_edit_point) {
1846 set_canvas_cursor (_cursors->grabber_edit_point);
1849 set_canvas_cursor (_cursors->grabber);
1855 case RegionViewName:
1857 /* when the name is not an active item, the entire name highlight is for trimming */
1859 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1860 if (mouse_mode == MouseObject && is_drawable()) {
1861 set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1862 _over_region_trim_target = true;
1868 case AutomationTrackItem:
1869 if (is_drawable()) {
1870 Gdk::Cursor *cursor;
1871 switch (mouse_mode) {
1873 cursor = _cursors->selector;
1876 cursor = _cursors->zoom_in;
1879 cursor = _cursors->cross_hair;
1883 set_canvas_cursor (cursor);
1885 AutomationTimeAxisView* atv;
1886 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1887 clear_entered_track = false;
1888 set_entered_track (atv);
1894 case RangeMarkerBarItem:
1895 case TransportMarkerBarItem:
1896 case CdMarkerBarItem:
1899 if (is_drawable()) {
1900 set_canvas_cursor (_cursors->timebar);
1905 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1908 entered_marker = marker;
1909 marker->set_color_rgba (ARDOUR_UI::config()->canvasvar_EnteredMarker.get());
1911 case MeterMarkerItem:
1912 case TempoMarkerItem:
1913 if (is_drawable()) {
1914 set_canvas_cursor (_cursors->timebar);
1918 case FadeInHandleItem:
1919 if (mouse_mode == MouseObject && !internal_editing()) {
1920 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1922 rect->property_fill_color_rgba() = 0xBBBBBBAA;
1924 set_canvas_cursor (_cursors->fade_in);
1928 case FadeOutHandleItem:
1929 if (mouse_mode == MouseObject && !internal_editing()) {
1930 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1932 rect->property_fill_color_rgba() = 0xBBBBBBAA;
1934 set_canvas_cursor (_cursors->fade_out);
1937 case FeatureLineItem:
1939 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1940 line->property_fill_color_rgba() = 0xFF0000FF;
1944 if ( get_smart_mode() ) {
1945 set_canvas_cursor ();
1953 /* second pass to handle entered track status in a comprehensible way.
1956 switch (item_type) {
1958 case AutomationLineItem:
1959 case ControlPointItem:
1960 /* these do not affect the current entered track state */
1961 clear_entered_track = false;
1964 case AutomationTrackItem:
1965 /* handled above already */
1969 set_entered_track (0);
1977 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent*, ItemType item_type)
1987 switch (item_type) {
1988 case ControlPointItem:
1989 cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
1990 if (cp->line().the_list()->interpolation() != AutomationList::Discrete) {
1991 if (cp->line().npoints() > 1 && !cp->get_selected()) {
1992 cp->set_visible (false);
1996 if (is_drawable()) {
1997 set_canvas_cursor (current_canvas_cursor);
2000 _verbose_cursor->hide ();
2003 case RegionViewNameHighlight:
2004 case LeftFrameHandle:
2005 case RightFrameHandle:
2006 case StartSelectionTrimItem:
2007 case EndSelectionTrimItem:
2008 case PlayheadCursorItem:
2010 _over_region_trim_target = false;
2012 if (is_drawable()) {
2013 set_canvas_cursor (current_canvas_cursor);
2018 case AutomationLineItem:
2019 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
2021 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
2023 line->property_fill_color_rgba() = al->get_line_color();
2025 if (is_drawable()) {
2026 set_canvas_cursor (current_canvas_cursor);
2030 case RegionViewName:
2031 /* see enter_handler() for notes */
2032 _over_region_trim_target = false;
2034 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
2035 if (is_drawable() && mouse_mode == MouseObject) {
2036 set_canvas_cursor (current_canvas_cursor);
2041 case RangeMarkerBarItem:
2042 case TransportMarkerBarItem:
2043 case CdMarkerBarItem:
2047 if (is_drawable()) {
2048 set_canvas_cursor (current_canvas_cursor);
2053 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2057 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
2058 location_flags_changed (loc, this);
2061 case MeterMarkerItem:
2062 case TempoMarkerItem:
2064 if (is_drawable()) {
2065 set_canvas_cursor (current_canvas_cursor);
2070 case FadeInHandleItem:
2071 case FadeOutHandleItem:
2072 rv = static_cast<RegionView*>(item->get_data ("regionview"));
2074 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
2076 rect->property_fill_color_rgba() = rv->get_fill_color();
2079 set_canvas_cursor (current_canvas_cursor);
2082 case AutomationTrackItem:
2083 if (is_drawable()) {
2084 set_canvas_cursor (current_canvas_cursor);
2085 clear_entered_track = true;
2086 Glib::signal_idle().connect (sigc::mem_fun(*this, &Editor::left_automation_track));
2089 case FeatureLineItem:
2091 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
2092 line->property_fill_color_rgba() = (guint) ARDOUR_UI::config()->canvasvar_ZeroLine.get();;
2104 Editor::left_automation_track ()
2106 if (clear_entered_track) {
2107 set_entered_track (0);
2108 clear_entered_track = false;
2114 Editor::scrub (framepos_t frame, double current_x)
2118 if (scrubbing_direction == 0) {
2120 _session->request_locate (frame, false);
2121 _session->request_transport_speed (0.1);
2122 scrubbing_direction = 1;
2126 if (last_scrub_x > current_x) {
2128 /* pointer moved to the left */
2130 if (scrubbing_direction > 0) {
2132 /* we reversed direction to go backwards */
2135 scrub_reverse_distance += (int) (last_scrub_x - current_x);
2139 /* still moving to the left (backwards) */
2141 scrub_reversals = 0;
2142 scrub_reverse_distance = 0;
2144 delta = 0.01 * (last_scrub_x - current_x);
2145 _session->request_transport_speed_nonzero (_session->transport_speed() - delta);
2149 /* pointer moved to the right */
2151 if (scrubbing_direction < 0) {
2152 /* we reversed direction to go forward */
2155 scrub_reverse_distance += (int) (current_x - last_scrub_x);
2158 /* still moving to the right */
2160 scrub_reversals = 0;
2161 scrub_reverse_distance = 0;
2163 delta = 0.01 * (current_x - last_scrub_x);
2164 _session->request_transport_speed_nonzero (_session->transport_speed() + delta);
2168 /* if there have been more than 2 opposite motion moves detected, or one that moves
2169 back more than 10 pixels, reverse direction
2172 if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
2174 if (scrubbing_direction > 0) {
2175 /* was forwards, go backwards */
2176 _session->request_transport_speed (-0.1);
2177 scrubbing_direction = -1;
2179 /* was backwards, go forwards */
2180 _session->request_transport_speed (0.1);
2181 scrubbing_direction = 1;
2184 scrub_reverse_distance = 0;
2185 scrub_reversals = 0;
2189 last_scrub_x = current_x;
2193 Editor::motion_handler (ArdourCanvas::Item* /*item*/, GdkEvent* event, bool from_autoscroll)
2195 _last_motion_y = event->motion.y;
2197 if (event->motion.is_hint) {
2200 /* We call this so that MOTION_NOTIFY events continue to be
2201 delivered to the canvas. We need to do this because we set
2202 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
2203 the density of the events, at the expense of a round-trip
2204 to the server. Given that this will mostly occur on cases
2205 where DISPLAY = :0.0, and given the cost of what the motion
2206 event might do, its a good tradeoff.
2209 track_canvas->get_pointer (x, y);
2212 if (current_stepping_trackview) {
2213 /* don't keep the persistent stepped trackview if the mouse moves */
2214 current_stepping_trackview = 0;
2215 step_timeout.disconnect ();
2218 if (_session && _session->actively_recording()) {
2219 /* Sorry. no dragging stuff around while we record */
2223 JoinObjectRangeState const old = _join_object_range_state;
2224 update_join_object_range_location (event->motion.x, event->motion.y);
2226 if (!_internal_editing && _join_object_range_state != old) {
2227 set_canvas_cursor ();
2230 if (!_internal_editing && _over_region_trim_target) {
2231 set_canvas_cursor_for_region_view (event->motion.x, entered_regionview);
2234 bool handled = false;
2235 if (_drags->active ()) {
2236 handled = _drags->motion_handler (event, from_autoscroll);
2243 track_canvas_motion (event);
2248 Editor::can_remove_control_point (ArdourCanvas::Item* item)
2250 ControlPoint* control_point;
2252 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2253 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2257 AutomationLine& line = control_point->line ();
2258 if (dynamic_cast<AudioRegionGainLine*> (&line)) {
2259 /* we shouldn't remove the first or last gain point in region gain lines */
2260 if (line.is_last_point(*control_point) || line.is_first_point(*control_point)) {
2269 Editor::remove_control_point (ArdourCanvas::Item* item)
2271 if (!can_remove_control_point (item)) {
2275 ControlPoint* control_point;
2277 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2278 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2282 control_point->line().remove_point (*control_point);
2286 Editor::edit_control_point (ArdourCanvas::Item* item)
2288 ControlPoint* p = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"));
2291 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2295 ControlPointDialog d (p);
2296 d.set_position (Gtk::WIN_POS_MOUSE);
2299 if (d.run () != RESPONSE_ACCEPT) {
2303 p->line().modify_point_y (*p, d.get_y_fraction ());
2307 Editor::edit_notes (MidiRegionView::Selection const & s)
2313 EditNoteDialog d (&(*s.begin())->region_view(), s);
2314 d.set_position (Gtk::WIN_POS_MOUSE);
2322 Editor::visible_order_range (int* low, int* high) const
2324 *low = TimeAxisView::max_order ();
2327 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
2329 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
2331 if (!rtv->hidden()) {
2333 if (*high < rtv->order()) {
2334 *high = rtv->order ();
2337 if (*low > rtv->order()) {
2338 *low = rtv->order ();
2345 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
2347 /* Either add to or set the set the region selection, unless
2348 this is an alignment click (control used)
2351 if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
2352 TimeAxisView* tv = &rv.get_time_axis_view();
2353 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
2355 if (rtv && rtv->is_track()) {
2356 speed = rtv->track()->speed();
2359 framepos_t where = get_preferred_edit_position();
2363 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
2365 align_region (rv.region(), SyncPoint, (framepos_t) (where * speed));
2367 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
2369 align_region (rv.region(), End, (framepos_t) (where * speed));
2373 align_region (rv.region(), Start, (framepos_t) (where * speed));
2380 Editor::collect_new_region_view (RegionView* rv)
2382 latest_regionviews.push_back (rv);
2386 Editor::collect_and_select_new_region_view (RegionView* rv)
2389 latest_regionviews.push_back (rv);
2393 Editor::cancel_selection ()
2395 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2396 (*i)->hide_selection ();
2399 selection->clear ();
2400 clicked_selection = 0;
2404 Editor::cancel_time_selection ()
2406 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2407 (*i)->hide_selection ();
2409 selection->time.clear ();
2410 clicked_selection = 0;
2414 Editor::point_trim (GdkEvent* event, framepos_t new_bound)
2416 RegionView* rv = clicked_regionview;
2418 /* Choose action dependant on which button was pressed */
2419 switch (event->button.button) {
2421 begin_reversible_command (_("start point trim"));
2423 if (selection->selected (rv)) {
2424 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
2425 i != selection->regions.by_layer().end(); ++i)
2427 if (!(*i)->region()->locked()) {
2428 (*i)->region()->clear_changes ();
2429 (*i)->region()->trim_front (new_bound);
2430 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2435 if (!rv->region()->locked()) {
2436 rv->region()->clear_changes ();
2437 rv->region()->trim_front (new_bound);
2438 _session->add_command(new StatefulDiffCommand (rv->region()));
2442 commit_reversible_command();
2446 begin_reversible_command (_("End point trim"));
2448 if (selection->selected (rv)) {
2450 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
2452 if (!(*i)->region()->locked()) {
2453 (*i)->region()->clear_changes();
2454 (*i)->region()->trim_end (new_bound);
2455 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2461 if (!rv->region()->locked()) {
2462 rv->region()->clear_changes ();
2463 rv->region()->trim_end (new_bound);
2464 _session->add_command (new StatefulDiffCommand (rv->region()));
2468 commit_reversible_command();
2477 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* /*event*/)
2482 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2483 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2487 Location* location = find_location_from_marker (marker, is_start);
2488 location->set_hidden (true, this);
2493 Editor::reposition_zoom_rect (framepos_t start, framepos_t end)
2495 double x1 = frame_to_pixel (start);
2496 double x2 = frame_to_pixel (end);
2497 double y2 = full_canvas_height - 1.0;
2499 zoom_rect->property_x1() = x1;
2500 zoom_rect->property_y1() = 1.0;
2501 zoom_rect->property_x2() = x2;
2502 zoom_rect->property_y2() = y2;
2507 Editor::mouse_rename_region (ArdourCanvas::Item* /*item*/, GdkEvent* /*event*/)
2509 using namespace Gtkmm2ext;
2511 ArdourPrompter prompter (false);
2513 prompter.set_prompt (_("Name for region:"));
2514 prompter.set_initial_text (clicked_regionview->region()->name());
2515 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
2516 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
2517 prompter.show_all ();
2518 switch (prompter.run ()) {
2519 case Gtk::RESPONSE_ACCEPT:
2521 prompter.get_result(str);
2523 clicked_regionview->region()->set_name (str);
2532 Editor::mouse_brush_insert_region (RegionView* rv, framepos_t pos)
2534 /* no brushing without a useful snap setting */
2536 switch (_snap_mode) {
2538 return; /* can't work because it allows region to be placed anywhere */
2543 switch (_snap_type) {
2551 /* don't brush a copy over the original */
2553 if (pos == rv->region()->position()) {
2557 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
2559 if (rtv == 0 || !rtv->is_track()) {
2563 boost::shared_ptr<Playlist> playlist = rtv->playlist();
2564 double speed = rtv->track()->speed();
2566 playlist->clear_changes ();
2567 boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region(), true));
2568 playlist->add_region (new_region, (framepos_t) (pos * speed));
2569 _session->add_command (new StatefulDiffCommand (playlist));
2571 // playlist is frozen, so we have to update manually XXX this is disgusting
2573 playlist->RegionAdded (new_region); /* EMIT SIGNAL */
2577 Editor::track_height_step_timeout ()
2579 if (get_microseconds() - last_track_height_step_timestamp < 250000) {
2580 current_stepping_trackview = 0;
2587 Editor::add_region_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2589 assert (region_view);
2591 if (!region_view->region()->playlist()) {
2595 _region_motion_group->raise_to_top ();
2597 if (Config->get_edit_mode() == Splice) {
2598 _drags->add (new RegionSpliceDrag (this, item, region_view, selection->regions.by_layer()));
2600 RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::select.property_id);
2601 _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), false, false));
2604 /* sync the canvas to what we think is its current state */
2605 update_canvas_now();
2609 Editor::add_region_copy_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2611 assert (region_view);
2613 if (!region_view->region()->playlist()) {
2617 _region_motion_group->raise_to_top ();
2619 RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::select.property_id);
2620 _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), false, true));
2624 Editor::add_region_brush_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2626 assert (region_view);
2628 if (!region_view->region()->playlist()) {
2632 if (Config->get_edit_mode() == Splice) {
2636 RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::select.property_id);
2637 _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), true, false));
2639 begin_reversible_command (Operations::drag_region_brush);
2642 /** Start a grab where a time range is selected, track(s) are selected, and the
2643 * user clicks and drags a region with a modifier in order to create a new region containing
2644 * the section of the clicked region that lies within the time range.
2647 Editor::start_selection_grab (ArdourCanvas::Item* /*item*/, GdkEvent* event)
2649 if (clicked_regionview == 0) {
2653 /* lets try to create new Region for the selection */
2655 vector<boost::shared_ptr<Region> > new_regions;
2656 create_region_from_selection (new_regions);
2658 if (new_regions.empty()) {
2662 /* XXX fix me one day to use all new regions */
2664 boost::shared_ptr<Region> region (new_regions.front());
2666 /* add it to the current stream/playlist.
2668 tricky: the streamview for the track will add a new regionview. we will
2669 catch the signal it sends when it creates the regionview to
2670 set the regionview we want to then drag.
2673 latest_regionviews.clear();
2674 sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
2676 /* A selection grab currently creates two undo/redo operations, one for
2677 creating the new region and another for moving it.
2680 begin_reversible_command (Operations::selection_grab);
2682 boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
2684 playlist->clear_changes ();
2685 clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
2686 _session->add_command(new StatefulDiffCommand (playlist));
2688 commit_reversible_command ();
2692 if (latest_regionviews.empty()) {
2693 /* something went wrong */
2697 /* we need to deselect all other regionviews, and select this one
2698 i'm ignoring undo stuff, because the region creation will take care of it
2700 selection->set (latest_regionviews);
2702 _drags->set (new RegionMoveDrag (this, latest_regionviews.front()->get_canvas_group(), latest_regionviews.front(), latest_regionviews, false, false), event);
2708 if (_drags->active ()) {
2711 selection->clear ();
2716 Editor::set_internal_edit (bool yn)
2718 if (_internal_editing == yn) {
2722 _internal_editing = yn;
2725 pre_internal_mouse_mode = mouse_mode;
2726 pre_internal_snap_type = _snap_type;
2727 pre_internal_snap_mode = _snap_mode;
2729 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2730 (*i)->enter_internal_edit_mode ();
2733 set_snap_to (internal_snap_type);
2734 set_snap_mode (internal_snap_mode);
2738 internal_snap_mode = _snap_mode;
2739 internal_snap_type = _snap_type;
2741 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2742 (*i)->leave_internal_edit_mode ();
2745 if (mouse_mode == MouseDraw && pre_internal_mouse_mode != MouseDraw) {
2746 /* we were drawing .. flip back to something sensible */
2747 set_mouse_mode (pre_internal_mouse_mode);
2750 set_snap_to (pre_internal_snap_type);
2751 set_snap_mode (pre_internal_snap_mode);
2754 set_canvas_cursor ();
2757 /** Update _join_object_range_state which indicate whether we are over the top or bottom half of a region view,
2758 * used by the `join object/range' tool mode.
2761 Editor::update_join_object_range_location (double /*x*/, double y)
2763 /* XXX: actually, this decides based on whether the mouse is in the top
2764 or bottom half of a the waveform part RouteTimeAxisView;
2766 Note that entered_{track,regionview} is not always setup (e.g. if
2767 the mouse is over a TimeSelection), and to get a Region
2768 that we're over requires searching the playlist.
2771 if ( !get_smart_mode() ) {
2772 _join_object_range_state = JOIN_OBJECT_RANGE_NONE;
2776 if (mouse_mode == MouseObject) {
2777 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2778 } else if (mouse_mode == MouseRange) {
2779 _join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
2782 /* XXX: maybe we should make entered_track work in all cases, rather than resorting to this */
2783 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y + vertical_adjustment.get_value() - canvas_timebars_vsize);
2787 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
2792 rtv->canvas_display()->w2i (cx, cy);
2794 double const c = cy / (rtv->view()->child_height() - TimeAxisViewItem::NAME_HIGHLIGHT_SIZE);
2796 _join_object_range_state = c <= 0.5 ? JOIN_OBJECT_RANGE_RANGE : JOIN_OBJECT_RANGE_OBJECT;
2802 Editor::effective_mouse_mode () const
2804 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
2806 } else if (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE) {
2814 Editor::remove_midi_note (ArdourCanvas::Item* item, GdkEvent *)
2816 ArdourCanvas::CanvasNoteEvent* e = dynamic_cast<ArdourCanvas::CanvasNoteEvent*> (item);
2819 e->region_view().delete_note (e->note ());
2823 Editor::set_canvas_cursor_for_region_view (double x, RegionView* rv)
2827 ArdourCanvas::Group* g = rv->get_canvas_group ();
2828 ArdourCanvas::Group* p = g->get_parent_group ();
2830 /* Compute x in region view parent coordinates */
2834 double x1, x2, y1, y2;
2835 g->get_bounds (x1, y1, x2, y2);
2837 /* Halfway across the region */
2838 double const h = (x1 + x2) / 2;
2840 Trimmable::CanTrim ct = rv->region()->can_trim ();
2842 if (ct & Trimmable::FrontTrimEarlier) {
2843 set_canvas_cursor (_cursors->left_side_trim);
2845 set_canvas_cursor (_cursors->left_side_trim_right_only);
2848 if (ct & Trimmable::EndTrimLater) {
2849 set_canvas_cursor (_cursors->right_side_trim);
2851 set_canvas_cursor (_cursors->right_side_trim_left_only);
2856 /** Obtain the pointer position in world coordinates */
2858 Editor::get_pointer_position (double& x, double& y) const
2861 track_canvas->get_pointer (px, py);
2862 track_canvas->window_to_world (px, py, x, y);