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 ()) {
984 /* click on a normal region view */
985 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
986 add_region_copy_drag (item, event, clicked_regionview);
987 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
988 add_region_brush_drag (item, event, clicked_regionview);
990 add_region_drag (item, event, clicked_regionview);
994 // if (!internal_editing() && (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE && !selection->regions.empty())) {
995 // _drags->add (new SelectionDrag (this, clicked_axisview->get_selection_rect (clicked_selection)->rect, SelectionDrag::SelectionMove));
998 _drags->start_grab (event);
1001 case RegionViewNameHighlight:
1002 case LeftFrameHandle:
1003 case RightFrameHandle:
1004 if (!clicked_regionview->region()->locked()) {
1005 RegionSelection s = get_equivalent_regions (selection->regions, Properties::select.property_id);
1006 _drags->set (new TrimDrag (this, item, clicked_regionview, s.by_layer()), event);
1011 case RegionViewName:
1013 /* rename happens on edit clicks */
1014 RegionSelection s = get_equivalent_regions (selection->regions, Properties::select.property_id);
1015 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, s.by_layer()), event);
1020 case ControlPointItem:
1021 _drags->set (new ControlPointDrag (this, item), event);
1025 case AutomationLineItem:
1026 _drags->set (new LineDrag (this, item), event);
1031 if (internal_editing()) {
1032 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
1033 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
1037 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
1041 case AutomationTrackItem:
1043 TimeAxisView* parent = clicked_axisview->get_parent ();
1044 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (clicked_axisview);
1046 if (parent && dynamic_cast<MidiTimeAxisView*> (parent) && atv->show_regions ()) {
1048 RouteTimeAxisView* p = dynamic_cast<RouteTimeAxisView*> (parent);
1050 boost::shared_ptr<Playlist> pl = p->track()->playlist ();
1051 if (pl->n_regions() == 0) {
1052 /* Parent has no regions; create one so that we have somewhere to put automation */
1053 _drags->set (new RegionCreateDrag (this, item, parent), event);
1055 /* See if there's a region before the click that we can extend, and extend it if so */
1056 framepos_t const t = event_frame (event);
1057 boost::shared_ptr<Region> prev = pl->find_next_region (t, End, -1);
1059 _drags->set (new RegionCreateDrag (this, item, parent), event);
1061 prev->set_length (t - prev->position ());
1065 /* rubberband drag to select automation points */
1066 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
1073 if ( get_smart_mode() ) {
1074 /* we're in "smart" joined mode, and we've clicked on a Selection */
1075 double const y = event->button.y + vertical_adjustment.get_value() - canvas_timebars_vsize;
1076 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
1078 /* if we're over an automation track, start a drag of its data */
1079 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
1081 _drags->set (new AutomationRangeDrag (this, atv, selection->time), event, _cursors->up_down);
1084 /* if we're over a track and a region, and in the `object' part of a region,
1085 put a selection around the region and drag both
1087 /* RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1088 if (rtv && _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
1089 boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track> (rtv->route ());
1091 boost::shared_ptr<Playlist> pl = t->playlist ();
1094 boost::shared_ptr<Region> r = pl->top_region_at (event_frame (event));
1096 RegionView* rv = rtv->view()->find_view (r);
1097 clicked_selection = select_range (rv->region()->position(),
1098 rv->region()->last_frame()+1);
1099 _drags->add (new SelectionDrag (this, item, SelectionDrag::SelectionMove));
1100 list<RegionView*> rvs;
1102 _drags->add (new RegionMoveDrag (this, item, rv, rvs, false, false));
1103 _drags->start_grab (event);
1126 switch (item_type) {
1128 _drags->set (new LineDrag (this, item), event);
1131 case ControlPointItem:
1132 _drags->set (new ControlPointDrag (this, item), event);
1138 AudioRegionView* arv = dynamic_cast<AudioRegionView *> (clicked_regionview);
1140 _drags->set (new AutomationRangeDrag (this, arv, selection->time), event, _cursors->up_down);
1141 _drags->start_grab (event);
1147 case AutomationLineItem:
1148 _drags->set (new LineDrag (this, item), event);
1158 if (event->type == GDK_BUTTON_PRESS) {
1159 _drags->set (new MouseZoomDrag (this, item), event);
1166 if (internal_editing() && item_type == NoteItem) {
1167 /* drag notes if we're in internal edit mode */
1168 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
1170 } else if (clicked_regionview) {
1172 _drags->set (new TimeFXDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1178 _drags->set (new ScrubDrag (this, item), event);
1179 scrub_reversals = 0;
1180 scrub_reverse_distance = 0;
1181 last_scrub_x = event->button.x;
1182 scrubbing_direction = 0;
1183 set_canvas_cursor (_cursors->transparent);
1195 Editor::button_press_handler_2 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1197 Editing::MouseMode const eff = effective_mouse_mode ();
1200 switch (item_type) {
1202 if (internal_editing ()) {
1203 /* no region drags in internal edit mode */
1207 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
1208 add_region_copy_drag (item, event, clicked_regionview);
1210 add_region_drag (item, event, clicked_regionview);
1212 _drags->start_grab (event);
1215 case ControlPointItem:
1216 _drags->set (new ControlPointDrag (this, item), event);
1224 switch (item_type) {
1225 case RegionViewNameHighlight:
1226 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1230 case LeftFrameHandle:
1231 case RightFrameHandle:
1232 if (!internal_editing ()) {
1233 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1238 case RegionViewName:
1239 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer()), event);
1253 /* relax till release */
1259 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1260 temporal_zoom_to_frame (false, event_frame (event));
1262 temporal_zoom_to_frame (true, event_frame(event));
1275 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1277 if (event->type != GDK_BUTTON_PRESS) {
1281 Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->track_canvas->get_window();
1283 if (canvas_window) {
1284 Glib::RefPtr<const Gdk::Window> pointer_window;
1287 Gdk::ModifierType mask;
1289 pointer_window = canvas_window->get_pointer (x, y, mask);
1291 if (pointer_window == track_canvas->get_bin_window()) {
1292 track_canvas->window_to_world (x, y, wx, wy);
1296 pre_press_cursor = current_canvas_cursor;
1298 track_canvas->grab_focus();
1300 if (_session && _session->actively_recording()) {
1304 if (internal_editing()) {
1305 bool leave_internal_edit_mode = false;
1307 switch (item_type) {
1312 if (!dynamic_cast<MidiRegionView*> (clicked_regionview) && !dynamic_cast<AutomationRegionView*> (clicked_regionview)) {
1313 leave_internal_edit_mode = true;
1317 case PlayheadCursorItem:
1319 case TempoMarkerItem:
1320 case MeterMarkerItem:
1324 case RangeMarkerBarItem:
1325 case CdMarkerBarItem:
1326 case TransportMarkerBarItem:
1328 /* button press on these events never does anything to
1329 change the editing mode.
1337 if (leave_internal_edit_mode) {
1338 ActionManager::do_action ("MouseMode", "toggle-internal-edit");
1342 button_selection (item, event, item_type);
1344 if (!_drags->active () &&
1345 (Keyboard::is_delete_event (&event->button) ||
1346 Keyboard::is_context_menu_event (&event->button) ||
1347 Keyboard::is_edit_event (&event->button))) {
1349 /* handled by button release */
1353 //not rolling, range mode click + join_play_range : locate the PH here
1354 if ( !_drags->active () && !_session->transport_rolling() && ( effective_mouse_mode() == MouseRange ) && Config->get_always_play_range() ) {
1355 framepos_t where = event_frame (event, 0, 0);
1357 _session->request_locate (where, false);
1360 switch (event->button.button) {
1362 return button_press_handler_1 (item, event, item_type);
1366 return button_press_handler_2 (item, event, item_type);
1373 return button_press_dispatch (&event->button);
1382 Editor::button_press_dispatch (GdkEventButton* ev)
1384 /* this function is intended only for buttons 4 and above.
1387 Gtkmm2ext::MouseButton b (ev->state, ev->button);
1388 return button_bindings->activate (b, Gtkmm2ext::Bindings::Press);
1392 Editor::button_release_dispatch (GdkEventButton* ev)
1394 /* this function is intended only for buttons 4 and above.
1397 Gtkmm2ext::MouseButton b (ev->state, ev->button);
1398 return button_bindings->activate (b, Gtkmm2ext::Bindings::Release);
1402 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1404 framepos_t where = event_frame (event, 0, 0);
1405 AutomationTimeAxisView* atv = 0;
1407 if (pre_press_cursor) {
1408 set_canvas_cursor (pre_press_cursor);
1409 pre_press_cursor = 0;
1412 /* no action if we're recording */
1414 if (_session && _session->actively_recording()) {
1418 /* see if we're finishing a drag */
1420 bool were_dragging = false;
1421 if (_drags->active ()) {
1422 bool const r = _drags->end_grab (event);
1424 /* grab dragged, so do nothing else */
1428 were_dragging = true;
1431 update_region_layering_order_editor ();
1433 /* edit events get handled here */
1435 if (!_drags->active () && Keyboard::is_edit_event (&event->button)) {
1436 switch (item_type) {
1438 show_region_properties ();
1441 case TempoMarkerItem:
1442 edit_tempo_marker (item);
1445 case MeterMarkerItem:
1446 edit_meter_marker (item);
1449 case RegionViewName:
1450 if (clicked_regionview->name_active()) {
1451 return mouse_rename_region (item, event);
1455 case ControlPointItem:
1456 edit_control_point (item);
1465 /* context menu events get handled here */
1466 if (Keyboard::is_context_menu_event (&event->button)) {
1468 context_click_event = *event;
1470 if (!_drags->active ()) {
1472 /* no matter which button pops up the context menu, tell the menu
1473 widget to use button 1 to drive menu selection.
1476 switch (item_type) {
1478 case FadeInHandleItem:
1480 case FadeOutHandleItem:
1481 popup_fade_context_menu (1, event->button.time, item, item_type);
1484 case StartCrossFadeItem:
1485 popup_xfade_in_context_menu (1, event->button.time, item, item_type);
1488 case EndCrossFadeItem:
1489 popup_xfade_out_context_menu (1, event->button.time, item, item_type);
1493 popup_track_context_menu (1, event->button.time, item_type, false);
1497 case RegionViewNameHighlight:
1498 case LeftFrameHandle:
1499 case RightFrameHandle:
1500 case RegionViewName:
1501 popup_track_context_menu (1, event->button.time, item_type, false);
1505 popup_track_context_menu (1, event->button.time, item_type, true);
1508 case AutomationTrackItem:
1509 popup_track_context_menu (1, event->button.time, item_type, false);
1513 case RangeMarkerBarItem:
1514 case TransportMarkerBarItem:
1515 case CdMarkerBarItem:
1519 popup_ruler_menu (where, item_type);
1523 marker_context_menu (&event->button, item);
1526 case TempoMarkerItem:
1527 tempo_or_meter_marker_context_menu (&event->button, item);
1530 case MeterMarkerItem:
1531 tempo_or_meter_marker_context_menu (&event->button, item);
1534 case CrossfadeViewItem:
1535 popup_track_context_menu (1, event->button.time, item_type, false);
1538 case ControlPointItem:
1539 popup_control_point_context_menu (item, event);
1550 /* delete events get handled here */
1552 Editing::MouseMode const eff = effective_mouse_mode ();
1554 if (!_drags->active () && Keyboard::is_delete_event (&event->button)) {
1556 switch (item_type) {
1557 case TempoMarkerItem:
1558 remove_tempo_marker (item);
1561 case MeterMarkerItem:
1562 remove_meter_marker (item);
1566 remove_marker (*item, event);
1570 if (eff == MouseObject) {
1571 remove_clicked_region ();
1575 case ControlPointItem:
1576 remove_control_point (item);
1580 remove_midi_note (item, event);
1589 switch (event->button.button) {
1592 switch (item_type) {
1593 /* see comments in button_press_handler */
1594 case PlayheadCursorItem:
1597 case AutomationLineItem:
1598 case StartSelectionTrimItem:
1599 case EndSelectionTrimItem:
1603 if (!_dragging_playhead) {
1604 snap_to_with_modifier (where, event, 0, true);
1605 mouse_add_new_marker (where);
1609 case CdMarkerBarItem:
1610 if (!_dragging_playhead) {
1611 // if we get here then a dragged range wasn't done
1612 snap_to_with_modifier (where, event, 0, true);
1613 mouse_add_new_marker (where, true);
1618 if (!_dragging_playhead) {
1619 snap_to_with_modifier (where, event);
1620 mouse_add_new_tempo_event (where);
1625 if (!_dragging_playhead) {
1626 mouse_add_new_meter_event (pixel_to_frame (event->button.x));
1637 switch (item_type) {
1638 case AutomationTrackItem:
1639 atv = dynamic_cast<AutomationTimeAxisView*>(clicked_axisview);
1641 atv->add_automation_event (event, where, event->button.y);
1651 switch (item_type) {
1654 /* check that we didn't drag before releasing, since
1655 its really annoying to create new control
1656 points when doing this.
1658 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (clicked_regionview);
1659 if (!were_dragging && arv) {
1660 arv->add_gain_point_event (item, event);
1666 case AutomationTrackItem:
1667 dynamic_cast<AutomationTimeAxisView*>(clicked_axisview)->
1668 add_automation_event (event, where, event->button.y);
1677 set_canvas_cursor (current_canvas_cursor);
1678 if (scrubbing_direction == 0) {
1679 /* no drag, just a click */
1680 switch (item_type) {
1682 play_selected_region ();
1688 /* make sure we stop */
1689 _session->request_transport_speed (0.0);
1698 /* do any (de)selection operations that should occur on button release */
1699 button_selection (item, event, item_type);
1708 switch (item_type) {
1710 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1712 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier|Keyboard::SecondaryModifier))) {
1715 // Button2 click is unused
1730 // x_style_paste (where, 1.0);
1751 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1758 switch (item_type) {
1759 case ControlPointItem:
1760 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1761 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1762 cp->set_visible (true);
1766 at_y = cp->get_y ();
1767 cp->i2w (at_x, at_y);
1771 fraction = 1.0 - (cp->get_y() / cp->line().height());
1773 if (is_drawable() && !_drags->active ()) {
1774 set_canvas_cursor (_cursors->fader);
1777 _verbose_cursor->set (cp->line().get_verbose_cursor_string (fraction), at_x, at_y);
1778 _verbose_cursor->show ();
1783 if (mouse_mode == MouseGain) {
1784 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1786 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredGainLine.get();
1787 if (is_drawable()) {
1788 set_canvas_cursor (_cursors->fader);
1793 case AutomationLineItem:
1794 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1795 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1797 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredAutomationLine.get();
1799 if (is_drawable()) {
1800 set_canvas_cursor (_cursors->fader);
1805 case RegionViewNameHighlight:
1806 if (is_drawable() && effective_mouse_mode() == MouseObject && entered_regionview) {
1807 set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1808 _over_region_trim_target = true;
1812 case LeftFrameHandle:
1813 case RightFrameHandle:
1814 if (is_drawable() && effective_mouse_mode() == MouseObject && !internal_editing() && entered_regionview) {
1815 set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1819 case StartSelectionTrimItem:
1820 if (is_drawable()) {
1821 set_canvas_cursor (_cursors->left_side_trim);
1824 case EndSelectionTrimItem:
1825 if (is_drawable()) {
1826 set_canvas_cursor (_cursors->right_side_trim);
1830 case PlayheadCursorItem:
1831 if (is_drawable()) {
1832 switch (_edit_point) {
1834 set_canvas_cursor (_cursors->grabber_edit_point);
1837 set_canvas_cursor (_cursors->grabber);
1843 case RegionViewName:
1845 /* when the name is not an active item, the entire name highlight is for trimming */
1847 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1848 if (mouse_mode == MouseObject && is_drawable()) {
1849 set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1850 _over_region_trim_target = true;
1856 case AutomationTrackItem:
1857 if (is_drawable()) {
1858 Gdk::Cursor *cursor;
1859 switch (mouse_mode) {
1861 cursor = _cursors->selector;
1864 cursor = _cursors->zoom_in;
1867 cursor = _cursors->cross_hair;
1871 set_canvas_cursor (cursor);
1873 AutomationTimeAxisView* atv;
1874 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1875 clear_entered_track = false;
1876 set_entered_track (atv);
1882 case RangeMarkerBarItem:
1883 case TransportMarkerBarItem:
1884 case CdMarkerBarItem:
1887 if (is_drawable()) {
1888 set_canvas_cursor (_cursors->timebar);
1893 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1896 entered_marker = marker;
1897 marker->set_color_rgba (ARDOUR_UI::config()->canvasvar_EnteredMarker.get());
1899 case MeterMarkerItem:
1900 case TempoMarkerItem:
1901 if (is_drawable()) {
1902 set_canvas_cursor (_cursors->timebar);
1906 case FadeInHandleItem:
1907 if (mouse_mode == MouseObject && !internal_editing()) {
1908 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1910 rect->property_fill_color_rgba() = 0xBBBBBBAA;
1912 set_canvas_cursor (_cursors->fade_in);
1916 case FadeOutHandleItem:
1917 if (mouse_mode == MouseObject && !internal_editing()) {
1918 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1920 rect->property_fill_color_rgba() = 0xBBBBBBAA;
1922 set_canvas_cursor (_cursors->fade_out);
1925 case FeatureLineItem:
1927 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1928 line->property_fill_color_rgba() = 0xFF0000FF;
1932 if ( get_smart_mode() ) {
1933 set_canvas_cursor ();
1941 /* second pass to handle entered track status in a comprehensible way.
1944 switch (item_type) {
1946 case AutomationLineItem:
1947 case ControlPointItem:
1948 /* these do not affect the current entered track state */
1949 clear_entered_track = false;
1952 case AutomationTrackItem:
1953 /* handled above already */
1957 set_entered_track (0);
1965 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent*, ItemType item_type)
1975 switch (item_type) {
1976 case ControlPointItem:
1977 cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
1978 if (cp->line().the_list()->interpolation() != AutomationList::Discrete) {
1979 if (cp->line().npoints() > 1 && !cp->get_selected()) {
1980 cp->set_visible (false);
1984 if (is_drawable()) {
1985 set_canvas_cursor (current_canvas_cursor);
1988 _verbose_cursor->hide ();
1991 case RegionViewNameHighlight:
1992 case LeftFrameHandle:
1993 case RightFrameHandle:
1994 case StartSelectionTrimItem:
1995 case EndSelectionTrimItem:
1996 case PlayheadCursorItem:
1998 _over_region_trim_target = false;
2000 if (is_drawable()) {
2001 set_canvas_cursor (current_canvas_cursor);
2006 case AutomationLineItem:
2007 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
2009 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
2011 line->property_fill_color_rgba() = al->get_line_color();
2013 if (is_drawable()) {
2014 set_canvas_cursor (current_canvas_cursor);
2018 case RegionViewName:
2019 /* see enter_handler() for notes */
2020 _over_region_trim_target = false;
2022 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
2023 if (is_drawable() && mouse_mode == MouseObject) {
2024 set_canvas_cursor (current_canvas_cursor);
2029 case RangeMarkerBarItem:
2030 case TransportMarkerBarItem:
2031 case CdMarkerBarItem:
2035 if (is_drawable()) {
2036 set_canvas_cursor (current_canvas_cursor);
2041 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2045 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
2046 location_flags_changed (loc, this);
2049 case MeterMarkerItem:
2050 case TempoMarkerItem:
2052 if (is_drawable()) {
2053 set_canvas_cursor (current_canvas_cursor);
2058 case FadeInHandleItem:
2059 case FadeOutHandleItem:
2060 rv = static_cast<RegionView*>(item->get_data ("regionview"));
2062 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
2064 rect->property_fill_color_rgba() = rv->get_fill_color();
2067 set_canvas_cursor (current_canvas_cursor);
2070 case AutomationTrackItem:
2071 if (is_drawable()) {
2072 set_canvas_cursor (current_canvas_cursor);
2073 clear_entered_track = true;
2074 Glib::signal_idle().connect (sigc::mem_fun(*this, &Editor::left_automation_track));
2077 case FeatureLineItem:
2079 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
2080 line->property_fill_color_rgba() = (guint) ARDOUR_UI::config()->canvasvar_ZeroLine.get();;
2092 Editor::left_automation_track ()
2094 if (clear_entered_track) {
2095 set_entered_track (0);
2096 clear_entered_track = false;
2102 Editor::scrub (framepos_t frame, double current_x)
2106 if (scrubbing_direction == 0) {
2108 _session->request_locate (frame, false);
2109 _session->request_transport_speed (0.1);
2110 scrubbing_direction = 1;
2114 if (last_scrub_x > current_x) {
2116 /* pointer moved to the left */
2118 if (scrubbing_direction > 0) {
2120 /* we reversed direction to go backwards */
2123 scrub_reverse_distance += (int) (last_scrub_x - current_x);
2127 /* still moving to the left (backwards) */
2129 scrub_reversals = 0;
2130 scrub_reverse_distance = 0;
2132 delta = 0.01 * (last_scrub_x - current_x);
2133 _session->request_transport_speed_nonzero (_session->transport_speed() - delta);
2137 /* pointer moved to the right */
2139 if (scrubbing_direction < 0) {
2140 /* we reversed direction to go forward */
2143 scrub_reverse_distance += (int) (current_x - last_scrub_x);
2146 /* still moving to the right */
2148 scrub_reversals = 0;
2149 scrub_reverse_distance = 0;
2151 delta = 0.01 * (current_x - last_scrub_x);
2152 _session->request_transport_speed_nonzero (_session->transport_speed() + delta);
2156 /* if there have been more than 2 opposite motion moves detected, or one that moves
2157 back more than 10 pixels, reverse direction
2160 if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
2162 if (scrubbing_direction > 0) {
2163 /* was forwards, go backwards */
2164 _session->request_transport_speed (-0.1);
2165 scrubbing_direction = -1;
2167 /* was backwards, go forwards */
2168 _session->request_transport_speed (0.1);
2169 scrubbing_direction = 1;
2172 scrub_reverse_distance = 0;
2173 scrub_reversals = 0;
2177 last_scrub_x = current_x;
2181 Editor::motion_handler (ArdourCanvas::Item* /*item*/, GdkEvent* event, bool from_autoscroll)
2183 _last_motion_y = event->motion.y;
2185 if (event->motion.is_hint) {
2188 /* We call this so that MOTION_NOTIFY events continue to be
2189 delivered to the canvas. We need to do this because we set
2190 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
2191 the density of the events, at the expense of a round-trip
2192 to the server. Given that this will mostly occur on cases
2193 where DISPLAY = :0.0, and given the cost of what the motion
2194 event might do, its a good tradeoff.
2197 track_canvas->get_pointer (x, y);
2200 if (current_stepping_trackview) {
2201 /* don't keep the persistent stepped trackview if the mouse moves */
2202 current_stepping_trackview = 0;
2203 step_timeout.disconnect ();
2206 if (_session && _session->actively_recording()) {
2207 /* Sorry. no dragging stuff around while we record */
2211 JoinObjectRangeState const old = _join_object_range_state;
2212 update_join_object_range_location (event->motion.x, event->motion.y);
2214 if (!_internal_editing && _join_object_range_state != old) {
2215 set_canvas_cursor ();
2218 if (!_internal_editing && _over_region_trim_target) {
2219 set_canvas_cursor_for_region_view (event->motion.x, entered_regionview);
2222 bool handled = false;
2223 if (_drags->active ()) {
2224 handled = _drags->motion_handler (event, from_autoscroll);
2231 track_canvas_motion (event);
2236 Editor::can_remove_control_point (ArdourCanvas::Item* item)
2238 ControlPoint* control_point;
2240 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2241 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2245 AutomationLine& line = control_point->line ();
2246 if (dynamic_cast<AudioRegionGainLine*> (&line)) {
2247 /* we shouldn't remove the first or last gain point in region gain lines */
2248 if (line.is_last_point(*control_point) || line.is_first_point(*control_point)) {
2257 Editor::remove_control_point (ArdourCanvas::Item* item)
2259 if (!can_remove_control_point (item)) {
2263 ControlPoint* control_point;
2265 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2266 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2270 control_point->line().remove_point (*control_point);
2274 Editor::edit_control_point (ArdourCanvas::Item* item)
2276 ControlPoint* p = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"));
2279 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2283 ControlPointDialog d (p);
2284 d.set_position (Gtk::WIN_POS_MOUSE);
2287 if (d.run () != RESPONSE_ACCEPT) {
2291 p->line().modify_point_y (*p, d.get_y_fraction ());
2295 Editor::edit_notes (TimeAxisViewItem& tavi)
2297 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(&tavi);
2303 MidiRegionView::Selection const & s = mrv->selection();
2309 EditNoteDialog d (&(*s.begin())->region_view(), s);
2310 d.set_position (Gtk::WIN_POS_MOUSE);
2317 Editor::visible_order_range (int* low, int* high) const
2319 *low = TimeAxisView::max_order ();
2322 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
2324 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
2326 if (!rtv->hidden()) {
2328 if (*high < rtv->order()) {
2329 *high = rtv->order ();
2332 if (*low > rtv->order()) {
2333 *low = rtv->order ();
2340 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
2342 /* Either add to or set the set the region selection, unless
2343 this is an alignment click (control used)
2346 if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
2347 TimeAxisView* tv = &rv.get_time_axis_view();
2348 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
2350 if (rtv && rtv->is_track()) {
2351 speed = rtv->track()->speed();
2354 framepos_t where = get_preferred_edit_position();
2358 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
2360 align_region (rv.region(), SyncPoint, (framepos_t) (where * speed));
2362 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
2364 align_region (rv.region(), End, (framepos_t) (where * speed));
2368 align_region (rv.region(), Start, (framepos_t) (where * speed));
2375 Editor::collect_new_region_view (RegionView* rv)
2377 latest_regionviews.push_back (rv);
2381 Editor::collect_and_select_new_region_view (RegionView* rv)
2384 latest_regionviews.push_back (rv);
2388 Editor::cancel_selection ()
2390 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2391 (*i)->hide_selection ();
2394 selection->clear ();
2395 clicked_selection = 0;
2399 Editor::cancel_time_selection ()
2401 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2402 (*i)->hide_selection ();
2404 selection->time.clear ();
2405 clicked_selection = 0;
2409 Editor::point_trim (GdkEvent* event, framepos_t new_bound)
2411 RegionView* rv = clicked_regionview;
2413 /* Choose action dependant on which button was pressed */
2414 switch (event->button.button) {
2416 begin_reversible_command (_("start point trim"));
2418 if (selection->selected (rv)) {
2419 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
2420 i != selection->regions.by_layer().end(); ++i)
2422 if (!(*i)->region()->locked()) {
2423 (*i)->region()->clear_changes ();
2424 (*i)->region()->trim_front (new_bound);
2425 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2430 if (!rv->region()->locked()) {
2431 rv->region()->clear_changes ();
2432 rv->region()->trim_front (new_bound);
2433 _session->add_command(new StatefulDiffCommand (rv->region()));
2437 commit_reversible_command();
2441 begin_reversible_command (_("End point trim"));
2443 if (selection->selected (rv)) {
2445 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
2447 if (!(*i)->region()->locked()) {
2448 (*i)->region()->clear_changes();
2449 (*i)->region()->trim_end (new_bound);
2450 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2456 if (!rv->region()->locked()) {
2457 rv->region()->clear_changes ();
2458 rv->region()->trim_end (new_bound);
2459 _session->add_command (new StatefulDiffCommand (rv->region()));
2463 commit_reversible_command();
2472 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* /*event*/)
2477 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2478 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2482 Location* location = find_location_from_marker (marker, is_start);
2483 location->set_hidden (true, this);
2488 Editor::reposition_zoom_rect (framepos_t start, framepos_t end)
2490 double x1 = frame_to_pixel (start);
2491 double x2 = frame_to_pixel (end);
2492 double y2 = full_canvas_height - 1.0;
2494 zoom_rect->property_x1() = x1;
2495 zoom_rect->property_y1() = 1.0;
2496 zoom_rect->property_x2() = x2;
2497 zoom_rect->property_y2() = y2;
2502 Editor::mouse_rename_region (ArdourCanvas::Item* /*item*/, GdkEvent* /*event*/)
2504 using namespace Gtkmm2ext;
2506 ArdourPrompter prompter (false);
2508 prompter.set_prompt (_("Name for region:"));
2509 prompter.set_initial_text (clicked_regionview->region()->name());
2510 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
2511 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
2512 prompter.show_all ();
2513 switch (prompter.run ()) {
2514 case Gtk::RESPONSE_ACCEPT:
2516 prompter.get_result(str);
2518 clicked_regionview->region()->set_name (str);
2527 Editor::mouse_brush_insert_region (RegionView* rv, framepos_t pos)
2529 /* no brushing without a useful snap setting */
2531 switch (_snap_mode) {
2533 return; /* can't work because it allows region to be placed anywhere */
2538 switch (_snap_type) {
2546 /* don't brush a copy over the original */
2548 if (pos == rv->region()->position()) {
2552 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
2554 if (rtv == 0 || !rtv->is_track()) {
2558 boost::shared_ptr<Playlist> playlist = rtv->playlist();
2559 double speed = rtv->track()->speed();
2561 playlist->clear_changes ();
2562 boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region(), true));
2563 playlist->add_region (new_region, (framepos_t) (pos * speed));
2564 _session->add_command (new StatefulDiffCommand (playlist));
2566 // playlist is frozen, so we have to update manually XXX this is disgusting
2568 playlist->RegionAdded (new_region); /* EMIT SIGNAL */
2572 Editor::track_height_step_timeout ()
2574 if (get_microseconds() - last_track_height_step_timestamp < 250000) {
2575 current_stepping_trackview = 0;
2582 Editor::add_region_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2584 assert (region_view);
2586 if (!region_view->region()->playlist()) {
2590 _region_motion_group->raise_to_top ();
2592 if (Config->get_edit_mode() == Splice) {
2593 _drags->add (new RegionSpliceDrag (this, item, region_view, selection->regions.by_layer()));
2595 RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::select.property_id);
2596 _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), false, false));
2599 /* sync the canvas to what we think is its current state */
2600 update_canvas_now();
2604 Editor::add_region_copy_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2606 assert (region_view);
2608 if (!region_view->region()->playlist()) {
2612 _region_motion_group->raise_to_top ();
2614 RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::select.property_id);
2615 _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), false, true));
2619 Editor::add_region_brush_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2621 assert (region_view);
2623 if (!region_view->region()->playlist()) {
2627 if (Config->get_edit_mode() == Splice) {
2631 RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::select.property_id);
2632 _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), true, false));
2634 begin_reversible_command (Operations::drag_region_brush);
2637 /** Start a grab where a time range is selected, track(s) are selected, and the
2638 * user clicks and drags a region with a modifier in order to create a new region containing
2639 * the section of the clicked region that lies within the time range.
2642 Editor::start_selection_grab (ArdourCanvas::Item* /*item*/, GdkEvent* event)
2644 if (clicked_regionview == 0) {
2648 /* lets try to create new Region for the selection */
2650 vector<boost::shared_ptr<Region> > new_regions;
2651 create_region_from_selection (new_regions);
2653 if (new_regions.empty()) {
2657 /* XXX fix me one day to use all new regions */
2659 boost::shared_ptr<Region> region (new_regions.front());
2661 /* add it to the current stream/playlist.
2663 tricky: the streamview for the track will add a new regionview. we will
2664 catch the signal it sends when it creates the regionview to
2665 set the regionview we want to then drag.
2668 latest_regionviews.clear();
2669 sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
2671 /* A selection grab currently creates two undo/redo operations, one for
2672 creating the new region and another for moving it.
2675 begin_reversible_command (Operations::selection_grab);
2677 boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
2679 playlist->clear_changes ();
2680 clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
2681 _session->add_command(new StatefulDiffCommand (playlist));
2683 commit_reversible_command ();
2687 if (latest_regionviews.empty()) {
2688 /* something went wrong */
2692 /* we need to deselect all other regionviews, and select this one
2693 i'm ignoring undo stuff, because the region creation will take care of it
2695 selection->set (latest_regionviews);
2697 _drags->set (new RegionMoveDrag (this, latest_regionviews.front()->get_canvas_group(), latest_regionviews.front(), latest_regionviews, false, false), event);
2703 if (_drags->active ()) {
2706 selection->clear ();
2711 Editor::set_internal_edit (bool yn)
2713 if (_internal_editing == yn) {
2717 _internal_editing = yn;
2720 pre_internal_mouse_mode = mouse_mode;
2721 pre_internal_snap_type = _snap_type;
2722 pre_internal_snap_mode = _snap_mode;
2724 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2725 (*i)->enter_internal_edit_mode ();
2728 set_snap_to (internal_snap_type);
2729 set_snap_mode (internal_snap_mode);
2733 internal_snap_mode = _snap_mode;
2734 internal_snap_type = _snap_type;
2736 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2737 (*i)->leave_internal_edit_mode ();
2740 if (mouse_mode == MouseDraw && pre_internal_mouse_mode != MouseDraw) {
2741 /* we were drawing .. flip back to something sensible */
2742 set_mouse_mode (pre_internal_mouse_mode);
2745 set_snap_to (pre_internal_snap_type);
2746 set_snap_mode (pre_internal_snap_mode);
2749 set_canvas_cursor ();
2752 /** Update _join_object_range_state which indicate whether we are over the top or bottom half of a region view,
2753 * used by the `join object/range' tool mode.
2756 Editor::update_join_object_range_location (double /*x*/, double y)
2758 /* XXX: actually, this decides based on whether the mouse is in the top
2759 or bottom half of a the waveform part RouteTimeAxisView;
2761 Note that entered_{track,regionview} is not always setup (e.g. if
2762 the mouse is over a TimeSelection), and to get a Region
2763 that we're over requires searching the playlist.
2766 if ( !get_smart_mode() ) {
2767 _join_object_range_state = JOIN_OBJECT_RANGE_NONE;
2771 if (mouse_mode == MouseObject) {
2772 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2773 } else if (mouse_mode == MouseRange) {
2774 _join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
2777 /* XXX: maybe we should make entered_track work in all cases, rather than resorting to this */
2778 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y + vertical_adjustment.get_value() - canvas_timebars_vsize);
2782 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
2787 rtv->canvas_display()->w2i (cx, cy);
2789 double const c = cy / (rtv->view()->child_height() - TimeAxisViewItem::NAME_HIGHLIGHT_SIZE);
2791 _join_object_range_state = c <= 0.5 ? JOIN_OBJECT_RANGE_RANGE : JOIN_OBJECT_RANGE_OBJECT;
2797 Editor::effective_mouse_mode () const
2799 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
2801 } else if (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE) {
2809 Editor::remove_midi_note (ArdourCanvas::Item* item, GdkEvent *)
2811 ArdourCanvas::CanvasNoteEvent* e = dynamic_cast<ArdourCanvas::CanvasNoteEvent*> (item);
2814 e->region_view().delete_note (e->note ());
2818 Editor::set_canvas_cursor_for_region_view (double x, RegionView* rv)
2822 ArdourCanvas::Group* g = rv->get_canvas_group ();
2823 ArdourCanvas::Group* p = g->get_parent_group ();
2825 /* Compute x in region view parent coordinates */
2829 double x1, x2, y1, y2;
2830 g->get_bounds (x1, y1, x2, y2);
2832 /* Halfway across the region */
2833 double const h = (x1 + x2) / 2;
2835 Trimmable::CanTrim ct = rv->region()->can_trim ();
2837 if (ct & Trimmable::FrontTrimEarlier) {
2838 set_canvas_cursor (_cursors->left_side_trim);
2840 set_canvas_cursor (_cursors->left_side_trim_right_only);
2843 if (ct & Trimmable::EndTrimLater) {
2844 set_canvas_cursor (_cursors->right_side_trim);
2846 set_canvas_cursor (_cursors->right_side_trim_left_only);
2851 /** Obtain the pointer position in world coordinates */
2853 Editor::get_pointer_position (double& x, double& y) const
2856 track_canvas->get_pointer (px, py);
2857 track_canvas->window_to_world (px, py, x, y);