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 update_time_selection_display ();
505 MouseModeChanged (); /* EMIT SIGNAL */
509 Editor::update_time_selection_display ()
511 if (smart_mode_action->get_active()) {
512 /* not sure what to do here */
513 if (mouse_mode == MouseObject) {
517 switch (mouse_mode) {
519 selection->clear_objects ();
522 selection->clear_time ();
529 Editor::step_mouse_mode (bool next)
531 switch (current_mouse_mode()) {
534 if (Profile->get_sae()) {
535 set_mouse_mode (MouseZoom);
537 set_mouse_mode (MouseRange);
540 set_mouse_mode (MouseTimeFX);
545 if (next) set_mouse_mode (MouseDraw);
546 else set_mouse_mode (MouseObject);
550 if (next) set_mouse_mode (MouseZoom);
551 else set_mouse_mode (MouseRange);
556 if (Profile->get_sae()) {
557 set_mouse_mode (MouseTimeFX);
559 set_mouse_mode (MouseGain);
562 if (Profile->get_sae()) {
563 set_mouse_mode (MouseObject);
565 set_mouse_mode (MouseDraw);
571 if (next) set_mouse_mode (MouseTimeFX);
572 else set_mouse_mode (MouseZoom);
577 set_mouse_mode (MouseAudition);
579 if (Profile->get_sae()) {
580 set_mouse_mode (MouseZoom);
582 set_mouse_mode (MouseGain);
588 if (next) set_mouse_mode (MouseObject);
589 else set_mouse_mode (MouseTimeFX);
595 Editor::toggle_internal_editing_from_double_click (GdkEvent* event)
597 if (_drags->active()) {
598 _drags->end_grab (event);
601 ActionManager::do_action ("MouseMode", "toggle-internal-edit");
603 /* prevent reversion of edit cursor on button release */
605 pre_press_cursor = 0;
611 Editor::button_selection (ArdourCanvas::Item* /*item*/, GdkEvent* event, ItemType item_type)
613 /* in object/audition/timefx/gain-automation mode,
614 any button press sets the selection if the object
615 can be selected. this is a bit of hack, because
616 we want to avoid this if the mouse operation is a
619 note: not dbl-click or triple-click
621 Also note that there is no region selection in internal edit mode, otherwise
622 for operations operating on the selection (e.g. cut) it is not obvious whether
623 to cut notes or regions.
626 MouseMode eff_mouse_mode = effective_mouse_mode ();
628 if (get_smart_mode() && eff_mouse_mode == MouseRange && event->button.button == 3 && item_type == RegionItem) {
629 /* context clicks are always about object properties, even if
630 we're in range mode within smart mode.
632 eff_mouse_mode = MouseObject;
635 if (((eff_mouse_mode != MouseObject) &&
636 (eff_mouse_mode != MouseAudition || item_type != RegionItem) &&
637 (eff_mouse_mode != MouseTimeFX || item_type != RegionItem) &&
638 (eff_mouse_mode != MouseGain) &&
639 (eff_mouse_mode != MouseDraw)) ||
640 ((event->type != GDK_BUTTON_PRESS && event->type != GDK_BUTTON_RELEASE) || event->button.button > 3) ||
641 (internal_editing() && eff_mouse_mode != MouseTimeFX)) {
646 if (event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) {
648 if ((event->button.state & Keyboard::RelevantModifierKeyMask) && event->button.button != 1) {
650 /* almost no selection action on modified button-2 or button-3 events */
652 if (item_type != RegionItem && event->button.button != 2) {
658 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
659 bool press = (event->type == GDK_BUTTON_PRESS);
664 if (eff_mouse_mode != MouseRange) {
665 set_selected_regionview_from_click (press, op);
667 /* don't change the selection unless the
668 clicked track is not currently selected. if
669 so, "collapse" the selection to just this
672 if (!selection->selected (clicked_axisview)) {
673 set_selected_track_as_side_effect (Selection::Set);
677 if (eff_mouse_mode != MouseRange) {
678 set_selected_regionview_from_click (press, op);
683 case RegionViewNameHighlight:
685 case LeftFrameHandle:
686 case RightFrameHandle:
687 if ( mouse_mode != MouseRange ) {
688 set_selected_regionview_from_click (press, op);
689 } else if (event->type == GDK_BUTTON_PRESS) {
690 set_selected_track_as_side_effect (op);
694 case FadeInHandleItem:
696 case FadeOutHandleItem:
698 case StartCrossFadeItem:
699 case EndCrossFadeItem:
700 if ( mouse_mode != MouseRange ) {
701 set_selected_regionview_from_click (press, op);
702 } else if (event->type == GDK_BUTTON_PRESS) {
703 set_selected_track_as_side_effect (op);
707 case ControlPointItem:
708 set_selected_track_as_side_effect (op);
709 if ( mouse_mode != MouseRange ) {
710 set_selected_control_point_from_click (press, op);
715 /* for context click, select track */
716 if (event->button.button == 3) {
717 selection->clear_tracks ();
718 set_selected_track_as_side_effect (op);
722 case AutomationTrackItem:
723 set_selected_track_as_side_effect (op);
732 Editor::button_press_handler_1 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
734 /* single mouse clicks on any of these item types operate
735 independent of mouse mode, mostly because they are
736 not on the main track canvas or because we want
741 case PlayheadCursorItem:
742 _drags->set (new CursorDrag (this, *playhead_cursor, true), event);
746 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
747 hide_marker (item, event);
749 _drags->set (new MarkerDrag (this, item), event);
753 case TempoMarkerItem:
755 TempoMarker* m = reinterpret_cast<TempoMarker*> (item->get_data ("marker"));
758 new TempoMarkerDrag (
761 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
768 case MeterMarkerItem:
770 MeterMarker* m = reinterpret_cast<MeterMarker*> (item->get_data ("marker"));
773 new MeterMarkerDrag (
776 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
784 _drags->set (new VideoTimeLineDrag (this, item), event);
791 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
792 _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
798 case RangeMarkerBarItem:
799 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
800 _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
802 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateRangeMarker), event);
807 case CdMarkerBarItem:
808 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
809 _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
811 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateCDMarker), event);
816 case TransportMarkerBarItem:
817 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
818 _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
820 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateTransportMarker), event);
829 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
830 /* special case: allow trim of range selections in joined object mode;
831 in theory eff should equal MouseRange in this case, but it doesn't
832 because entering the range selection canvas item results in entered_regionview
833 being set to 0, so update_join_object_range_location acts as if we aren't
836 if (item_type == StartSelectionTrimItem) {
837 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
838 } else if (item_type == EndSelectionTrimItem) {
839 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
843 Editing::MouseMode eff = effective_mouse_mode ();
845 /* special case: allow drag of region fade in/out in object mode with join object/range enabled */
846 if (item_type == FadeInHandleItem || item_type == FadeOutHandleItem) {
850 /* there is no Range mode when in internal edit mode */
851 if (eff == MouseRange && internal_editing()) {
858 case StartSelectionTrimItem:
859 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
862 case EndSelectionTrimItem:
863 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
867 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
868 start_selection_grab (item, event);
870 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
871 /* grab selection for moving */
872 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionMove), event);
874 double const y = event->button.y;
875 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
877 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
878 if ( get_smart_mode() && atv) {
879 /* smart "join" mode: drag automation */
880 _drags->set (new AutomationRangeDrag (this, atv, selection->time), event, _cursors->up_down);
882 /* this was debated, but decided the more common action was to
883 make a new selection */
884 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
891 if (internal_editing()) {
892 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
893 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
897 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::RangeSelectModifier)) {
898 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionExtend), event);
900 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
906 case RegionViewNameHighlight:
907 if (!clicked_regionview->region()->locked()) {
908 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
914 if (!internal_editing()) {
915 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::RangeSelectModifier)) {
916 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionExtend), event);
918 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
928 if (internal_editing()) {
929 /* trim notes if we're in internal edit mode and near the ends of the note */
930 NoteBase* cn = reinterpret_cast<NoteBase*>(item->get_data ("notebase"));
932 if (cn && cn->big_enough_to_trim() && cn->mouse_near_ends()) {
933 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
935 _drags->set (new NoteDrag (this, item), event);
941 if (internal_editing()) {
942 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
943 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
957 if (internal_editing()) {
958 NoteBase* cn = reinterpret_cast<NoteBase*> (item->get_data ("notebase"));
960 if (cn->mouse_near_ends()) {
961 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
963 _drags->set (new NoteDrag (this, item), event);
973 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)) &&
974 event->type == GDK_BUTTON_PRESS) {
976 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
978 } else if (event->type == GDK_BUTTON_PRESS) {
981 case FadeInHandleItem:
983 _drags->set (new FadeInDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), selection->regions), event, _cursors->fade_in);
987 case FadeOutHandleItem:
989 _drags->set (new FadeOutDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), selection->regions), event, _cursors->fade_out);
993 case StartCrossFadeItem:
994 case EndCrossFadeItem:
995 /* we might allow user to grab inside the fade to trim a region with preserve_fade_anchor. for not this is not fully implemented */
996 // if (!clicked_regionview->region()->locked()) {
997 // _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer(), true), event);
1002 case FeatureLineItem:
1004 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier)) {
1005 remove_transient(item);
1009 _drags->set (new FeatureLineDrag (this, item), event);
1015 if (dynamic_cast<AutomationRegionView*> (clicked_regionview)) {
1016 /* click on an automation region view; do nothing here and let the ARV's signal handler
1022 if (internal_editing ()) {
1026 /* click on a normal region view */
1027 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
1028 add_region_copy_drag (item, event, clicked_regionview);
1029 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
1030 add_region_brush_drag (item, event, clicked_regionview);
1032 add_region_drag (item, event, clicked_regionview);
1036 // if (!internal_editing() && (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE && !selection->regions.empty())) {
1037 // _drags->add (new SelectionDrag (this, clicked_axisview->get_selection_rect (clicked_selection)->rect, SelectionDrag::SelectionMove));
1040 _drags->start_grab (event);
1044 case RegionViewNameHighlight:
1045 case LeftFrameHandle:
1046 case RightFrameHandle:
1047 if (!clicked_regionview->region()->locked()) {
1048 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1053 case RegionViewName:
1055 /* rename happens on edit clicks */
1056 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer()), event);
1061 case ControlPointItem:
1062 _drags->set (new ControlPointDrag (this, item), event);
1066 case AutomationLineItem:
1067 _drags->set (new LineDrag (this, item), event);
1072 if (internal_editing()) {
1073 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
1074 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
1078 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
1082 case AutomationTrackItem:
1084 TimeAxisView* parent = clicked_axisview->get_parent ();
1085 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (clicked_axisview);
1087 if (parent && dynamic_cast<MidiTimeAxisView*> (parent) && atv->show_regions ()) {
1089 RouteTimeAxisView* p = dynamic_cast<RouteTimeAxisView*> (parent);
1091 boost::shared_ptr<Playlist> pl = p->track()->playlist ();
1092 if (pl->n_regions() == 0) {
1093 /* Parent has no regions; create one so that we have somewhere to put automation */
1094 _drags->set (new RegionCreateDrag (this, item, parent), event);
1096 /* See if there's a region before the click that we can extend, and extend it if so */
1097 framepos_t const t = canvas_event_sample (event);
1098 boost::shared_ptr<Region> prev = pl->find_next_region (t, End, -1);
1100 _drags->set (new RegionCreateDrag (this, item, parent), event);
1102 prev->set_length (t - prev->position ());
1106 /* rubberband drag to select automation points */
1107 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
1114 if ( get_smart_mode() ) {
1115 /* we're in "smart" joined mode, and we've clicked on a Selection */
1116 double const y = event->button.y;
1117 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
1119 /* if we're over an automation track, start a drag of its data */
1120 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
1122 _drags->set (new AutomationRangeDrag (this, atv, selection->time), event, _cursors->up_down);
1125 /* if we're over a track and a region, and in the `object' part of a region,
1126 put a selection around the region and drag both
1128 /* RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1129 if (rtv && _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
1130 boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track> (rtv->route ());
1132 boost::shared_ptr<Playlist> pl = t->playlist ();
1135 boost::shared_ptr<Region> r = pl->top_region_at (canvas_event_sample (event));
1137 RegionView* rv = rtv->view()->find_view (r);
1138 clicked_selection = select_range (rv->region()->position(),
1139 rv->region()->last_frame()+1);
1140 _drags->add (new SelectionDrag (this, item, SelectionDrag::SelectionMove));
1141 list<RegionView*> rvs;
1143 _drags->add (new RegionMoveDrag (this, item, rv, rvs, false, false));
1144 _drags->start_grab (event);
1168 switch (item_type) {
1170 _drags->set (new LineDrag (this, item), event);
1173 case ControlPointItem:
1174 _drags->set (new ControlPointDrag (this, item), event);
1180 AudioRegionView* arv = dynamic_cast<AudioRegionView *> (clicked_regionview);
1182 _drags->set (new AutomationRangeDrag (this, arv, selection->time), event, _cursors->up_down);
1183 _drags->start_grab (event);
1189 case AutomationLineItem:
1190 _drags->set (new LineDrag (this, item), event);
1200 if (event->type == GDK_BUTTON_PRESS) {
1201 _drags->set (new MouseZoomDrag (this, item), event);
1208 if (internal_editing() && item_type == NoteItem) {
1209 /* drag notes if we're in internal edit mode */
1210 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
1212 } else if (clicked_regionview) {
1214 _drags->set (new TimeFXDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1220 _drags->set (new ScrubDrag (this, item), event);
1221 scrub_reversals = 0;
1222 scrub_reverse_distance = 0;
1223 last_scrub_x = event->button.x;
1224 scrubbing_direction = 0;
1225 set_canvas_cursor (_cursors->transparent);
1237 Editor::button_press_handler_2 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1239 Editing::MouseMode const eff = effective_mouse_mode ();
1242 switch (item_type) {
1244 if (internal_editing ()) {
1245 /* no region drags in internal edit mode */
1249 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
1250 add_region_copy_drag (item, event, clicked_regionview);
1252 add_region_drag (item, event, clicked_regionview);
1254 _drags->start_grab (event);
1257 case ControlPointItem:
1258 _drags->set (new ControlPointDrag (this, item), event);
1266 switch (item_type) {
1267 case RegionViewNameHighlight:
1268 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1272 case LeftFrameHandle:
1273 case RightFrameHandle:
1274 if (!internal_editing ()) {
1275 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1280 case RegionViewName:
1281 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer()), event);
1295 /* relax till release */
1301 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1302 temporal_zoom_to_frame (false, canvas_event_sample (event));
1304 temporal_zoom_to_frame (true, canvas_event_sample(event));
1317 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1319 if (event->type == GDK_2BUTTON_PRESS) {
1320 _drags->mark_double_click ();
1321 gdk_pointer_ungrab (GDK_CURRENT_TIME);
1325 if (event->type != GDK_BUTTON_PRESS) {
1329 Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->_track_canvas_viewport->get_window();
1331 if (canvas_window) {
1332 Glib::RefPtr<const Gdk::Window> pointer_window;
1335 Gdk::ModifierType mask;
1337 pointer_window = canvas_window->get_pointer (x, y, mask);
1339 if (pointer_window == _track_canvas->get_window()) {
1340 _track_canvas->window_to_canvas (x, y, wx, wy);
1344 pre_press_cursor = current_canvas_cursor;
1346 _track_canvas->grab_focus();
1348 if (_session && _session->actively_recording()) {
1352 if (internal_editing()) {
1353 bool leave_internal_edit_mode = false;
1355 switch (item_type) {
1360 if (!dynamic_cast<MidiRegionView*> (clicked_regionview) && !dynamic_cast<AutomationRegionView*> (clicked_regionview)) {
1361 leave_internal_edit_mode = true;
1365 case PlayheadCursorItem:
1367 case TempoMarkerItem:
1368 case MeterMarkerItem:
1372 case RangeMarkerBarItem:
1373 case CdMarkerBarItem:
1374 case TransportMarkerBarItem:
1376 /* button press on these events never does anything to
1377 change the editing mode.
1385 if (leave_internal_edit_mode) {
1386 ActionManager::do_action ("MouseMode", "toggle-internal-edit");
1390 button_selection (item, event, item_type);
1392 if (!_drags->active () &&
1393 (Keyboard::is_delete_event (&event->button) ||
1394 Keyboard::is_context_menu_event (&event->button) ||
1395 Keyboard::is_edit_event (&event->button))) {
1397 /* handled by button release */
1401 //not rolling, range mode click + join_play_range : locate the PH here
1402 if ( !_drags->active () && !_session->transport_rolling() && ( effective_mouse_mode() == MouseRange ) && Config->get_always_play_range() ) {
1403 framepos_t where = canvas_event_sample (event);
1405 _session->request_locate (where, false);
1408 switch (event->button.button) {
1410 return button_press_handler_1 (item, event, item_type);
1414 return button_press_handler_2 (item, event, item_type);
1421 return button_press_dispatch (&event->button);
1430 Editor::button_press_dispatch (GdkEventButton* ev)
1432 /* this function is intended only for buttons 4 and above.
1435 Gtkmm2ext::MouseButton b (ev->state, ev->button);
1436 return button_bindings->activate (b, Gtkmm2ext::Bindings::Press);
1440 Editor::button_release_dispatch (GdkEventButton* ev)
1442 /* this function is intended only for buttons 4 and above.
1445 Gtkmm2ext::MouseButton b (ev->state, ev->button);
1446 return button_bindings->activate (b, Gtkmm2ext::Bindings::Release);
1450 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1452 framepos_t where = canvas_event_sample (event);
1453 AutomationTimeAxisView* atv = 0;
1455 if (pre_press_cursor) {
1456 set_canvas_cursor (pre_press_cursor);
1457 pre_press_cursor = 0;
1460 /* no action if we're recording */
1462 if (_session && _session->actively_recording()) {
1466 /* see if we're finishing a drag */
1468 bool were_dragging = false;
1469 if (_drags->active ()) {
1470 bool const r = _drags->end_grab (event);
1472 /* grab dragged, so do nothing else */
1476 were_dragging = true;
1479 update_region_layering_order_editor ();
1481 /* edit events get handled here */
1483 if (!_drags->active () && Keyboard::is_edit_event (&event->button)) {
1484 switch (item_type) {
1486 show_region_properties ();
1489 case TempoMarkerItem: {
1491 TempoMarker* tempo_marker;
1493 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
1494 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
1498 if ((tempo_marker = dynamic_cast<TempoMarker*> (marker)) == 0) {
1499 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
1503 edit_tempo_marker (*tempo_marker);
1507 case MeterMarkerItem: {
1509 MeterMarker* meter_marker;
1511 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
1512 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
1516 if ((meter_marker = dynamic_cast<MeterMarker*> (marker)) == 0) {
1517 fatal << _("programming error: marker for meter is not a meter marker!") << endmsg;
1520 edit_meter_marker (*meter_marker);
1524 case RegionViewName:
1525 if (clicked_regionview->name_active()) {
1526 return mouse_rename_region (item, event);
1530 case ControlPointItem:
1531 edit_control_point (item);
1540 /* context menu events get handled here */
1541 if (Keyboard::is_context_menu_event (&event->button)) {
1543 context_click_event = *event;
1545 if (!_drags->active ()) {
1547 /* no matter which button pops up the context menu, tell the menu
1548 widget to use button 1 to drive menu selection.
1551 switch (item_type) {
1553 case FadeInHandleItem:
1555 case FadeOutHandleItem:
1556 popup_fade_context_menu (1, event->button.time, item, item_type);
1559 case StartCrossFadeItem:
1560 popup_xfade_in_context_menu (1, event->button.time, item, item_type);
1563 case EndCrossFadeItem:
1564 popup_xfade_out_context_menu (1, event->button.time, item, item_type);
1568 popup_track_context_menu (1, event->button.time, item_type, false);
1572 case RegionViewNameHighlight:
1573 case LeftFrameHandle:
1574 case RightFrameHandle:
1575 case RegionViewName:
1576 popup_track_context_menu (1, event->button.time, item_type, false);
1580 popup_track_context_menu (1, event->button.time, item_type, true);
1583 case AutomationTrackItem:
1584 popup_track_context_menu (1, event->button.time, item_type, false);
1588 case RangeMarkerBarItem:
1589 case TransportMarkerBarItem:
1590 case CdMarkerBarItem:
1594 popup_ruler_menu (where, item_type);
1598 marker_context_menu (&event->button, item);
1601 case TempoMarkerItem:
1602 tempo_or_meter_marker_context_menu (&event->button, item);
1605 case MeterMarkerItem:
1606 tempo_or_meter_marker_context_menu (&event->button, item);
1609 case CrossfadeViewItem:
1610 popup_track_context_menu (1, event->button.time, item_type, false);
1613 case ControlPointItem:
1614 popup_control_point_context_menu (item, event);
1625 /* delete events get handled here */
1627 Editing::MouseMode const eff = effective_mouse_mode ();
1629 if (!_drags->active () && Keyboard::is_delete_event (&event->button)) {
1631 switch (item_type) {
1632 case TempoMarkerItem:
1633 remove_tempo_marker (item);
1636 case MeterMarkerItem:
1637 remove_meter_marker (item);
1641 remove_marker (*item, event);
1645 if (eff == MouseObject) {
1646 remove_clicked_region ();
1650 case ControlPointItem:
1651 remove_control_point (item);
1655 remove_midi_note (item, event);
1664 switch (event->button.button) {
1667 switch (item_type) {
1668 /* see comments in button_press_handler */
1669 case PlayheadCursorItem:
1672 case AutomationLineItem:
1673 case StartSelectionTrimItem:
1674 case EndSelectionTrimItem:
1678 if (!_dragging_playhead) {
1679 snap_to_with_modifier (where, event, 0, true);
1680 mouse_add_new_marker (where);
1684 case CdMarkerBarItem:
1685 if (!_dragging_playhead) {
1686 // if we get here then a dragged range wasn't done
1687 snap_to_with_modifier (where, event, 0, true);
1688 mouse_add_new_marker (where, true);
1693 if (!_dragging_playhead) {
1694 snap_to_with_modifier (where, event);
1695 mouse_add_new_tempo_event (where);
1700 if (!_dragging_playhead) {
1701 mouse_add_new_meter_event (pixel_to_sample (event->button.x));
1712 switch (item_type) {
1713 case AutomationTrackItem:
1714 atv = dynamic_cast<AutomationTimeAxisView*>(clicked_axisview);
1716 bool with_guard_points = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
1717 atv->add_automation_event (event, where, event->button.y, with_guard_points);
1727 switch (item_type) {
1730 /* check that we didn't drag before releasing, since
1731 its really annoying to create new control
1732 points when doing this.
1734 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (clicked_regionview);
1735 if (!were_dragging && arv) {
1736 bool with_guard_points = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
1737 arv->add_gain_point_event (item, event, with_guard_points);
1743 case AutomationTrackItem: {
1744 bool with_guard_points = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
1745 dynamic_cast<AutomationTimeAxisView*>(clicked_axisview)->
1746 add_automation_event (event, where, event->button.y, with_guard_points);
1756 set_canvas_cursor (current_canvas_cursor);
1757 if (scrubbing_direction == 0) {
1758 /* no drag, just a click */
1759 switch (item_type) {
1761 play_selected_region ();
1767 /* make sure we stop */
1768 _session->request_transport_speed (0.0);
1777 /* do any (de)selection operations that should occur on button release */
1778 button_selection (item, event, item_type);
1787 switch (item_type) {
1789 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1791 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier|Keyboard::SecondaryModifier))) {
1794 // Button2 click is unused
1809 // x_style_paste (where, 1.0);
1830 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1837 switch (item_type) {
1838 case ControlPointItem:
1839 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1840 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1845 at_y = cp->get_y ();
1846 cp->i2w (at_x, at_y);
1850 fraction = 1.0 - (cp->get_y() / cp->line().height());
1852 if (is_drawable() && !_drags->active ()) {
1853 set_canvas_cursor (_cursors->fader);
1856 _verbose_cursor->set (cp->line().get_verbose_cursor_string (fraction), at_x, at_y);
1857 _verbose_cursor->show ();
1862 if (mouse_mode == MouseGain) {
1863 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1865 line->set_outline_color (ARDOUR_UI::config()->get_canvasvar_EnteredGainLine());
1867 if (is_drawable()) {
1868 set_canvas_cursor (_cursors->fader);
1873 case AutomationLineItem:
1874 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1875 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1877 line->set_outline_color (ARDOUR_UI::config()->get_canvasvar_EnteredAutomationLine());
1879 if (is_drawable()) {
1880 set_canvas_cursor (_cursors->fader);
1885 case RegionViewNameHighlight:
1886 if (is_drawable() && effective_mouse_mode() == MouseObject && entered_regionview) {
1887 set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1888 _over_region_trim_target = true;
1892 case LeftFrameHandle:
1893 case RightFrameHandle:
1894 if (is_drawable() && effective_mouse_mode() == MouseObject && !internal_editing() && entered_regionview) {
1895 set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1899 case StartSelectionTrimItem:
1900 if (is_drawable()) {
1901 set_canvas_cursor (_cursors->left_side_trim);
1904 case EndSelectionTrimItem:
1905 if (is_drawable()) {
1906 set_canvas_cursor (_cursors->right_side_trim);
1910 case PlayheadCursorItem:
1911 if (is_drawable()) {
1912 switch (_edit_point) {
1914 set_canvas_cursor (_cursors->grabber_edit_point);
1917 set_canvas_cursor (_cursors->grabber);
1923 case RegionViewName:
1925 /* when the name is not an active item, the entire name highlight is for trimming */
1927 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1928 if (mouse_mode == MouseObject && is_drawable()) {
1929 set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1930 _over_region_trim_target = true;
1936 case AutomationTrackItem:
1937 if (is_drawable()) {
1938 Gdk::Cursor *cursor;
1939 switch (mouse_mode) {
1941 cursor = _cursors->selector;
1944 cursor = _cursors->zoom_in;
1947 cursor = _cursors->cross_hair;
1951 set_canvas_cursor (cursor);
1953 AutomationTimeAxisView* atv;
1954 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1955 clear_entered_track = false;
1956 set_entered_track (atv);
1962 case RangeMarkerBarItem:
1963 case TransportMarkerBarItem:
1964 case CdMarkerBarItem:
1967 if (is_drawable()) {
1968 set_canvas_cursor (_cursors->timebar);
1973 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1976 entered_marker = marker;
1977 marker->set_color_rgba (ARDOUR_UI::config()->get_canvasvar_EnteredMarker());
1979 case MeterMarkerItem:
1980 case TempoMarkerItem:
1981 if (is_drawable()) {
1982 set_canvas_cursor (_cursors->timebar);
1986 case FadeInHandleItem:
1987 if (mouse_mode == MouseObject && !internal_editing()) {
1988 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
1990 RegionView* rv = static_cast<RegionView*>(item->get_data ("regionview"));
1991 rect->set_fill_color (rv->get_fill_color());
1992 set_canvas_cursor (_cursors->fade_in);
1997 case FadeOutHandleItem:
1998 if (mouse_mode == MouseObject && !internal_editing()) {
1999 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
2001 RegionView* rv = static_cast<RegionView*>(item->get_data ("regionview"));
2002 rect->set_fill_color (rv->get_fill_color ());
2003 set_canvas_cursor (_cursors->fade_out);
2007 case FeatureLineItem:
2009 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
2010 line->set_outline_color (0xFF0000FF);
2015 if ( get_smart_mode() ) {
2016 set_canvas_cursor ();
2024 /* second pass to handle entered track status in a comprehensible way.
2027 switch (item_type) {
2029 case AutomationLineItem:
2030 case ControlPointItem:
2031 /* these do not affect the current entered track state */
2032 clear_entered_track = false;
2035 case AutomationTrackItem:
2036 /* handled above already */
2040 set_entered_track (0);
2048 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent*, ItemType item_type)
2057 switch (item_type) {
2058 case ControlPointItem:
2059 if (is_drawable()) {
2060 set_canvas_cursor (current_canvas_cursor);
2063 _verbose_cursor->hide ();
2066 case RegionViewNameHighlight:
2067 case LeftFrameHandle:
2068 case RightFrameHandle:
2069 case StartSelectionTrimItem:
2070 case EndSelectionTrimItem:
2071 case PlayheadCursorItem:
2073 _over_region_trim_target = false;
2075 if (is_drawable()) {
2076 set_canvas_cursor (current_canvas_cursor);
2081 case AutomationLineItem:
2082 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
2084 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
2086 line->set_outline_color (al->get_line_color());
2089 if (is_drawable()) {
2090 set_canvas_cursor (current_canvas_cursor);
2094 case RegionViewName:
2095 /* see enter_handler() for notes */
2096 _over_region_trim_target = false;
2098 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
2099 if (is_drawable() && mouse_mode == MouseObject) {
2100 set_canvas_cursor (current_canvas_cursor);
2105 case RangeMarkerBarItem:
2106 case TransportMarkerBarItem:
2107 case CdMarkerBarItem:
2111 if (is_drawable()) {
2112 set_canvas_cursor (current_canvas_cursor);
2117 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2121 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
2122 location_flags_changed (loc, this);
2125 case MeterMarkerItem:
2126 case TempoMarkerItem:
2128 if (is_drawable()) {
2129 set_canvas_cursor (current_canvas_cursor);
2134 case FadeInHandleItem:
2135 case FadeOutHandleItem:
2136 rv = static_cast<RegionView*>(item->get_data ("regionview"));
2138 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
2140 rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_InactiveFadeHandle());
2143 set_canvas_cursor (current_canvas_cursor);
2146 case AutomationTrackItem:
2147 if (is_drawable()) {
2148 set_canvas_cursor (current_canvas_cursor);
2149 clear_entered_track = true;
2150 Glib::signal_idle().connect (sigc::mem_fun(*this, &Editor::left_automation_track));
2153 case FeatureLineItem:
2155 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
2156 line->set_outline_color (ARDOUR_UI::config()->get_canvasvar_ZeroLine());
2168 Editor::left_automation_track ()
2170 if (clear_entered_track) {
2171 set_entered_track (0);
2172 clear_entered_track = false;
2178 Editor::scrub (framepos_t frame, double current_x)
2182 if (scrubbing_direction == 0) {
2184 _session->request_locate (frame, false);
2185 _session->request_transport_speed (0.1);
2186 scrubbing_direction = 1;
2190 if (last_scrub_x > current_x) {
2192 /* pointer moved to the left */
2194 if (scrubbing_direction > 0) {
2196 /* we reversed direction to go backwards */
2199 scrub_reverse_distance += (int) (last_scrub_x - current_x);
2203 /* still moving to the left (backwards) */
2205 scrub_reversals = 0;
2206 scrub_reverse_distance = 0;
2208 delta = 0.01 * (last_scrub_x - current_x);
2209 _session->request_transport_speed_nonzero (_session->transport_speed() - delta);
2213 /* pointer moved to the right */
2215 if (scrubbing_direction < 0) {
2216 /* we reversed direction to go forward */
2219 scrub_reverse_distance += (int) (current_x - last_scrub_x);
2222 /* still moving to the right */
2224 scrub_reversals = 0;
2225 scrub_reverse_distance = 0;
2227 delta = 0.01 * (current_x - last_scrub_x);
2228 _session->request_transport_speed_nonzero (_session->transport_speed() + delta);
2232 /* if there have been more than 2 opposite motion moves detected, or one that moves
2233 back more than 10 pixels, reverse direction
2236 if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
2238 if (scrubbing_direction > 0) {
2239 /* was forwards, go backwards */
2240 _session->request_transport_speed (-0.1);
2241 scrubbing_direction = -1;
2243 /* was backwards, go forwards */
2244 _session->request_transport_speed (0.1);
2245 scrubbing_direction = 1;
2248 scrub_reverse_distance = 0;
2249 scrub_reversals = 0;
2253 last_scrub_x = current_x;
2257 Editor::motion_handler (ArdourCanvas::Item* /*item*/, GdkEvent* event, bool from_autoscroll)
2259 _last_motion_y = event->motion.y;
2261 if (event->motion.is_hint) {
2264 /* We call this so that MOTION_NOTIFY events continue to be
2265 delivered to the canvas. We need to do this because we set
2266 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
2267 the density of the events, at the expense of a round-trip
2268 to the server. Given that this will mostly occur on cases
2269 where DISPLAY = :0.0, and given the cost of what the motion
2270 event might do, its a good tradeoff.
2273 _track_canvas->get_pointer (x, y);
2276 if (current_stepping_trackview) {
2277 /* don't keep the persistent stepped trackview if the mouse moves */
2278 current_stepping_trackview = 0;
2279 step_timeout.disconnect ();
2282 if (_session && _session->actively_recording()) {
2283 /* Sorry. no dragging stuff around while we record */
2287 JoinObjectRangeState const old = _join_object_range_state;
2288 update_join_object_range_location (event->motion.x, event->motion.y);
2290 if (!_internal_editing && _join_object_range_state != old) {
2291 set_canvas_cursor ();
2294 if (!_internal_editing && _over_region_trim_target) {
2295 set_canvas_cursor_for_region_view (event->motion.x, entered_regionview);
2298 bool handled = false;
2299 if (_drags->active ()) {
2300 handled = _drags->motion_handler (event, from_autoscroll);
2307 track_canvas_motion (event);
2312 Editor::can_remove_control_point (ArdourCanvas::Item* item)
2314 ControlPoint* control_point;
2316 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2317 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2321 AutomationLine& line = control_point->line ();
2322 if (dynamic_cast<AudioRegionGainLine*> (&line)) {
2323 /* we shouldn't remove the first or last gain point in region gain lines */
2324 if (line.is_last_point(*control_point) || line.is_first_point(*control_point)) {
2333 Editor::remove_control_point (ArdourCanvas::Item* item)
2335 if (!can_remove_control_point (item)) {
2339 ControlPoint* control_point;
2341 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2342 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2346 control_point->line().remove_point (*control_point);
2350 Editor::edit_control_point (ArdourCanvas::Item* item)
2352 ControlPoint* p = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"));
2355 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2359 ControlPointDialog d (p);
2362 if (d.run () != RESPONSE_ACCEPT) {
2366 p->line().modify_point_y (*p, d.get_y_fraction ());
2370 Editor::edit_notes (TimeAxisViewItem& tavi)
2372 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(&tavi);
2378 MidiRegionView::Selection const & s = mrv->selection();
2384 EditNoteDialog* d = new EditNoteDialog (&(*s.begin())->region_view(), s);
2388 d->signal_response().connect (sigc::bind (sigc::mem_fun (*this, &Editor::note_edit_done), d));
2392 Editor::note_edit_done (int r, EditNoteDialog* d)
2399 Editor::visible_order_range (int* low, int* high) const
2401 *low = TimeAxisView::max_order ();
2404 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
2406 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
2408 if (!rtv->hidden()) {
2410 if (*high < rtv->order()) {
2411 *high = rtv->order ();
2414 if (*low > rtv->order()) {
2415 *low = rtv->order ();
2422 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
2424 /* Either add to or set the set the region selection, unless
2425 this is an alignment click (control used)
2428 if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
2429 TimeAxisView* tv = &rv.get_time_axis_view();
2430 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
2432 if (rtv && rtv->is_track()) {
2433 speed = rtv->track()->speed();
2436 framepos_t where = get_preferred_edit_position();
2440 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
2442 align_region (rv.region(), SyncPoint, (framepos_t) (where * speed));
2444 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
2446 align_region (rv.region(), End, (framepos_t) (where * speed));
2450 align_region (rv.region(), Start, (framepos_t) (where * speed));
2457 Editor::collect_new_region_view (RegionView* rv)
2459 latest_regionviews.push_back (rv);
2463 Editor::collect_and_select_new_region_view (RegionView* rv)
2466 latest_regionviews.push_back (rv);
2470 Editor::cancel_selection ()
2472 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2473 (*i)->hide_selection ();
2476 selection->clear ();
2477 clicked_selection = 0;
2481 Editor::cancel_time_selection ()
2483 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2484 (*i)->hide_selection ();
2486 selection->time.clear ();
2487 clicked_selection = 0;
2491 Editor::point_trim (GdkEvent* event, framepos_t new_bound)
2493 RegionView* rv = clicked_regionview;
2495 /* Choose action dependant on which button was pressed */
2496 switch (event->button.button) {
2498 begin_reversible_command (_("start point trim"));
2500 if (selection->selected (rv)) {
2501 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
2502 i != selection->regions.by_layer().end(); ++i)
2504 if (!(*i)->region()->locked()) {
2505 (*i)->region()->clear_changes ();
2506 (*i)->region()->trim_front (new_bound);
2507 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2512 if (!rv->region()->locked()) {
2513 rv->region()->clear_changes ();
2514 rv->region()->trim_front (new_bound);
2515 _session->add_command(new StatefulDiffCommand (rv->region()));
2519 commit_reversible_command();
2523 begin_reversible_command (_("End point trim"));
2525 if (selection->selected (rv)) {
2527 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
2529 if (!(*i)->region()->locked()) {
2530 (*i)->region()->clear_changes();
2531 (*i)->region()->trim_end (new_bound);
2532 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2538 if (!rv->region()->locked()) {
2539 rv->region()->clear_changes ();
2540 rv->region()->trim_end (new_bound);
2541 _session->add_command (new StatefulDiffCommand (rv->region()));
2545 commit_reversible_command();
2554 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* /*event*/)
2559 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2560 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2564 Location* location = find_location_from_marker (marker, is_start);
2565 location->set_hidden (true, this);
2570 Editor::reposition_zoom_rect (framepos_t start, framepos_t end)
2572 double x1 = sample_to_pixel (start);
2573 double x2 = sample_to_pixel (end);
2574 double y2 = _full_canvas_height - 1.0;
2576 zoom_rect->set (ArdourCanvas::Rect (x1, 1.0, x2, y2));
2581 Editor::mouse_rename_region (ArdourCanvas::Item* /*item*/, GdkEvent* /*event*/)
2583 using namespace Gtkmm2ext;
2585 ArdourPrompter prompter (false);
2587 prompter.set_prompt (_("Name for region:"));
2588 prompter.set_initial_text (clicked_regionview->region()->name());
2589 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
2590 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
2591 prompter.show_all ();
2592 switch (prompter.run ()) {
2593 case Gtk::RESPONSE_ACCEPT:
2595 prompter.get_result(str);
2597 clicked_regionview->region()->set_name (str);
2606 Editor::mouse_brush_insert_region (RegionView* rv, framepos_t pos)
2608 /* no brushing without a useful snap setting */
2610 switch (_snap_mode) {
2612 return; /* can't work because it allows region to be placed anywhere */
2617 switch (_snap_type) {
2625 /* don't brush a copy over the original */
2627 if (pos == rv->region()->position()) {
2631 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
2633 if (rtv == 0 || !rtv->is_track()) {
2637 boost::shared_ptr<Playlist> playlist = rtv->playlist();
2638 double speed = rtv->track()->speed();
2640 playlist->clear_changes ();
2641 boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region(), true));
2642 playlist->add_region (new_region, (framepos_t) (pos * speed));
2643 _session->add_command (new StatefulDiffCommand (playlist));
2645 // playlist is frozen, so we have to update manually XXX this is disgusting
2647 playlist->RegionAdded (new_region); /* EMIT SIGNAL */
2651 Editor::track_height_step_timeout ()
2653 if (get_microseconds() - last_track_height_step_timestamp < 250000) {
2654 current_stepping_trackview = 0;
2661 Editor::add_region_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2663 assert (region_view);
2665 if (!region_view->region()->playlist()) {
2669 _region_motion_group->raise_to_top ();
2671 if (Config->get_edit_mode() == Splice) {
2672 _drags->add (new RegionSpliceDrag (this, item, region_view, selection->regions.by_layer()));
2674 _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), false, false));
2679 Editor::add_region_copy_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2681 assert (region_view);
2683 if (!region_view->region()->playlist()) {
2687 _region_motion_group->raise_to_top ();
2689 _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), false, true));
2693 Editor::add_region_brush_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2695 assert (region_view);
2697 if (!region_view->region()->playlist()) {
2701 if (Config->get_edit_mode() == Splice) {
2705 _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), true, false));
2707 begin_reversible_command (Operations::drag_region_brush);
2710 /** Start a grab where a time range is selected, track(s) are selected, and the
2711 * user clicks and drags a region with a modifier in order to create a new region containing
2712 * the section of the clicked region that lies within the time range.
2715 Editor::start_selection_grab (ArdourCanvas::Item* /*item*/, GdkEvent* event)
2717 if (clicked_regionview == 0) {
2721 /* lets try to create new Region for the selection */
2723 vector<boost::shared_ptr<Region> > new_regions;
2724 create_region_from_selection (new_regions);
2726 if (new_regions.empty()) {
2730 /* XXX fix me one day to use all new regions */
2732 boost::shared_ptr<Region> region (new_regions.front());
2734 /* add it to the current stream/playlist.
2736 tricky: the streamview for the track will add a new regionview. we will
2737 catch the signal it sends when it creates the regionview to
2738 set the regionview we want to then drag.
2741 latest_regionviews.clear();
2742 sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
2744 /* A selection grab currently creates two undo/redo operations, one for
2745 creating the new region and another for moving it.
2748 begin_reversible_command (Operations::selection_grab);
2750 boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
2752 playlist->clear_changes ();
2753 clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
2754 _session->add_command(new StatefulDiffCommand (playlist));
2756 commit_reversible_command ();
2760 if (latest_regionviews.empty()) {
2761 /* something went wrong */
2765 /* we need to deselect all other regionviews, and select this one
2766 i'm ignoring undo stuff, because the region creation will take care of it
2768 selection->set (latest_regionviews);
2770 _drags->set (new RegionMoveDrag (this, latest_regionviews.front()->get_canvas_group(), latest_regionviews.front(), latest_regionviews, false, false), event);
2776 if (_drags->active ()) {
2779 selection->clear ();
2784 Editor::set_internal_edit (bool yn)
2786 if (_internal_editing == yn) {
2790 _internal_editing = yn;
2793 pre_internal_mouse_mode = mouse_mode;
2794 pre_internal_snap_type = _snap_type;
2795 pre_internal_snap_mode = _snap_mode;
2797 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2798 (*i)->enter_internal_edit_mode ();
2801 set_snap_to (internal_snap_type);
2802 set_snap_mode (internal_snap_mode);
2806 internal_snap_mode = _snap_mode;
2807 internal_snap_type = _snap_type;
2809 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2810 (*i)->leave_internal_edit_mode ();
2813 if (mouse_mode == MouseDraw && pre_internal_mouse_mode != MouseDraw) {
2814 /* we were drawing .. flip back to something sensible */
2815 set_mouse_mode (pre_internal_mouse_mode);
2818 set_snap_to (pre_internal_snap_type);
2819 set_snap_mode (pre_internal_snap_mode);
2822 set_canvas_cursor ();
2825 /** Update _join_object_range_state which indicate whether we are over the top or bottom half of a region view,
2826 * used by the `join object/range' tool mode.
2829 Editor::update_join_object_range_location (double /*x*/, double y)
2831 /* XXX: actually, this decides based on whether the mouse is in the top
2832 or bottom half of a the waveform part RouteTimeAxisView;
2834 Note that entered_{track,regionview} is not always setup (e.g. if
2835 the mouse is over a TimeSelection), and to get a Region
2836 that we're over requires searching the playlist.
2839 if ( !get_smart_mode() ) {
2840 _join_object_range_state = JOIN_OBJECT_RANGE_NONE;
2844 if (mouse_mode == MouseObject) {
2845 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2846 } else if (mouse_mode == MouseRange) {
2847 _join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
2850 /* XXX: maybe we should make entered_track work in all cases, rather than resorting to this */
2851 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
2855 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
2860 rtv->canvas_display()->canvas_to_item (cx, cy);
2862 double const c = cy / (rtv->view()->child_height() - TimeAxisViewItem::NAME_HIGHLIGHT_SIZE);
2864 _join_object_range_state = c <= 0.5 ? JOIN_OBJECT_RANGE_RANGE : JOIN_OBJECT_RANGE_OBJECT;
2870 Editor::effective_mouse_mode () const
2872 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
2874 } else if (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE) {
2882 Editor::remove_midi_note (ArdourCanvas::Item* item, GdkEvent *)
2884 NoteBase* e = reinterpret_cast<NoteBase*> (item->get_data ("notebase"));
2887 e->region_view().delete_note (e->note ());
2891 Editor::set_canvas_cursor_for_region_view (double x, RegionView* rv)
2893 /* XXX: this check should not be necessary */
2900 ArdourCanvas::Group* g = rv->get_canvas_group ();
2901 ArdourCanvas::Group* p = g->parent ();
2903 /* Compute x in region view parent coordinates */
2905 p->canvas_to_item (x, dy);
2907 boost::optional<ArdourCanvas::Rect> item_bbox = g->bounding_box ();
2909 ArdourCanvas::Rect parent_bbox = g->item_to_parent (item_bbox.get ());
2911 /* Halfway across the region */
2912 double const h = (parent_bbox.x0 + parent_bbox.x1) / 2;
2914 Trimmable::CanTrim ct = rv->region()->can_trim ();
2916 if (ct & Trimmable::FrontTrimEarlier) {
2917 set_canvas_cursor (_cursors->left_side_trim);
2919 set_canvas_cursor (_cursors->left_side_trim_right_only);
2922 if (ct & Trimmable::EndTrimLater) {
2923 set_canvas_cursor (_cursors->right_side_trim);
2925 set_canvas_cursor (_cursors->right_side_trim_left_only);
2930 /** Obtain the pointer position in canvas coordinates */
2932 Editor::get_pointer_position (double& x, double& y) const
2935 _track_canvas->get_pointer (px, py);
2936 _track_canvas->window_to_canvas (px, py, x, y);