2 Copyright (C) 2000-2001 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
29 #include "pbd/error.h"
30 #include "pbd/enumwriter.h"
31 #include "pbd/memento_command.h"
32 #include "pbd/basename.h"
33 #include "pbd/stateful_diff_command.h"
35 #include "gtkmm2ext/bindings.h"
36 #include "gtkmm2ext/utils.h"
37 #include "gtkmm2ext/tearoff.h"
39 #include "canvas/canvas.h"
41 #include "ardour/audioregion.h"
42 #include "ardour/operations.h"
43 #include "ardour/playlist.h"
44 #include "ardour/profile.h"
45 #include "ardour/region_factory.h"
46 #include "ardour/route.h"
47 #include "ardour/session.h"
48 #include "ardour/types.h"
50 #include "ardour_ui.h"
53 #include "time_axis_view.h"
54 #include "audio_time_axis.h"
55 #include "audio_region_view.h"
56 #include "midi_region_view.h"
58 #include "streamview.h"
59 #include "region_gain_line.h"
60 #include "automation_time_axis.h"
61 #include "control_point.h"
64 #include "selection.h"
67 #include "rgb_macros.h"
68 #include "control_point_dialog.h"
69 #include "editor_drag.h"
70 #include "automation_region_view.h"
71 #include "edit_note_dialog.h"
72 #include "mouse_cursors.h"
73 #include "editor_cursors.h"
74 #include "verbose_cursor.h"
80 using namespace ARDOUR;
83 using namespace Editing;
84 using Gtkmm2ext::Keyboard;
87 Editor::mouse_frame (framepos_t& where, bool& in_track_canvas) const
89 /* gdk_window_get_pointer() has X11's XQueryPointer semantics in that it only
90 pays attentions to subwindows. this means that menu windows are ignored, and
91 if the pointer is in a menu, the return window from the call will be the
92 the regular subwindow *under* the menu.
94 this matters quite a lot if the pointer is moving around in a menu that overlaps
95 the track canvas because we will believe that we are within the track canvas
96 when we are not. therefore, we track enter/leave events for the track canvas
97 and allow that to override the result of gdk_window_get_pointer().
100 if (!within_track_canvas) {
105 Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->_track_canvas->get_window();
107 if (!canvas_window) {
111 Glib::RefPtr<const Gdk::Window> pointer_window = Gdk::Display::get_default()->get_window_at_pointer (x, y);
113 if (!pointer_window) {
117 if (pointer_window != canvas_window && pointer_window != _time_bars_canvas->get_window()) {
118 in_track_canvas = false;
122 in_track_canvas = true;
125 event.type = GDK_BUTTON_RELEASE;
129 where = window_event_sample (&event, 0, 0);
135 Editor::window_event_sample (GdkEvent const * event, double* pcx, double* pcy) const
140 if (!gdk_event_get_coords (event, &x, &y)) {
144 /* event coordinates are in window units, so convert to canvas
145 * (i.e. account for scrolling)
148 ArdourCanvas::Duple d = _track_canvas->window_to_canvas (ArdourCanvas::Duple (x, y));
158 return pixel_to_sample (d.x);
162 Editor::canvas_event_sample (GdkEvent const * event, double* pcx, double* pcy) const
167 /* event coordinates are already in canvas units */
169 if (!gdk_event_get_coords (event, &x, &y)) {
170 cerr << "!NO c COORDS for event type " << event->type << endl;
182 /* note that pixel_to_sample() never returns less than zero, so even if the pixel
183 position is negative (as can be the case with motion events in particular),
184 the frame location is always positive.
187 return pixel_to_sample (x);
191 Editor::which_grabber_cursor ()
193 Gdk::Cursor* c = _cursors->grabber;
195 if (_internal_editing) {
196 switch (mouse_mode) {
198 c = _cursors->midi_pencil;
202 c = _cursors->grabber_note;
206 c = _cursors->midi_resize;
210 c = _cursors->grabber_note;
219 switch (_edit_point) {
221 c = _cursors->grabber_edit_point;
224 boost::shared_ptr<Movable> m = _movable.lock();
225 if (m && m->locked()) {
226 c = _cursors->speaker;
236 Editor::set_current_trimmable (boost::shared_ptr<Trimmable> t)
238 boost::shared_ptr<Trimmable> st = _trimmable.lock();
240 if (!st || st == t) {
242 set_canvas_cursor ();
247 Editor::set_current_movable (boost::shared_ptr<Movable> m)
249 boost::shared_ptr<Movable> sm = _movable.lock();
251 if (!sm || sm != m) {
253 set_canvas_cursor ();
258 Editor::set_canvas_cursor ()
260 switch (mouse_mode) {
262 current_canvas_cursor = _cursors->selector;
263 if (_internal_editing) {
264 current_canvas_cursor = which_grabber_cursor();
269 current_canvas_cursor = which_grabber_cursor();
273 current_canvas_cursor = _cursors->midi_pencil;
277 current_canvas_cursor = _cursors->cross_hair;
281 if (Keyboard::the_keyboard().key_is_down (GDK_Control_L)) {
282 current_canvas_cursor = _cursors->zoom_out;
284 current_canvas_cursor = _cursors->zoom_in;
289 current_canvas_cursor = _cursors->time_fx; // just use playhead
293 current_canvas_cursor = _cursors->speaker;
297 if (!_internal_editing) {
298 switch (_join_object_range_state) {
299 case JOIN_OBJECT_RANGE_NONE:
301 case JOIN_OBJECT_RANGE_OBJECT:
302 current_canvas_cursor = which_grabber_cursor ();
304 case JOIN_OBJECT_RANGE_RANGE:
305 current_canvas_cursor = _cursors->selector;
310 /* up-down cursor as a cue that automation can be dragged up and down when in join object/range mode */
311 if (!_internal_editing && get_smart_mode() ) {
314 get_pointer_position (x, y);
316 if (x >= 0 && y >= 0) {
318 vector<ArdourCanvas::Item const *> items;
320 _track_canvas->root()->add_items_at_point (ArdourCanvas::Duple (x,y), items);
322 // first item will be the upper most
324 if (!items.empty()) {
325 const ArdourCanvas::Item* i = items.front();
327 if (i && i->parent() && i->parent()->get_data (X_("timeselection"))) {
328 pair<TimeAxisView*, int> tvp = trackview_by_y_position (_last_motion_y);
329 if (dynamic_cast<AutomationTimeAxisView*> (tvp.first)) {
330 current_canvas_cursor = _cursors->up_down;
337 set_canvas_cursor (current_canvas_cursor, true);
341 Editor::mouse_mode_object_range_toggled()
343 MouseMode m = mouse_mode;
345 Glib::RefPtr<Action> act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-range"));
347 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
350 if (tact->get_active()) {
351 m = MouseObject; //Smart mode turned to ON, force editing to Object mode
354 set_mouse_mode(m, true); //call this so the button styles can get updated
358 Editor::set_mouse_mode (MouseMode m, bool force)
360 if (_drags->active ()) {
364 if (!force && m == mouse_mode) {
368 Glib::RefPtr<Action> act;
372 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-range"));
376 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-object"));
380 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-draw"));
384 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-gain"));
388 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-zoom"));
392 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-timefx"));
396 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-audition"));
402 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
405 /* go there and back to ensure that the toggled handler is called to set up mouse_mode */
406 tact->set_active (false);
407 tact->set_active (true);
409 //NOTE: this will result in a call to mouse_mode_toggled which does the heavy lifting
413 Editor::mouse_mode_toggled (MouseMode m)
415 Glib::RefPtr<Action> act;
416 Glib::RefPtr<ToggleAction> tact;
420 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-range"));
424 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-object"));
428 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-draw"));
432 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-gain"));
436 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-zoom"));
440 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-timefx"));
444 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-audition"));
450 tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
453 if (!tact->get_active()) {
454 /* this was just the notification that the old mode has been
455 * left. we'll get called again with the new mode active in a
463 act = ActionManager::get_action (X_("MouseMode"), X_("toggle-internal-edit"));
464 tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
465 tact->set_active (true);
471 if (_session && mouse_mode == MouseAudition) {
472 /* stop transport and reset default speed to avoid oddness with
474 _session->request_transport_speed (0.0, true);
481 //TODO: set button styles for smart buttons
483 if ( smart_mode_action->get_active() ) {
484 if( mouse_mode == MouseObject ) { //smart active and object active
485 smart_mode_button.set_active(1);
486 smart_mode_button.set_name("smart mode button");
487 mouse_move_button.set_name("smart mode button");
488 } else { //smart active but object inactive
489 smart_mode_button.set_active(0);
490 smart_mode_button.set_name("smart mode button");
491 mouse_move_button.set_name("mouse mode button");
494 smart_mode_button.set_active(0);
495 smart_mode_button.set_name("mouse mode button");
496 mouse_move_button.set_name("mouse mode button");
500 set_canvas_cursor ();
501 set_gain_envelope_visibility ();
503 MouseModeChanged (); /* EMIT SIGNAL */
507 Editor::step_mouse_mode (bool next)
509 switch (current_mouse_mode()) {
512 if (Profile->get_sae()) {
513 set_mouse_mode (MouseZoom);
515 set_mouse_mode (MouseRange);
518 set_mouse_mode (MouseTimeFX);
523 if (next) set_mouse_mode (MouseDraw);
524 else set_mouse_mode (MouseObject);
528 if (next) set_mouse_mode (MouseZoom);
529 else set_mouse_mode (MouseRange);
534 if (Profile->get_sae()) {
535 set_mouse_mode (MouseTimeFX);
537 set_mouse_mode (MouseGain);
540 if (Profile->get_sae()) {
541 set_mouse_mode (MouseObject);
543 set_mouse_mode (MouseDraw);
549 if (next) set_mouse_mode (MouseTimeFX);
550 else set_mouse_mode (MouseZoom);
555 set_mouse_mode (MouseAudition);
557 if (Profile->get_sae()) {
558 set_mouse_mode (MouseZoom);
560 set_mouse_mode (MouseGain);
566 if (next) set_mouse_mode (MouseObject);
567 else set_mouse_mode (MouseTimeFX);
573 Editor::toggle_internal_editing_from_double_click (GdkEvent* event)
575 if (_drags->active()) {
576 _drags->end_grab (event);
579 ActionManager::do_action ("MouseMode", "toggle-internal-edit");
581 /* prevent reversion of edit cursor on button release */
583 pre_press_cursor = 0;
589 Editor::button_selection (ArdourCanvas::Item* /*item*/, GdkEvent* event, ItemType item_type)
591 /* in object/audition/timefx/gain-automation mode,
592 any button press sets the selection if the object
593 can be selected. this is a bit of hack, because
594 we want to avoid this if the mouse operation is a
597 note: not dbl-click or triple-click
599 Also note that there is no region selection in internal edit mode, otherwise
600 for operations operating on the selection (e.g. cut) it is not obvious whether
601 to cut notes or regions.
604 if (((mouse_mode != MouseObject) &&
605 (mouse_mode != MouseAudition || item_type != RegionItem) &&
606 (mouse_mode != MouseTimeFX || item_type != RegionItem) &&
607 (mouse_mode != MouseGain) &&
608 (mouse_mode != MouseDraw)) ||
609 ((event->type != GDK_BUTTON_PRESS && event->type != GDK_BUTTON_RELEASE) || event->button.button > 3) ||
610 (internal_editing() && mouse_mode != MouseTimeFX)) {
615 if (event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) {
617 if ((event->button.state & Keyboard::RelevantModifierKeyMask) && event->button.button != 1) {
619 /* almost no selection action on modified button-2 or button-3 events */
621 if (item_type != RegionItem && event->button.button != 2) {
627 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
628 bool press = (event->type == GDK_BUTTON_PRESS);
632 if (!get_smart_mode() || (_join_object_range_state != JOIN_OBJECT_RANGE_RANGE)) {
634 if (mouse_mode != MouseRange) {
635 set_selected_regionview_from_click (press, op);
637 /* don't change the selection unless the
638 clicked track is not currently selected. if
639 so, "collapse" the selection to just this
642 if (!selection->selected (clicked_axisview)) {
643 set_selected_track_as_side_effect (Selection::Set);
647 if (mouse_mode != MouseRange) {
648 set_selected_regionview_from_click (press, op);
654 case RegionViewNameHighlight:
656 case LeftFrameHandle:
657 case RightFrameHandle:
658 if ( mouse_mode != MouseRange ) {
659 set_selected_regionview_from_click (press, op);
660 } else if (event->type == GDK_BUTTON_PRESS) {
661 set_selected_track_as_side_effect (op);
665 case FadeInHandleItem:
667 case FadeOutHandleItem:
669 case StartCrossFadeItem:
670 case EndCrossFadeItem:
671 if ( mouse_mode != MouseRange ) {
672 set_selected_regionview_from_click (press, op);
673 } else if (event->type == GDK_BUTTON_PRESS) {
674 set_selected_track_as_side_effect (op);
678 case ControlPointItem:
679 set_selected_track_as_side_effect (op);
680 if ( mouse_mode != MouseRange ) {
681 set_selected_control_point_from_click (press, op);
686 /* for context click, select track */
687 if (event->button.button == 3) {
688 selection->clear_tracks ();
689 set_selected_track_as_side_effect (op);
693 case AutomationTrackItem:
694 set_selected_track_as_side_effect (op);
703 Editor::button_press_handler_1 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
705 /* single mouse clicks on any of these item types operate
706 independent of mouse mode, mostly because they are
707 not on the main track canvas or because we want
712 case PlayheadCursorItem:
713 _drags->set (new CursorDrag (this, *playhead_cursor, true), event);
717 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
718 hide_marker (item, event);
720 _drags->set (new MarkerDrag (this, item), event);
724 case TempoMarkerItem:
726 TempoMarker* m = reinterpret_cast<TempoMarker*> (item->get_data ("marker"));
729 new TempoMarkerDrag (
732 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
739 case MeterMarkerItem:
741 MeterMarker* m = reinterpret_cast<MeterMarker*> (item->get_data ("marker"));
744 new MeterMarkerDrag (
747 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
755 _drags->set (new VideoTimeLineDrag (this, item), event);
762 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
763 _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
769 case RangeMarkerBarItem:
770 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
771 _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
773 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateRangeMarker), event);
778 case CdMarkerBarItem:
779 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
780 _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
782 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateCDMarker), event);
787 case TransportMarkerBarItem:
788 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
789 _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
791 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateTransportMarker), event);
800 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
801 /* special case: allow trim of range selections in joined object mode;
802 in theory eff should equal MouseRange in this case, but it doesn't
803 because entering the range selection canvas item results in entered_regionview
804 being set to 0, so update_join_object_range_location acts as if we aren't
807 if (item_type == StartSelectionTrimItem) {
808 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
809 } else if (item_type == EndSelectionTrimItem) {
810 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
814 Editing::MouseMode eff = effective_mouse_mode ();
816 /* special case: allow drag of region fade in/out in object mode with join object/range enabled */
817 if (item_type == FadeInHandleItem || item_type == FadeOutHandleItem) {
821 /* there is no Range mode when in internal edit mode */
822 if (eff == MouseRange && internal_editing()) {
829 case StartSelectionTrimItem:
830 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
833 case EndSelectionTrimItem:
834 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
838 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
839 start_selection_grab (item, event);
841 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
842 /* grab selection for moving */
843 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionMove), event);
845 double const y = event->button.y;
846 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
848 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
849 if ( get_smart_mode() && atv) {
850 /* smart "join" mode: drag automation */
851 _drags->set (new AutomationRangeDrag (this, atv, selection->time), event, _cursors->up_down);
853 /* this was debated, but decided the more common action was to
854 make a new selection */
855 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
862 if (internal_editing()) {
863 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
864 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
868 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::RangeSelectModifier)) {
869 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionExtend), event);
871 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
877 case RegionViewNameHighlight:
878 if (!clicked_regionview->region()->locked()) {
879 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
885 if (!internal_editing()) {
886 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::RangeSelectModifier)) {
887 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionExtend), event);
889 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
899 if (internal_editing()) {
900 /* trim notes if we're in internal edit mode and near the ends of the note */
901 NoteBase* cn = reinterpret_cast<NoteBase*>(item->get_data ("notebase"));
903 if (cn && cn->big_enough_to_trim() && cn->mouse_near_ends()) {
904 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
906 _drags->set (new NoteDrag (this, item), event);
912 if (internal_editing()) {
913 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
914 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
928 if (internal_editing()) {
929 NoteBase* cn = reinterpret_cast<NoteBase*> (item->get_data ("notebase"));
931 if (cn->mouse_near_ends()) {
932 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
934 _drags->set (new NoteDrag (this, item), event);
944 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)) &&
945 event->type == GDK_BUTTON_PRESS) {
947 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
949 } else if (event->type == GDK_BUTTON_PRESS) {
952 case FadeInHandleItem:
954 _drags->set (new FadeInDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), selection->regions), event, _cursors->fade_in);
958 case FadeOutHandleItem:
960 _drags->set (new FadeOutDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), selection->regions), event, _cursors->fade_out);
964 case StartCrossFadeItem:
965 case EndCrossFadeItem:
966 /* we might allow user to grab inside the fade to trim a region with preserve_fade_anchor. for not this is not fully implemented */
967 // if (!clicked_regionview->region()->locked()) {
968 // _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer(), true), event);
973 case FeatureLineItem:
975 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier)) {
976 remove_transient(item);
980 _drags->set (new FeatureLineDrag (this, item), event);
986 if (dynamic_cast<AutomationRegionView*> (clicked_regionview)) {
987 /* click on an automation region view; do nothing here and let the ARV's signal handler
993 if (internal_editing ()) {
997 /* click on a normal region view */
998 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
999 add_region_copy_drag (item, event, clicked_regionview);
1000 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
1001 add_region_brush_drag (item, event, clicked_regionview);
1003 add_region_drag (item, event, clicked_regionview);
1007 // if (!internal_editing() && (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE && !selection->regions.empty())) {
1008 // _drags->add (new SelectionDrag (this, clicked_axisview->get_selection_rect (clicked_selection)->rect, SelectionDrag::SelectionMove));
1011 _drags->start_grab (event);
1015 case RegionViewNameHighlight:
1016 case LeftFrameHandle:
1017 case RightFrameHandle:
1018 if (!clicked_regionview->region()->locked()) {
1019 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1024 case RegionViewName:
1026 /* rename happens on edit clicks */
1027 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer()), event);
1032 case ControlPointItem:
1033 _drags->set (new ControlPointDrag (this, item), event);
1037 case AutomationLineItem:
1038 _drags->set (new LineDrag (this, item), event);
1043 if (internal_editing()) {
1044 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
1045 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
1049 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
1053 case AutomationTrackItem:
1055 TimeAxisView* parent = clicked_axisview->get_parent ();
1056 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (clicked_axisview);
1058 if (parent && dynamic_cast<MidiTimeAxisView*> (parent) && atv->show_regions ()) {
1060 RouteTimeAxisView* p = dynamic_cast<RouteTimeAxisView*> (parent);
1062 boost::shared_ptr<Playlist> pl = p->track()->playlist ();
1063 if (pl->n_regions() == 0) {
1064 /* Parent has no regions; create one so that we have somewhere to put automation */
1065 _drags->set (new RegionCreateDrag (this, item, parent), event);
1067 /* See if there's a region before the click that we can extend, and extend it if so */
1068 framepos_t const t = canvas_event_sample (event);
1069 boost::shared_ptr<Region> prev = pl->find_next_region (t, End, -1);
1071 _drags->set (new RegionCreateDrag (this, item, parent), event);
1073 prev->set_length (t - prev->position ());
1077 /* rubberband drag to select automation points */
1078 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
1085 if ( get_smart_mode() ) {
1086 /* we're in "smart" joined mode, and we've clicked on a Selection */
1087 double const y = event->button.y;
1088 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
1090 /* if we're over an automation track, start a drag of its data */
1091 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
1093 _drags->set (new AutomationRangeDrag (this, atv, selection->time), event, _cursors->up_down);
1096 /* if we're over a track and a region, and in the `object' part of a region,
1097 put a selection around the region and drag both
1099 /* RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1100 if (rtv && _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
1101 boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track> (rtv->route ());
1103 boost::shared_ptr<Playlist> pl = t->playlist ();
1106 boost::shared_ptr<Region> r = pl->top_region_at (canvas_event_sample (event));
1108 RegionView* rv = rtv->view()->find_view (r);
1109 clicked_selection = select_range (rv->region()->position(),
1110 rv->region()->last_frame()+1);
1111 _drags->add (new SelectionDrag (this, item, SelectionDrag::SelectionMove));
1112 list<RegionView*> rvs;
1114 _drags->add (new RegionMoveDrag (this, item, rv, rvs, false, false));
1115 _drags->start_grab (event);
1139 switch (item_type) {
1141 _drags->set (new LineDrag (this, item), event);
1144 case ControlPointItem:
1145 _drags->set (new ControlPointDrag (this, item), event);
1151 AudioRegionView* arv = dynamic_cast<AudioRegionView *> (clicked_regionview);
1153 _drags->set (new AutomationRangeDrag (this, arv, selection->time), event, _cursors->up_down);
1154 _drags->start_grab (event);
1160 case AutomationLineItem:
1161 _drags->set (new LineDrag (this, item), event);
1171 if (event->type == GDK_BUTTON_PRESS) {
1172 _drags->set (new MouseZoomDrag (this, item), event);
1179 if (internal_editing() && item_type == NoteItem) {
1180 /* drag notes if we're in internal edit mode */
1181 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
1183 } else if (clicked_regionview) {
1185 _drags->set (new TimeFXDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1191 _drags->set (new ScrubDrag (this, item), event);
1192 scrub_reversals = 0;
1193 scrub_reverse_distance = 0;
1194 last_scrub_x = event->button.x;
1195 scrubbing_direction = 0;
1196 set_canvas_cursor (_cursors->transparent);
1208 Editor::button_press_handler_2 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1210 Editing::MouseMode const eff = effective_mouse_mode ();
1213 switch (item_type) {
1215 if (internal_editing ()) {
1216 /* no region drags in internal edit mode */
1220 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
1221 add_region_copy_drag (item, event, clicked_regionview);
1223 add_region_drag (item, event, clicked_regionview);
1225 _drags->start_grab (event);
1228 case ControlPointItem:
1229 _drags->set (new ControlPointDrag (this, item), event);
1237 switch (item_type) {
1238 case RegionViewNameHighlight:
1239 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1243 case LeftFrameHandle:
1244 case RightFrameHandle:
1245 if (!internal_editing ()) {
1246 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1251 case RegionViewName:
1252 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer()), event);
1266 /* relax till release */
1272 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1273 temporal_zoom_to_frame (false, canvas_event_sample (event));
1275 temporal_zoom_to_frame (true, canvas_event_sample(event));
1288 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1290 if (event->type == GDK_2BUTTON_PRESS) {
1291 _drags->mark_double_click ();
1292 gdk_pointer_ungrab (GDK_CURRENT_TIME);
1296 if (event->type != GDK_BUTTON_PRESS) {
1300 Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->_track_canvas_viewport->get_window();
1302 if (canvas_window) {
1303 Glib::RefPtr<const Gdk::Window> pointer_window;
1306 Gdk::ModifierType mask;
1308 pointer_window = canvas_window->get_pointer (x, y, mask);
1310 if (pointer_window == _track_canvas->get_window()) {
1311 _track_canvas->window_to_canvas (x, y, wx, wy);
1315 pre_press_cursor = current_canvas_cursor;
1317 _track_canvas->grab_focus();
1319 if (_session && _session->actively_recording()) {
1323 if (internal_editing()) {
1324 bool leave_internal_edit_mode = false;
1326 switch (item_type) {
1331 if (!dynamic_cast<MidiRegionView*> (clicked_regionview) && !dynamic_cast<AutomationRegionView*> (clicked_regionview)) {
1332 leave_internal_edit_mode = true;
1336 case PlayheadCursorItem:
1338 case TempoMarkerItem:
1339 case MeterMarkerItem:
1343 case RangeMarkerBarItem:
1344 case CdMarkerBarItem:
1345 case TransportMarkerBarItem:
1347 /* button press on these events never does anything to
1348 change the editing mode.
1356 if (leave_internal_edit_mode) {
1357 ActionManager::do_action ("MouseMode", "toggle-internal-edit");
1361 button_selection (item, event, item_type);
1363 if (!_drags->active () &&
1364 (Keyboard::is_delete_event (&event->button) ||
1365 Keyboard::is_context_menu_event (&event->button) ||
1366 Keyboard::is_edit_event (&event->button))) {
1368 /* handled by button release */
1372 //not rolling, range mode click + join_play_range : locate the PH here
1373 if ( !_drags->active () && !_session->transport_rolling() && ( effective_mouse_mode() == MouseRange ) && Config->get_always_play_range() ) {
1374 framepos_t where = canvas_event_sample (event);
1376 _session->request_locate (where, false);
1379 switch (event->button.button) {
1381 return button_press_handler_1 (item, event, item_type);
1385 return button_press_handler_2 (item, event, item_type);
1392 return button_press_dispatch (&event->button);
1401 Editor::button_press_dispatch (GdkEventButton* ev)
1403 /* this function is intended only for buttons 4 and above.
1406 Gtkmm2ext::MouseButton b (ev->state, ev->button);
1407 return button_bindings->activate (b, Gtkmm2ext::Bindings::Press);
1411 Editor::button_release_dispatch (GdkEventButton* ev)
1413 /* this function is intended only for buttons 4 and above.
1416 Gtkmm2ext::MouseButton b (ev->state, ev->button);
1417 return button_bindings->activate (b, Gtkmm2ext::Bindings::Release);
1421 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1423 framepos_t where = canvas_event_sample (event);
1424 AutomationTimeAxisView* atv = 0;
1426 if (pre_press_cursor) {
1427 set_canvas_cursor (pre_press_cursor);
1428 pre_press_cursor = 0;
1431 /* no action if we're recording */
1433 if (_session && _session->actively_recording()) {
1437 /* see if we're finishing a drag */
1439 bool were_dragging = false;
1440 if (_drags->active ()) {
1441 bool const r = _drags->end_grab (event);
1443 /* grab dragged, so do nothing else */
1447 were_dragging = true;
1450 update_region_layering_order_editor ();
1452 /* edit events get handled here */
1454 if (!_drags->active () && Keyboard::is_edit_event (&event->button)) {
1455 switch (item_type) {
1457 show_region_properties ();
1460 case TempoMarkerItem: {
1462 TempoMarker* tempo_marker;
1464 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
1465 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
1469 if ((tempo_marker = dynamic_cast<TempoMarker*> (marker)) == 0) {
1470 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
1474 edit_tempo_marker (*tempo_marker);
1478 case MeterMarkerItem: {
1480 MeterMarker* meter_marker;
1482 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
1483 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
1487 if ((meter_marker = dynamic_cast<MeterMarker*> (marker)) == 0) {
1488 fatal << _("programming error: marker for meter is not a meter marker!") << endmsg;
1491 edit_meter_marker (*meter_marker);
1495 case RegionViewName:
1496 if (clicked_regionview->name_active()) {
1497 return mouse_rename_region (item, event);
1501 case ControlPointItem:
1502 edit_control_point (item);
1511 /* context menu events get handled here */
1512 if (Keyboard::is_context_menu_event (&event->button)) {
1514 context_click_event = *event;
1516 if (!_drags->active ()) {
1518 /* no matter which button pops up the context menu, tell the menu
1519 widget to use button 1 to drive menu selection.
1522 switch (item_type) {
1524 case FadeInHandleItem:
1526 case FadeOutHandleItem:
1527 popup_fade_context_menu (1, event->button.time, item, item_type);
1530 case StartCrossFadeItem:
1531 popup_xfade_in_context_menu (1, event->button.time, item, item_type);
1534 case EndCrossFadeItem:
1535 popup_xfade_out_context_menu (1, event->button.time, item, item_type);
1539 popup_track_context_menu (1, event->button.time, item_type, false);
1543 case RegionViewNameHighlight:
1544 case LeftFrameHandle:
1545 case RightFrameHandle:
1546 case RegionViewName:
1547 popup_track_context_menu (1, event->button.time, item_type, false);
1551 popup_track_context_menu (1, event->button.time, item_type, true);
1554 case AutomationTrackItem:
1555 popup_track_context_menu (1, event->button.time, item_type, false);
1559 case RangeMarkerBarItem:
1560 case TransportMarkerBarItem:
1561 case CdMarkerBarItem:
1565 popup_ruler_menu (where, item_type);
1569 marker_context_menu (&event->button, item);
1572 case TempoMarkerItem:
1573 tempo_or_meter_marker_context_menu (&event->button, item);
1576 case MeterMarkerItem:
1577 tempo_or_meter_marker_context_menu (&event->button, item);
1580 case CrossfadeViewItem:
1581 popup_track_context_menu (1, event->button.time, item_type, false);
1584 case ControlPointItem:
1585 popup_control_point_context_menu (item, event);
1596 /* delete events get handled here */
1598 Editing::MouseMode const eff = effective_mouse_mode ();
1600 if (!_drags->active () && Keyboard::is_delete_event (&event->button)) {
1602 switch (item_type) {
1603 case TempoMarkerItem:
1604 remove_tempo_marker (item);
1607 case MeterMarkerItem:
1608 remove_meter_marker (item);
1612 remove_marker (*item, event);
1616 if (eff == MouseObject) {
1617 remove_clicked_region ();
1621 case ControlPointItem:
1622 remove_control_point (item);
1626 remove_midi_note (item, event);
1635 switch (event->button.button) {
1638 switch (item_type) {
1639 /* see comments in button_press_handler */
1640 case PlayheadCursorItem:
1643 case AutomationLineItem:
1644 case StartSelectionTrimItem:
1645 case EndSelectionTrimItem:
1649 if (!_dragging_playhead) {
1650 snap_to_with_modifier (where, event, 0, true);
1651 mouse_add_new_marker (where);
1655 case CdMarkerBarItem:
1656 if (!_dragging_playhead) {
1657 // if we get here then a dragged range wasn't done
1658 snap_to_with_modifier (where, event, 0, true);
1659 mouse_add_new_marker (where, true);
1664 if (!_dragging_playhead) {
1665 snap_to_with_modifier (where, event);
1666 mouse_add_new_tempo_event (where);
1671 if (!_dragging_playhead) {
1672 mouse_add_new_meter_event (pixel_to_sample (event->button.x));
1683 switch (item_type) {
1684 case AutomationTrackItem:
1685 atv = dynamic_cast<AutomationTimeAxisView*>(clicked_axisview);
1687 bool with_guard_points = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
1688 atv->add_automation_event (event, where, event->button.y, with_guard_points);
1698 switch (item_type) {
1701 /* check that we didn't drag before releasing, since
1702 its really annoying to create new control
1703 points when doing this.
1705 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (clicked_regionview);
1706 if (!were_dragging && arv) {
1707 bool with_guard_points = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
1708 arv->add_gain_point_event (item, event, with_guard_points);
1714 case AutomationTrackItem: {
1715 bool with_guard_points = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
1716 dynamic_cast<AutomationTimeAxisView*>(clicked_axisview)->
1717 add_automation_event (event, where, event->button.y, with_guard_points);
1727 set_canvas_cursor (current_canvas_cursor);
1728 if (scrubbing_direction == 0) {
1729 /* no drag, just a click */
1730 switch (item_type) {
1732 play_selected_region ();
1738 /* make sure we stop */
1739 _session->request_transport_speed (0.0);
1748 /* do any (de)selection operations that should occur on button release */
1749 button_selection (item, event, item_type);
1758 switch (item_type) {
1760 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1762 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier|Keyboard::SecondaryModifier))) {
1765 // Button2 click is unused
1780 // x_style_paste (where, 1.0);
1801 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1808 switch (item_type) {
1809 case ControlPointItem:
1810 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1811 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1816 at_y = cp->get_y ();
1817 cp->i2w (at_x, at_y);
1821 fraction = 1.0 - (cp->get_y() / cp->line().height());
1823 if (is_drawable() && !_drags->active ()) {
1824 set_canvas_cursor (_cursors->fader);
1827 _verbose_cursor->set (cp->line().get_verbose_cursor_string (fraction), at_x, at_y);
1828 _verbose_cursor->show ();
1833 if (mouse_mode == MouseGain) {
1834 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1836 line->set_outline_color (ARDOUR_UI::config()->get_canvasvar_EnteredGainLine());
1838 if (is_drawable()) {
1839 set_canvas_cursor (_cursors->fader);
1844 case AutomationLineItem:
1845 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1846 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1848 line->set_outline_color (ARDOUR_UI::config()->get_canvasvar_EnteredAutomationLine());
1850 if (is_drawable()) {
1851 set_canvas_cursor (_cursors->fader);
1856 case RegionViewNameHighlight:
1857 if (is_drawable() && effective_mouse_mode() == MouseObject && entered_regionview) {
1858 set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1859 _over_region_trim_target = true;
1863 case LeftFrameHandle:
1864 case RightFrameHandle:
1865 if (is_drawable() && effective_mouse_mode() == MouseObject && !internal_editing() && entered_regionview) {
1866 set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1870 case StartSelectionTrimItem:
1871 if (is_drawable()) {
1872 set_canvas_cursor (_cursors->left_side_trim);
1875 case EndSelectionTrimItem:
1876 if (is_drawable()) {
1877 set_canvas_cursor (_cursors->right_side_trim);
1881 case PlayheadCursorItem:
1882 if (is_drawable()) {
1883 switch (_edit_point) {
1885 set_canvas_cursor (_cursors->grabber_edit_point);
1888 set_canvas_cursor (_cursors->grabber);
1894 case RegionViewName:
1896 /* when the name is not an active item, the entire name highlight is for trimming */
1898 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1899 if (mouse_mode == MouseObject && is_drawable()) {
1900 set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1901 _over_region_trim_target = true;
1907 case AutomationTrackItem:
1908 if (is_drawable()) {
1909 Gdk::Cursor *cursor;
1910 switch (mouse_mode) {
1912 cursor = _cursors->selector;
1915 cursor = _cursors->zoom_in;
1918 cursor = _cursors->cross_hair;
1922 set_canvas_cursor (cursor);
1924 AutomationTimeAxisView* atv;
1925 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1926 clear_entered_track = false;
1927 set_entered_track (atv);
1933 case RangeMarkerBarItem:
1934 case TransportMarkerBarItem:
1935 case CdMarkerBarItem:
1938 if (is_drawable()) {
1939 set_canvas_cursor (_cursors->timebar);
1944 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1947 entered_marker = marker;
1948 marker->set_color_rgba (ARDOUR_UI::config()->get_canvasvar_EnteredMarker());
1950 case MeterMarkerItem:
1951 case TempoMarkerItem:
1952 if (is_drawable()) {
1953 set_canvas_cursor (_cursors->timebar);
1957 case FadeInHandleItem:
1958 if (mouse_mode == MouseObject && !internal_editing()) {
1959 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
1961 RegionView* rv = static_cast<RegionView*>(item->get_data ("regionview"));
1962 rect->set_fill_color (rv->get_fill_color());
1963 set_canvas_cursor (_cursors->fade_in);
1968 case FadeOutHandleItem:
1969 if (mouse_mode == MouseObject && !internal_editing()) {
1970 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
1972 RegionView* rv = static_cast<RegionView*>(item->get_data ("regionview"));
1973 rect->set_fill_color (rv->get_fill_color ());
1974 set_canvas_cursor (_cursors->fade_out);
1978 case FeatureLineItem:
1980 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1981 line->set_outline_color (0xFF0000FF);
1986 if ( get_smart_mode() ) {
1987 set_canvas_cursor ();
1995 /* second pass to handle entered track status in a comprehensible way.
1998 switch (item_type) {
2000 case AutomationLineItem:
2001 case ControlPointItem:
2002 /* these do not affect the current entered track state */
2003 clear_entered_track = false;
2006 case AutomationTrackItem:
2007 /* handled above already */
2011 set_entered_track (0);
2019 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent*, ItemType item_type)
2028 switch (item_type) {
2029 case ControlPointItem:
2030 if (is_drawable()) {
2031 set_canvas_cursor (current_canvas_cursor);
2034 _verbose_cursor->hide ();
2037 case RegionViewNameHighlight:
2038 case LeftFrameHandle:
2039 case RightFrameHandle:
2040 case StartSelectionTrimItem:
2041 case EndSelectionTrimItem:
2042 case PlayheadCursorItem:
2044 _over_region_trim_target = false;
2046 if (is_drawable()) {
2047 set_canvas_cursor (current_canvas_cursor);
2052 case AutomationLineItem:
2053 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
2055 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
2057 line->set_outline_color (al->get_line_color());
2060 if (is_drawable()) {
2061 set_canvas_cursor (current_canvas_cursor);
2065 case RegionViewName:
2066 /* see enter_handler() for notes */
2067 _over_region_trim_target = false;
2069 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
2070 if (is_drawable() && mouse_mode == MouseObject) {
2071 set_canvas_cursor (current_canvas_cursor);
2076 case RangeMarkerBarItem:
2077 case TransportMarkerBarItem:
2078 case CdMarkerBarItem:
2082 if (is_drawable()) {
2083 set_canvas_cursor (current_canvas_cursor);
2088 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2092 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
2093 location_flags_changed (loc, this);
2096 case MeterMarkerItem:
2097 case TempoMarkerItem:
2099 if (is_drawable()) {
2100 set_canvas_cursor (current_canvas_cursor);
2105 case FadeInHandleItem:
2106 case FadeOutHandleItem:
2107 rv = static_cast<RegionView*>(item->get_data ("regionview"));
2109 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
2111 rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_InactiveFadeHandle());
2114 set_canvas_cursor (current_canvas_cursor);
2117 case AutomationTrackItem:
2118 if (is_drawable()) {
2119 set_canvas_cursor (current_canvas_cursor);
2120 clear_entered_track = true;
2121 Glib::signal_idle().connect (sigc::mem_fun(*this, &Editor::left_automation_track));
2124 case FeatureLineItem:
2126 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
2127 line->set_outline_color (ARDOUR_UI::config()->get_canvasvar_ZeroLine());
2139 Editor::left_automation_track ()
2141 if (clear_entered_track) {
2142 set_entered_track (0);
2143 clear_entered_track = false;
2149 Editor::scrub (framepos_t frame, double current_x)
2153 if (scrubbing_direction == 0) {
2155 _session->request_locate (frame, false);
2156 _session->request_transport_speed (0.1);
2157 scrubbing_direction = 1;
2161 if (last_scrub_x > current_x) {
2163 /* pointer moved to the left */
2165 if (scrubbing_direction > 0) {
2167 /* we reversed direction to go backwards */
2170 scrub_reverse_distance += (int) (last_scrub_x - current_x);
2174 /* still moving to the left (backwards) */
2176 scrub_reversals = 0;
2177 scrub_reverse_distance = 0;
2179 delta = 0.01 * (last_scrub_x - current_x);
2180 _session->request_transport_speed_nonzero (_session->transport_speed() - delta);
2184 /* pointer moved to the right */
2186 if (scrubbing_direction < 0) {
2187 /* we reversed direction to go forward */
2190 scrub_reverse_distance += (int) (current_x - last_scrub_x);
2193 /* still moving to the right */
2195 scrub_reversals = 0;
2196 scrub_reverse_distance = 0;
2198 delta = 0.01 * (current_x - last_scrub_x);
2199 _session->request_transport_speed_nonzero (_session->transport_speed() + delta);
2203 /* if there have been more than 2 opposite motion moves detected, or one that moves
2204 back more than 10 pixels, reverse direction
2207 if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
2209 if (scrubbing_direction > 0) {
2210 /* was forwards, go backwards */
2211 _session->request_transport_speed (-0.1);
2212 scrubbing_direction = -1;
2214 /* was backwards, go forwards */
2215 _session->request_transport_speed (0.1);
2216 scrubbing_direction = 1;
2219 scrub_reverse_distance = 0;
2220 scrub_reversals = 0;
2224 last_scrub_x = current_x;
2228 Editor::motion_handler (ArdourCanvas::Item* /*item*/, GdkEvent* event, bool from_autoscroll)
2230 _last_motion_y = event->motion.y;
2232 if (event->motion.is_hint) {
2235 /* We call this so that MOTION_NOTIFY events continue to be
2236 delivered to the canvas. We need to do this because we set
2237 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
2238 the density of the events, at the expense of a round-trip
2239 to the server. Given that this will mostly occur on cases
2240 where DISPLAY = :0.0, and given the cost of what the motion
2241 event might do, its a good tradeoff.
2244 _track_canvas->get_pointer (x, y);
2247 if (current_stepping_trackview) {
2248 /* don't keep the persistent stepped trackview if the mouse moves */
2249 current_stepping_trackview = 0;
2250 step_timeout.disconnect ();
2253 if (_session && _session->actively_recording()) {
2254 /* Sorry. no dragging stuff around while we record */
2258 JoinObjectRangeState const old = _join_object_range_state;
2259 update_join_object_range_location (event->motion.x, event->motion.y);
2261 if (!_internal_editing && _join_object_range_state != old) {
2262 set_canvas_cursor ();
2265 if (!_internal_editing && _over_region_trim_target) {
2266 set_canvas_cursor_for_region_view (event->motion.x, entered_regionview);
2269 bool handled = false;
2270 if (_drags->active ()) {
2271 handled = _drags->motion_handler (event, from_autoscroll);
2278 track_canvas_motion (event);
2283 Editor::can_remove_control_point (ArdourCanvas::Item* item)
2285 ControlPoint* control_point;
2287 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2288 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2292 AutomationLine& line = control_point->line ();
2293 if (dynamic_cast<AudioRegionGainLine*> (&line)) {
2294 /* we shouldn't remove the first or last gain point in region gain lines */
2295 if (line.is_last_point(*control_point) || line.is_first_point(*control_point)) {
2304 Editor::remove_control_point (ArdourCanvas::Item* item)
2306 if (!can_remove_control_point (item)) {
2310 ControlPoint* control_point;
2312 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2313 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2317 control_point->line().remove_point (*control_point);
2321 Editor::edit_control_point (ArdourCanvas::Item* item)
2323 ControlPoint* p = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"));
2326 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2330 ControlPointDialog d (p);
2333 if (d.run () != RESPONSE_ACCEPT) {
2337 p->line().modify_point_y (*p, d.get_y_fraction ());
2341 Editor::edit_notes (TimeAxisViewItem& tavi)
2343 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(&tavi);
2349 MidiRegionView::Selection const & s = mrv->selection();
2355 EditNoteDialog* d = new EditNoteDialog (&(*s.begin())->region_view(), s);
2359 d->signal_response().connect (sigc::bind (sigc::mem_fun (*this, &Editor::note_edit_done), d));
2363 Editor::note_edit_done (int r, EditNoteDialog* d)
2370 Editor::visible_order_range (int* low, int* high) const
2372 *low = TimeAxisView::max_order ();
2375 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
2377 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
2379 if (!rtv->hidden()) {
2381 if (*high < rtv->order()) {
2382 *high = rtv->order ();
2385 if (*low > rtv->order()) {
2386 *low = rtv->order ();
2393 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
2395 /* Either add to or set the set the region selection, unless
2396 this is an alignment click (control used)
2399 if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
2400 TimeAxisView* tv = &rv.get_time_axis_view();
2401 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
2403 if (rtv && rtv->is_track()) {
2404 speed = rtv->track()->speed();
2407 framepos_t where = get_preferred_edit_position();
2411 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
2413 align_region (rv.region(), SyncPoint, (framepos_t) (where * speed));
2415 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
2417 align_region (rv.region(), End, (framepos_t) (where * speed));
2421 align_region (rv.region(), Start, (framepos_t) (where * speed));
2428 Editor::collect_new_region_view (RegionView* rv)
2430 latest_regionviews.push_back (rv);
2434 Editor::collect_and_select_new_region_view (RegionView* rv)
2437 latest_regionviews.push_back (rv);
2441 Editor::cancel_selection ()
2443 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2444 (*i)->hide_selection ();
2447 selection->clear ();
2448 clicked_selection = 0;
2452 Editor::cancel_time_selection ()
2454 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2455 (*i)->hide_selection ();
2457 selection->time.clear ();
2458 clicked_selection = 0;
2462 Editor::point_trim (GdkEvent* event, framepos_t new_bound)
2464 RegionView* rv = clicked_regionview;
2466 /* Choose action dependant on which button was pressed */
2467 switch (event->button.button) {
2469 begin_reversible_command (_("start point trim"));
2471 if (selection->selected (rv)) {
2472 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
2473 i != selection->regions.by_layer().end(); ++i)
2475 if (!(*i)->region()->locked()) {
2476 (*i)->region()->clear_changes ();
2477 (*i)->region()->trim_front (new_bound);
2478 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2483 if (!rv->region()->locked()) {
2484 rv->region()->clear_changes ();
2485 rv->region()->trim_front (new_bound);
2486 _session->add_command(new StatefulDiffCommand (rv->region()));
2490 commit_reversible_command();
2494 begin_reversible_command (_("End point trim"));
2496 if (selection->selected (rv)) {
2498 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
2500 if (!(*i)->region()->locked()) {
2501 (*i)->region()->clear_changes();
2502 (*i)->region()->trim_end (new_bound);
2503 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2509 if (!rv->region()->locked()) {
2510 rv->region()->clear_changes ();
2511 rv->region()->trim_end (new_bound);
2512 _session->add_command (new StatefulDiffCommand (rv->region()));
2516 commit_reversible_command();
2525 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* /*event*/)
2530 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2531 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2535 Location* location = find_location_from_marker (marker, is_start);
2536 location->set_hidden (true, this);
2541 Editor::reposition_zoom_rect (framepos_t start, framepos_t end)
2543 double x1 = sample_to_pixel (start);
2544 double x2 = sample_to_pixel (end);
2545 double y2 = _full_canvas_height - 1.0;
2547 zoom_rect->set (ArdourCanvas::Rect (x1, 1.0, x2, y2));
2552 Editor::mouse_rename_region (ArdourCanvas::Item* /*item*/, GdkEvent* /*event*/)
2554 using namespace Gtkmm2ext;
2556 ArdourPrompter prompter (false);
2558 prompter.set_prompt (_("Name for region:"));
2559 prompter.set_initial_text (clicked_regionview->region()->name());
2560 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
2561 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
2562 prompter.show_all ();
2563 switch (prompter.run ()) {
2564 case Gtk::RESPONSE_ACCEPT:
2566 prompter.get_result(str);
2568 clicked_regionview->region()->set_name (str);
2577 Editor::mouse_brush_insert_region (RegionView* rv, framepos_t pos)
2579 /* no brushing without a useful snap setting */
2581 switch (_snap_mode) {
2583 return; /* can't work because it allows region to be placed anywhere */
2588 switch (_snap_type) {
2596 /* don't brush a copy over the original */
2598 if (pos == rv->region()->position()) {
2602 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
2604 if (rtv == 0 || !rtv->is_track()) {
2608 boost::shared_ptr<Playlist> playlist = rtv->playlist();
2609 double speed = rtv->track()->speed();
2611 playlist->clear_changes ();
2612 boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region(), true));
2613 playlist->add_region (new_region, (framepos_t) (pos * speed));
2614 _session->add_command (new StatefulDiffCommand (playlist));
2616 // playlist is frozen, so we have to update manually XXX this is disgusting
2618 playlist->RegionAdded (new_region); /* EMIT SIGNAL */
2622 Editor::track_height_step_timeout ()
2624 if (get_microseconds() - last_track_height_step_timestamp < 250000) {
2625 current_stepping_trackview = 0;
2632 Editor::add_region_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2634 assert (region_view);
2636 if (!region_view->region()->playlist()) {
2640 _region_motion_group->raise_to_top ();
2642 if (Config->get_edit_mode() == Splice) {
2643 _drags->add (new RegionSpliceDrag (this, item, region_view, selection->regions.by_layer()));
2645 _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), false, false));
2650 Editor::add_region_copy_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2652 assert (region_view);
2654 if (!region_view->region()->playlist()) {
2658 _region_motion_group->raise_to_top ();
2660 _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), false, true));
2664 Editor::add_region_brush_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2666 assert (region_view);
2668 if (!region_view->region()->playlist()) {
2672 if (Config->get_edit_mode() == Splice) {
2676 _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), true, false));
2678 begin_reversible_command (Operations::drag_region_brush);
2681 /** Start a grab where a time range is selected, track(s) are selected, and the
2682 * user clicks and drags a region with a modifier in order to create a new region containing
2683 * the section of the clicked region that lies within the time range.
2686 Editor::start_selection_grab (ArdourCanvas::Item* /*item*/, GdkEvent* event)
2688 if (clicked_regionview == 0) {
2692 /* lets try to create new Region for the selection */
2694 vector<boost::shared_ptr<Region> > new_regions;
2695 create_region_from_selection (new_regions);
2697 if (new_regions.empty()) {
2701 /* XXX fix me one day to use all new regions */
2703 boost::shared_ptr<Region> region (new_regions.front());
2705 /* add it to the current stream/playlist.
2707 tricky: the streamview for the track will add a new regionview. we will
2708 catch the signal it sends when it creates the regionview to
2709 set the regionview we want to then drag.
2712 latest_regionviews.clear();
2713 sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
2715 /* A selection grab currently creates two undo/redo operations, one for
2716 creating the new region and another for moving it.
2719 begin_reversible_command (Operations::selection_grab);
2721 boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
2723 playlist->clear_changes ();
2724 clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
2725 _session->add_command(new StatefulDiffCommand (playlist));
2727 commit_reversible_command ();
2731 if (latest_regionviews.empty()) {
2732 /* something went wrong */
2736 /* we need to deselect all other regionviews, and select this one
2737 i'm ignoring undo stuff, because the region creation will take care of it
2739 selection->set (latest_regionviews);
2741 _drags->set (new RegionMoveDrag (this, latest_regionviews.front()->get_canvas_group(), latest_regionviews.front(), latest_regionviews, false, false), event);
2747 if (_drags->active ()) {
2750 selection->clear ();
2755 Editor::set_internal_edit (bool yn)
2757 if (_internal_editing == yn) {
2761 _internal_editing = yn;
2764 pre_internal_mouse_mode = mouse_mode;
2765 pre_internal_snap_type = _snap_type;
2766 pre_internal_snap_mode = _snap_mode;
2768 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2769 (*i)->enter_internal_edit_mode ();
2772 set_snap_to (internal_snap_type);
2773 set_snap_mode (internal_snap_mode);
2777 internal_snap_mode = _snap_mode;
2778 internal_snap_type = _snap_type;
2780 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2781 (*i)->leave_internal_edit_mode ();
2784 if (mouse_mode == MouseDraw && pre_internal_mouse_mode != MouseDraw) {
2785 /* we were drawing .. flip back to something sensible */
2786 set_mouse_mode (pre_internal_mouse_mode);
2789 set_snap_to (pre_internal_snap_type);
2790 set_snap_mode (pre_internal_snap_mode);
2793 set_canvas_cursor ();
2796 /** Update _join_object_range_state which indicate whether we are over the top or bottom half of a region view,
2797 * used by the `join object/range' tool mode.
2800 Editor::update_join_object_range_location (double /*x*/, double y)
2802 /* XXX: actually, this decides based on whether the mouse is in the top
2803 or bottom half of a the waveform part RouteTimeAxisView;
2805 Note that entered_{track,regionview} is not always setup (e.g. if
2806 the mouse is over a TimeSelection), and to get a Region
2807 that we're over requires searching the playlist.
2810 if ( !get_smart_mode() ) {
2811 _join_object_range_state = JOIN_OBJECT_RANGE_NONE;
2815 if (mouse_mode == MouseObject) {
2816 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2817 } else if (mouse_mode == MouseRange) {
2818 _join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
2821 /* XXX: maybe we should make entered_track work in all cases, rather than resorting to this */
2822 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
2826 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
2831 rtv->canvas_display()->canvas_to_item (cx, cy);
2833 double const c = cy / (rtv->view()->child_height() - TimeAxisViewItem::NAME_HIGHLIGHT_SIZE);
2835 _join_object_range_state = c <= 0.5 ? JOIN_OBJECT_RANGE_RANGE : JOIN_OBJECT_RANGE_OBJECT;
2841 Editor::effective_mouse_mode () const
2843 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
2845 } else if (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE) {
2853 Editor::remove_midi_note (ArdourCanvas::Item* item, GdkEvent *)
2855 NoteBase* e = reinterpret_cast<NoteBase*> (item->get_data ("notebase"));
2858 e->region_view().delete_note (e->note ());
2862 Editor::set_canvas_cursor_for_region_view (double x, RegionView* rv)
2864 /* XXX: this check should not be necessary */
2871 ArdourCanvas::Group* g = rv->get_canvas_group ();
2872 ArdourCanvas::Group* p = g->parent ();
2874 /* Compute x in region view parent coordinates */
2876 p->canvas_to_item (x, dy);
2878 boost::optional<ArdourCanvas::Rect> item_bbox = g->bounding_box ();
2880 ArdourCanvas::Rect parent_bbox = g->item_to_parent (item_bbox.get ());
2882 /* Halfway across the region */
2883 double const h = (parent_bbox.x0 + parent_bbox.x1) / 2;
2885 Trimmable::CanTrim ct = rv->region()->can_trim ();
2887 if (ct & Trimmable::FrontTrimEarlier) {
2888 set_canvas_cursor (_cursors->left_side_trim);
2890 set_canvas_cursor (_cursors->left_side_trim_right_only);
2893 if (ct & Trimmable::EndTrimLater) {
2894 set_canvas_cursor (_cursors->right_side_trim);
2896 set_canvas_cursor (_cursors->right_side_trim_left_only);
2901 /** Obtain the pointer position in canvas coordinates */
2903 Editor::get_pointer_position (double& x, double& y) const
2906 _track_canvas->get_pointer (px, py);
2907 _track_canvas->window_to_canvas (px, py, x, y);