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) {
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
137 ArdourCanvas::Duple d;
139 if (!gdk_event_get_coords (event, &d.x, &d.y)) {
143 /* adjust for scrolling (the canvas used by Ardour has global scroll
144 * disabled, so we have to do the adjustment explicitly).
147 d.translate (ArdourCanvas::Duple (horizontal_adjustment.get_value(), vertical_adjustment.get_value()));
149 /* event coordinates are in window units, so convert to canvas
152 d = _track_canvas->window_to_canvas (d);
162 return pixel_to_sample (d.x);
166 Editor::canvas_event_sample (GdkEvent const * event, double* pcx, double* pcy) const
171 /* event coordinates are already in canvas units */
173 if (!gdk_event_get_coords (event, &x, &y)) {
174 cerr << "!NO c COORDS for event type " << event->type << endl;
186 /* note that pixel_to_sample_from_event() never returns less than zero, so even if the pixel
187 position is negative (as can be the case with motion events in particular),
188 the frame location is always positive.
191 return pixel_to_sample_from_event (x);
195 Editor::which_grabber_cursor ()
197 Gdk::Cursor* c = _cursors->grabber;
199 if (_internal_editing) {
200 switch (mouse_mode) {
202 c = _cursors->midi_pencil;
206 c = _cursors->grabber_note;
210 c = _cursors->midi_resize;
214 c = _cursors->grabber_note;
223 switch (_edit_point) {
225 c = _cursors->grabber_edit_point;
228 boost::shared_ptr<Movable> m = _movable.lock();
229 if (m && m->locked()) {
230 c = _cursors->speaker;
240 Editor::set_current_trimmable (boost::shared_ptr<Trimmable> t)
242 boost::shared_ptr<Trimmable> st = _trimmable.lock();
244 if (!st || st == t) {
246 set_canvas_cursor ();
251 Editor::set_current_movable (boost::shared_ptr<Movable> m)
253 boost::shared_ptr<Movable> sm = _movable.lock();
255 if (!sm || sm != m) {
257 set_canvas_cursor ();
262 Editor::set_canvas_cursor ()
264 switch (mouse_mode) {
266 current_canvas_cursor = _cursors->selector;
267 if (_internal_editing) {
268 current_canvas_cursor = which_grabber_cursor();
273 current_canvas_cursor = which_grabber_cursor();
277 current_canvas_cursor = _cursors->midi_pencil;
281 current_canvas_cursor = _cursors->cross_hair;
285 if (Keyboard::the_keyboard().key_is_down (GDK_Control_L)) {
286 current_canvas_cursor = _cursors->zoom_out;
288 current_canvas_cursor = _cursors->zoom_in;
293 current_canvas_cursor = _cursors->time_fx; // just use playhead
297 current_canvas_cursor = _cursors->speaker;
301 if (!_internal_editing) {
302 switch (_join_object_range_state) {
303 case JOIN_OBJECT_RANGE_NONE:
305 case JOIN_OBJECT_RANGE_OBJECT:
306 current_canvas_cursor = which_grabber_cursor ();
308 case JOIN_OBJECT_RANGE_RANGE:
309 current_canvas_cursor = _cursors->selector;
314 /* up-down cursor as a cue that automation can be dragged up and down when in join object/range mode */
315 if (!_internal_editing && get_smart_mode() ) {
318 get_pointer_position (x, y);
320 if (x >= 0 && y >= 0) {
322 vector<ArdourCanvas::Item const *> items;
324 /* Note how we choose a specific scroll group to get
325 * items from. This could be problematic.
328 hv_scroll_group->add_items_at_point (ArdourCanvas::Duple (x,y), items);
330 // first item will be the upper most
332 if (!items.empty()) {
333 const ArdourCanvas::Item* i = items.front();
335 if (i && i->parent() && i->parent()->get_data (X_("timeselection"))) {
336 pair<TimeAxisView*, int> tvp = trackview_by_y_position (_last_motion_y);
337 if (dynamic_cast<AutomationTimeAxisView*> (tvp.first)) {
338 current_canvas_cursor = _cursors->up_down;
345 set_canvas_cursor (current_canvas_cursor, true);
349 Editor::mouse_mode_object_range_toggled()
351 MouseMode m = mouse_mode;
353 Glib::RefPtr<Action> act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-range"));
355 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
358 if (tact->get_active()) {
359 m = MouseObject; //Smart mode turned to ON, force editing to Object mode
362 set_mouse_mode(m, true); //call this so the button styles can get updated
366 Editor::set_mouse_mode (MouseMode m, bool force)
368 if (_drags->active ()) {
372 if (!force && m == mouse_mode) {
376 Glib::RefPtr<Action> act;
380 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-range"));
384 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-object"));
388 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-draw"));
392 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-gain"));
396 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-zoom"));
400 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-timefx"));
404 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-audition"));
410 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
413 /* go there and back to ensure that the toggled handler is called to set up mouse_mode */
414 tact->set_active (false);
415 tact->set_active (true);
417 //NOTE: this will result in a call to mouse_mode_toggled which does the heavy lifting
421 Editor::mouse_mode_toggled (MouseMode m)
423 Glib::RefPtr<Action> act;
424 Glib::RefPtr<ToggleAction> tact;
428 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-range"));
432 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-object"));
436 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-draw"));
440 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-gain"));
444 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-zoom"));
448 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-timefx"));
452 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-audition"));
458 tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
461 if (!tact->get_active()) {
462 /* this was just the notification that the old mode has been
463 * left. we'll get called again with the new mode active in a
471 act = ActionManager::get_action (X_("MouseMode"), X_("toggle-internal-edit"));
472 tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
473 tact->set_active (true);
479 if (_session && mouse_mode == MouseAudition) {
480 /* stop transport and reset default speed to avoid oddness with
482 _session->request_transport_speed (0.0, true);
489 //TODO: set button styles for smart buttons
491 if ( smart_mode_action->get_active() ) {
492 if( mouse_mode == MouseObject ) { //smart active and object active
493 smart_mode_button.set_active(1);
494 smart_mode_button.set_name("smart mode button");
495 mouse_move_button.set_name("smart mode button");
496 } else { //smart active but object inactive
497 smart_mode_button.set_active(0);
498 smart_mode_button.set_name("smart mode button");
499 mouse_move_button.set_name("mouse mode button");
502 smart_mode_button.set_active(0);
503 smart_mode_button.set_name("mouse mode button");
504 mouse_move_button.set_name("mouse mode button");
508 set_canvas_cursor ();
509 set_gain_envelope_visibility ();
511 update_time_selection_display ();
513 MouseModeChanged (); /* EMIT SIGNAL */
517 Editor::update_time_selection_display ()
519 if (smart_mode_action->get_active()) {
520 /* not sure what to do here */
521 if (mouse_mode == MouseObject) {
525 switch (mouse_mode) {
527 selection->clear_objects ();
530 selection->clear_time ();
537 Editor::step_mouse_mode (bool next)
539 switch (current_mouse_mode()) {
542 if (Profile->get_sae()) {
543 set_mouse_mode (MouseZoom);
545 set_mouse_mode (MouseRange);
548 set_mouse_mode (MouseTimeFX);
553 if (next) set_mouse_mode (MouseDraw);
554 else set_mouse_mode (MouseObject);
558 if (next) set_mouse_mode (MouseZoom);
559 else set_mouse_mode (MouseRange);
564 if (Profile->get_sae()) {
565 set_mouse_mode (MouseTimeFX);
567 set_mouse_mode (MouseGain);
570 if (Profile->get_sae()) {
571 set_mouse_mode (MouseObject);
573 set_mouse_mode (MouseDraw);
579 if (next) set_mouse_mode (MouseTimeFX);
580 else set_mouse_mode (MouseZoom);
585 set_mouse_mode (MouseAudition);
587 if (Profile->get_sae()) {
588 set_mouse_mode (MouseZoom);
590 set_mouse_mode (MouseGain);
596 if (next) set_mouse_mode (MouseObject);
597 else set_mouse_mode (MouseTimeFX);
603 Editor::toggle_internal_editing_from_double_click (GdkEvent* event)
605 if (_drags->active()) {
606 _drags->end_grab (event);
609 ActionManager::do_action ("MouseMode", "toggle-internal-edit");
611 /* prevent reversion of edit cursor on button release */
613 pre_press_cursor = 0;
619 Editor::button_selection (ArdourCanvas::Item* /*item*/, GdkEvent* event, ItemType item_type)
621 /* in object/audition/timefx/gain-automation mode,
622 any button press sets the selection if the object
623 can be selected. this is a bit of hack, because
624 we want to avoid this if the mouse operation is a
627 note: not dbl-click or triple-click
629 Also note that there is no region selection in internal edit mode, otherwise
630 for operations operating on the selection (e.g. cut) it is not obvious whether
631 to cut notes or regions.
634 MouseMode eff_mouse_mode = mouse_mode;
636 if (get_smart_mode() && eff_mouse_mode == MouseRange && event->button.button == 3 && item_type == RegionItem) {
637 /* context clicks are always about object properties, even if
638 we're in range mode within smart mode.
640 eff_mouse_mode = MouseObject;
643 if (((mouse_mode != MouseObject) &&
644 (mouse_mode != MouseAudition || item_type != RegionItem) &&
645 (mouse_mode != MouseTimeFX || item_type != RegionItem) &&
646 (mouse_mode != MouseGain) &&
647 (mouse_mode != MouseDraw)) ||
648 ((event->type != GDK_BUTTON_PRESS && event->type != GDK_BUTTON_RELEASE) || event->button.button > 3) ||
649 (internal_editing() && mouse_mode != MouseTimeFX)) {
654 if (event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) {
656 if ((event->button.state & Keyboard::RelevantModifierKeyMask) && event->button.button != 1) {
658 /* almost no selection action on modified button-2 or button-3 events */
660 if (item_type != RegionItem && event->button.button != 2) {
666 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
667 bool press = (event->type == GDK_BUTTON_PRESS);
672 if (eff_mouse_mode != MouseRange) {
673 set_selected_regionview_from_click (press, op);
675 /* don't change the selection unless the
676 clicked track is not currently selected. if
677 so, "collapse" the selection to just this
680 if (!selection->selected (clicked_axisview)) {
681 set_selected_track_as_side_effect (Selection::Set);
685 if (eff_mouse_mode != MouseRange) {
686 set_selected_regionview_from_click (press, op);
691 case RegionViewNameHighlight:
693 case LeftFrameHandle:
694 case RightFrameHandle:
695 if (eff_mouse_mode != MouseRange) {
696 set_selected_regionview_from_click (press, op);
697 } else if (event->type == GDK_BUTTON_PRESS) {
698 set_selected_track_as_side_effect (op);
702 case FadeInHandleItem:
703 case FadeInTrimHandleItem:
705 case FadeOutHandleItem:
706 case FadeOutTrimHandleItem:
708 case StartCrossFadeItem:
709 case EndCrossFadeItem:
710 if (eff_mouse_mode != MouseRange) {
711 cerr << "Should be setting selected regionview\n";
712 set_selected_regionview_from_click (press, op);
713 } else if (event->type == GDK_BUTTON_PRESS) {
714 set_selected_track_as_side_effect (op);
718 case ControlPointItem:
719 set_selected_track_as_side_effect (op);
720 if (eff_mouse_mode != MouseRange) {
721 set_selected_control_point_from_click (press, op);
726 /* for context click, select track */
727 if (event->button.button == 3) {
728 selection->clear_tracks ();
729 set_selected_track_as_side_effect (op);
733 case AutomationTrackItem:
734 set_selected_track_as_side_effect (op);
743 Editor::button_press_handler_1 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
745 /* single mouse clicks on any of these item types operate
746 independent of mouse mode, mostly because they are
747 not on the main track canvas or because we want
752 case PlayheadCursorItem:
753 _drags->set (new CursorDrag (this, *playhead_cursor, true), event);
757 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
758 hide_marker (item, event);
760 _drags->set (new MarkerDrag (this, item), event);
764 case TempoMarkerItem:
766 TempoMarker* m = reinterpret_cast<TempoMarker*> (item->get_data ("marker"));
769 new TempoMarkerDrag (
772 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
779 case MeterMarkerItem:
781 MeterMarker* m = reinterpret_cast<MeterMarker*> (item->get_data ("marker"));
784 new MeterMarkerDrag (
787 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
795 _drags->set (new VideoTimeLineDrag (this, item), event);
802 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
803 _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
809 case RangeMarkerBarItem:
810 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
811 _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
813 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateRangeMarker), event);
818 case CdMarkerBarItem:
819 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
820 _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
822 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateCDMarker), event);
827 case TransportMarkerBarItem:
828 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
829 _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
831 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateTransportMarker), event);
840 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
841 /* special case: allow trim of range selections in joined object mode;
842 in theory eff should equal MouseRange in this case, but it doesn't
843 because entering the range selection canvas item results in entered_regionview
844 being set to 0, so update_join_object_range_location acts as if we aren't
847 if (item_type == StartSelectionTrimItem) {
848 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
849 } else if (item_type == EndSelectionTrimItem) {
850 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
854 Editing::MouseMode eff = effective_mouse_mode ();
856 /* special case: allow drag of region fade in/out in object mode with join object/range enabled */
857 if (item_type == FadeInHandleItem || item_type == FadeOutHandleItem) {
861 /* there is no Range mode when in internal edit mode */
862 if (eff == MouseRange && internal_editing()) {
869 case StartSelectionTrimItem:
870 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
873 case EndSelectionTrimItem:
874 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
878 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
879 start_selection_grab (item, event);
881 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
882 /* grab selection for moving */
883 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionMove), event);
885 double const y = event->button.y;
886 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
888 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
889 if ( get_smart_mode() && atv) {
890 /* smart "join" mode: drag automation */
891 _drags->set (new AutomationRangeDrag (this, atv, selection->time), event, _cursors->up_down);
893 /* this was debated, but decided the more common action was to
894 make a new selection */
895 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
902 if (internal_editing()) {
903 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
904 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
908 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::RangeSelectModifier)) {
909 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionExtend), event);
911 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
917 case RegionViewNameHighlight:
918 if (!clicked_regionview->region()->locked()) {
919 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
925 if (!internal_editing()) {
926 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::RangeSelectModifier)) {
927 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionExtend), event);
929 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
939 /* Existing note: allow trimming/motion */
940 if (internal_editing()) {
941 /* trim notes if we're in internal edit mode and near the ends of the note */
942 NoteBase* cn = reinterpret_cast<NoteBase*>(item->get_data ("notebase"));
944 if (cn->big_enough_to_trim() && cn->mouse_near_ends()) {
945 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
947 _drags->set (new NoteDrag (this, item), event);
953 if (internal_editing()) {
954 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
955 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
969 /* Existing note: allow trimming/motion */
970 if (internal_editing()) {
971 NoteBase* cn = reinterpret_cast<NoteBase*> (item->get_data ("notebase"));
973 if (cn->big_enough_to_trim() && cn->mouse_near_ends()) {
974 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
976 _drags->set (new NoteDrag (this, item), event);
986 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)) &&
987 event->type == GDK_BUTTON_PRESS) {
989 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
991 } else if (event->type == GDK_BUTTON_PRESS) {
994 case FadeInHandleItem:
996 _drags->set (new FadeInDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), selection->regions), event, _cursors->fade_in);
1000 case FadeOutHandleItem:
1002 _drags->set (new FadeOutDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), selection->regions), event, _cursors->fade_out);
1006 case StartCrossFadeItem:
1007 case EndCrossFadeItem:
1008 /* we might allow user to grab inside the fade to trim a region with preserve_fade_anchor. for not this is not fully implemented */
1009 // if (!clicked_regionview->region()->locked()) {
1010 // _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer(), true), event);
1015 case FeatureLineItem:
1017 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier)) {
1018 remove_transient(item);
1022 _drags->set (new FeatureLineDrag (this, item), event);
1028 if (dynamic_cast<AutomationRegionView*> (clicked_regionview)) {
1029 /* click on an automation region view; do nothing here and let the ARV's signal handler
1035 if (internal_editing ()) {
1039 /* click on a normal region view */
1040 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
1041 add_region_copy_drag (item, event, clicked_regionview);
1042 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
1043 add_region_brush_drag (item, event, clicked_regionview);
1045 add_region_drag (item, event, clicked_regionview);
1049 // if (!internal_editing() && (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE && !selection->regions.empty())) {
1050 // _drags->add (new SelectionDrag (this, clicked_axisview->get_selection_rect (clicked_selection)->rect, SelectionDrag::SelectionMove));
1053 _drags->start_grab (event);
1057 case RegionViewNameHighlight:
1058 case LeftFrameHandle:
1059 case RightFrameHandle:
1060 if (!clicked_regionview->region()->locked()) {
1061 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1066 case FadeInTrimHandleItem:
1067 case FadeOutTrimHandleItem:
1068 if (!clicked_regionview->region()->locked()) {
1069 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer(), true), event);
1074 case RegionViewName:
1076 /* rename happens on edit clicks */
1077 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer()), event);
1082 case ControlPointItem:
1083 _drags->set (new ControlPointDrag (this, item), event);
1087 case AutomationLineItem:
1088 _drags->set (new LineDrag (this, item), event);
1093 if (internal_editing()) {
1094 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
1095 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
1099 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
1103 case AutomationTrackItem:
1105 TimeAxisView* parent = clicked_axisview->get_parent ();
1106 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (clicked_axisview);
1108 if (parent && dynamic_cast<MidiTimeAxisView*> (parent) && atv->show_regions ()) {
1110 RouteTimeAxisView* p = dynamic_cast<RouteTimeAxisView*> (parent);
1112 boost::shared_ptr<Playlist> pl = p->track()->playlist ();
1113 if (pl->n_regions() == 0) {
1114 /* Parent has no regions; create one so that we have somewhere to put automation */
1115 _drags->set (new RegionCreateDrag (this, item, parent), event);
1117 /* See if there's a region before the click that we can extend, and extend it if so */
1118 framepos_t const t = canvas_event_sample (event);
1119 boost::shared_ptr<Region> prev = pl->find_next_region (t, End, -1);
1121 _drags->set (new RegionCreateDrag (this, item, parent), event);
1123 prev->set_length (t - prev->position ());
1127 /* rubberband drag to select automation points */
1128 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
1135 if ( get_smart_mode() ) {
1136 /* we're in "smart" joined mode, and we've clicked on a Selection */
1137 double const y = event->button.y;
1138 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
1140 /* if we're over an automation track, start a drag of its data */
1141 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
1143 _drags->set (new AutomationRangeDrag (this, atv, selection->time), event, _cursors->up_down);
1146 /* if we're over a track and a region, and in the `object' part of a region,
1147 put a selection around the region and drag both
1149 /* RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1150 if (rtv && _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
1151 boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track> (rtv->route ());
1153 boost::shared_ptr<Playlist> pl = t->playlist ();
1156 boost::shared_ptr<Region> r = pl->top_region_at (canvas_event_sample (event));
1158 RegionView* rv = rtv->view()->find_view (r);
1159 clicked_selection = select_range (rv->region()->position(),
1160 rv->region()->last_frame()+1);
1161 _drags->add (new SelectionDrag (this, item, SelectionDrag::SelectionMove));
1162 list<RegionView*> rvs;
1164 _drags->add (new RegionMoveDrag (this, item, rv, rvs, false, false));
1165 _drags->start_grab (event);
1189 switch (item_type) {
1191 _drags->set (new LineDrag (this, item), event);
1194 case ControlPointItem:
1195 _drags->set (new ControlPointDrag (this, item), event);
1201 AudioRegionView* arv = dynamic_cast<AudioRegionView *> (clicked_regionview);
1203 _drags->set (new AutomationRangeDrag (this, arv, selection->time), event, _cursors->up_down);
1204 _drags->start_grab (event);
1210 case AutomationLineItem:
1211 _drags->set (new LineDrag (this, item), event);
1221 if (event->type == GDK_BUTTON_PRESS) {
1222 _drags->set (new MouseZoomDrag (this, item), event);
1229 if (internal_editing() && item_type == NoteItem ) {
1230 /* drag notes if we're in internal edit mode */
1231 NoteBase* cn = reinterpret_cast<NoteBase*>(item->get_data ("notebase"));
1233 if (cn->big_enough_to_trim()) {
1234 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
1237 } else if (clicked_regionview) {
1239 _drags->set (new TimeFXDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1245 _drags->set (new ScrubDrag (this, item), event);
1246 scrub_reversals = 0;
1247 scrub_reverse_distance = 0;
1248 last_scrub_x = event->button.x;
1249 scrubbing_direction = 0;
1250 set_canvas_cursor (_cursors->transparent);
1262 Editor::button_press_handler_2 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1264 Editing::MouseMode const eff = effective_mouse_mode ();
1267 switch (item_type) {
1269 if (internal_editing ()) {
1270 /* no region drags in internal edit mode */
1274 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
1275 add_region_copy_drag (item, event, clicked_regionview);
1277 add_region_drag (item, event, clicked_regionview);
1279 _drags->start_grab (event);
1282 case ControlPointItem:
1283 _drags->set (new ControlPointDrag (this, item), event);
1291 switch (item_type) {
1292 case RegionViewNameHighlight:
1293 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1297 case LeftFrameHandle:
1298 case RightFrameHandle:
1299 if (!internal_editing ()) {
1300 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1305 case RegionViewName:
1306 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer()), event);
1320 /* relax till release */
1326 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1327 temporal_zoom_to_frame (false, canvas_event_sample (event));
1329 temporal_zoom_to_frame (true, canvas_event_sample(event));
1342 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1344 if (event->type == GDK_2BUTTON_PRESS) {
1345 _drags->mark_double_click ();
1346 gdk_pointer_ungrab (GDK_CURRENT_TIME);
1350 if (event->type != GDK_BUTTON_PRESS) {
1354 pre_press_cursor = current_canvas_cursor;
1356 _track_canvas->grab_focus();
1358 if (_session && _session->actively_recording()) {
1362 if (internal_editing()) {
1363 bool leave_internal_edit_mode = false;
1365 switch (item_type) {
1370 if (!dynamic_cast<MidiRegionView*> (clicked_regionview) && !dynamic_cast<AutomationRegionView*> (clicked_regionview)) {
1371 leave_internal_edit_mode = true;
1375 case PlayheadCursorItem:
1377 case TempoMarkerItem:
1378 case MeterMarkerItem:
1382 case RangeMarkerBarItem:
1383 case CdMarkerBarItem:
1384 case TransportMarkerBarItem:
1386 /* button press on these events never does anything to
1387 change the editing mode.
1395 if (leave_internal_edit_mode) {
1396 ActionManager::do_action ("MouseMode", "toggle-internal-edit");
1400 button_selection (item, event, item_type);
1402 if (!_drags->active () &&
1403 (Keyboard::is_delete_event (&event->button) ||
1404 Keyboard::is_context_menu_event (&event->button) ||
1405 Keyboard::is_edit_event (&event->button))) {
1407 /* handled by button release */
1411 //not rolling, range mode click + join_play_range : locate the PH here
1412 if ( !_drags->active () && !_session->transport_rolling() && ( effective_mouse_mode() == MouseRange ) && Config->get_always_play_range() ) {
1413 framepos_t where = canvas_event_sample (event);
1415 _session->request_locate (where, false);
1418 switch (event->button.button) {
1420 return button_press_handler_1 (item, event, item_type);
1424 return button_press_handler_2 (item, event, item_type);
1431 return button_press_dispatch (&event->button);
1440 Editor::button_press_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::Press);
1450 Editor::button_release_dispatch (GdkEventButton* ev)
1452 /* this function is intended only for buttons 4 and above.
1455 Gtkmm2ext::MouseButton b (ev->state, ev->button);
1456 return button_bindings->activate (b, Gtkmm2ext::Bindings::Release);
1460 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1462 framepos_t where = canvas_event_sample (event);
1463 AutomationTimeAxisView* atv = 0;
1465 if (pre_press_cursor) {
1466 set_canvas_cursor (pre_press_cursor);
1467 pre_press_cursor = 0;
1470 /* no action if we're recording */
1472 if (_session && _session->actively_recording()) {
1476 /* see if we're finishing a drag */
1478 bool were_dragging = false;
1479 if (_drags->active ()) {
1480 bool const r = _drags->end_grab (event);
1482 /* grab dragged, so do nothing else */
1486 were_dragging = true;
1489 update_region_layering_order_editor ();
1491 /* edit events get handled here */
1493 if (!_drags->active () && Keyboard::is_edit_event (&event->button)) {
1494 switch (item_type) {
1496 show_region_properties ();
1499 case TempoMarkerItem: {
1501 TempoMarker* tempo_marker;
1503 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
1504 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
1508 if ((tempo_marker = dynamic_cast<TempoMarker*> (marker)) == 0) {
1509 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
1513 edit_tempo_marker (*tempo_marker);
1517 case MeterMarkerItem: {
1519 MeterMarker* meter_marker;
1521 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
1522 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
1526 if ((meter_marker = dynamic_cast<MeterMarker*> (marker)) == 0) {
1527 fatal << _("programming error: marker for meter is not a meter marker!") << endmsg;
1530 edit_meter_marker (*meter_marker);
1534 case RegionViewName:
1535 if (clicked_regionview->name_active()) {
1536 return mouse_rename_region (item, event);
1540 case ControlPointItem:
1541 edit_control_point (item);
1550 /* context menu events get handled here */
1551 if (Keyboard::is_context_menu_event (&event->button)) {
1553 context_click_event = *event;
1555 if (!_drags->active ()) {
1557 /* no matter which button pops up the context menu, tell the menu
1558 widget to use button 1 to drive menu selection.
1561 switch (item_type) {
1563 case FadeInHandleItem:
1564 case FadeInTrimHandleItem:
1565 case StartCrossFadeItem:
1566 popup_xfade_in_context_menu (1, event->button.time, item, item_type);
1570 case FadeOutHandleItem:
1571 case FadeOutTrimHandleItem:
1572 case EndCrossFadeItem:
1573 popup_xfade_out_context_menu (1, event->button.time, item, item_type);
1577 popup_track_context_menu (1, event->button.time, item_type, false);
1581 case RegionViewNameHighlight:
1582 case LeftFrameHandle:
1583 case RightFrameHandle:
1584 case RegionViewName:
1585 popup_track_context_menu (1, event->button.time, item_type, false);
1589 popup_track_context_menu (1, event->button.time, item_type, true);
1592 case AutomationTrackItem:
1593 popup_track_context_menu (1, event->button.time, item_type, false);
1597 case RangeMarkerBarItem:
1598 case TransportMarkerBarItem:
1599 case CdMarkerBarItem:
1603 popup_ruler_menu (where, item_type);
1607 marker_context_menu (&event->button, item);
1610 case TempoMarkerItem:
1611 tempo_or_meter_marker_context_menu (&event->button, item);
1614 case MeterMarkerItem:
1615 tempo_or_meter_marker_context_menu (&event->button, item);
1618 case CrossfadeViewItem:
1619 popup_track_context_menu (1, event->button.time, item_type, false);
1622 case ControlPointItem:
1623 popup_control_point_context_menu (item, event);
1634 /* delete events get handled here */
1636 Editing::MouseMode const eff = effective_mouse_mode ();
1638 if (!_drags->active () && Keyboard::is_delete_event (&event->button)) {
1640 switch (item_type) {
1641 case TempoMarkerItem:
1642 remove_tempo_marker (item);
1645 case MeterMarkerItem:
1646 remove_meter_marker (item);
1650 remove_marker (*item, event);
1654 if (eff == MouseObject) {
1655 remove_clicked_region ();
1659 case ControlPointItem:
1660 remove_control_point (item);
1664 remove_midi_note (item, event);
1673 switch (event->button.button) {
1676 switch (item_type) {
1677 /* see comments in button_press_handler */
1678 case PlayheadCursorItem:
1681 case AutomationLineItem:
1682 case StartSelectionTrimItem:
1683 case EndSelectionTrimItem:
1687 if (!_dragging_playhead) {
1688 snap_to_with_modifier (where, event, 0, true);
1689 mouse_add_new_marker (where);
1693 case CdMarkerBarItem:
1694 if (!_dragging_playhead) {
1695 // if we get here then a dragged range wasn't done
1696 snap_to_with_modifier (where, event, 0, true);
1697 mouse_add_new_marker (where, true);
1702 if (!_dragging_playhead) {
1703 snap_to_with_modifier (where, event);
1704 mouse_add_new_tempo_event (where);
1709 if (!_dragging_playhead) {
1710 mouse_add_new_meter_event (pixel_to_sample (event->button.x));
1721 switch (item_type) {
1722 case AutomationTrackItem:
1723 atv = dynamic_cast<AutomationTimeAxisView*>(clicked_axisview);
1725 bool with_guard_points = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
1726 atv->add_automation_event (event, where, event->button.y, with_guard_points);
1736 switch (item_type) {
1739 /* check that we didn't drag before releasing, since
1740 its really annoying to create new control
1741 points when doing this.
1743 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (clicked_regionview);
1744 if (!were_dragging && arv) {
1745 bool with_guard_points = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
1746 arv->add_gain_point_event (item, event, with_guard_points);
1752 case AutomationTrackItem: {
1753 bool with_guard_points = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
1754 dynamic_cast<AutomationTimeAxisView*>(clicked_axisview)->
1755 add_automation_event (event, where, event->button.y, with_guard_points);
1765 set_canvas_cursor (current_canvas_cursor);
1766 if (scrubbing_direction == 0) {
1767 /* no drag, just a click */
1768 switch (item_type) {
1770 play_selected_region ();
1776 /* make sure we stop */
1777 _session->request_transport_speed (0.0);
1786 /* do any (de)selection operations that should occur on button release */
1787 button_selection (item, event, item_type);
1796 switch (item_type) {
1798 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1800 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier|Keyboard::SecondaryModifier))) {
1803 // Button2 click is unused
1818 // x_style_paste (where, 1.0);
1839 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1846 switch (item_type) {
1847 case ControlPointItem:
1848 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1849 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1854 at_y = cp->get_y ();
1855 cp->i2w (at_x, at_y);
1859 fraction = 1.0 - (cp->get_y() / cp->line().height());
1861 if (is_drawable() && !_drags->active ()) {
1862 set_canvas_cursor (_cursors->fader);
1865 _verbose_cursor->set (cp->line().get_verbose_cursor_string (fraction), at_x, at_y);
1866 _verbose_cursor->show ();
1871 if (mouse_mode == MouseGain) {
1872 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1874 line->set_outline_color (ARDOUR_UI::config()->get_canvasvar_EnteredGainLine());
1876 if (is_drawable()) {
1877 set_canvas_cursor (_cursors->fader);
1882 case AutomationLineItem:
1883 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1884 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1886 line->set_outline_color (ARDOUR_UI::config()->get_canvasvar_EnteredAutomationLine());
1888 if (is_drawable()) {
1889 set_canvas_cursor (_cursors->fader);
1894 case RegionViewNameHighlight:
1895 if (is_drawable() && effective_mouse_mode() == MouseObject && entered_regionview) {
1896 set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1897 _over_region_trim_target = true;
1901 case LeftFrameHandle:
1902 case RightFrameHandle:
1903 if (is_drawable() && effective_mouse_mode() == MouseObject && !internal_editing() && entered_regionview) {
1904 set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1909 switch (effective_mouse_mode()) {
1911 set_canvas_cursor (_cursors->selector);
1914 set_canvas_cursor (which_grabber_cursor());
1919 case StartSelectionTrimItem:
1920 if (is_drawable()) {
1921 set_canvas_cursor (_cursors->left_side_trim);
1924 case EndSelectionTrimItem:
1925 if (is_drawable()) {
1926 set_canvas_cursor (_cursors->right_side_trim);
1930 case PlayheadCursorItem:
1931 if (is_drawable()) {
1932 switch (_edit_point) {
1934 set_canvas_cursor (_cursors->grabber_edit_point);
1937 set_canvas_cursor (_cursors->grabber);
1944 case RegionViewName:
1946 /* when the name is not an active item, the entire name highlight is for trimming */
1948 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1949 if (mouse_mode == MouseObject && is_drawable()) {
1950 set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1951 _over_region_trim_target = true;
1957 case AutomationTrackItem:
1958 if (is_drawable()) {
1959 Gdk::Cursor *cursor;
1960 switch (mouse_mode) {
1962 cursor = _cursors->selector;
1965 cursor = _cursors->zoom_in;
1968 cursor = _cursors->cross_hair;
1972 set_canvas_cursor (cursor);
1974 AutomationTimeAxisView* atv;
1975 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1976 clear_entered_track = false;
1977 set_entered_track (atv);
1983 case RangeMarkerBarItem:
1984 case TransportMarkerBarItem:
1985 case CdMarkerBarItem:
1988 if (is_drawable()) {
1989 set_canvas_cursor (_cursors->timebar);
1994 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1997 entered_marker = marker;
1998 marker->set_color_rgba (ARDOUR_UI::config()->get_canvasvar_EnteredMarker());
2000 case MeterMarkerItem:
2001 case TempoMarkerItem:
2002 if (is_drawable()) {
2003 set_canvas_cursor (_cursors->timebar);
2007 case FadeInHandleItem:
2008 if (mouse_mode == MouseObject && !internal_editing()) {
2009 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
2011 RegionView* rv = static_cast<RegionView*>(item->get_data ("regionview"));
2012 rect->set_fill_color (rv->get_fill_color());
2013 set_canvas_cursor (_cursors->fade_in);
2018 case FadeInTrimHandleItem:
2019 if (mouse_mode == MouseObject && !internal_editing()) {
2020 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
2022 RegionView* rv = static_cast<RegionView*>(item->get_data ("regionview"));
2023 rect->set_fill_color (rv->get_fill_color());
2024 set_canvas_cursor (_cursors->fade_trim_in);
2029 case FadeOutHandleItem:
2030 if (mouse_mode == MouseObject && !internal_editing()) {
2031 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
2033 RegionView* rv = static_cast<RegionView*>(item->get_data ("regionview"));
2034 rect->set_fill_color (rv->get_fill_color ());
2035 set_canvas_cursor (_cursors->fade_out);
2040 case FadeOutTrimHandleItem:
2041 if (mouse_mode == MouseObject && !internal_editing()) {
2042 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
2044 RegionView* rv = static_cast<RegionView*>(item->get_data ("regionview"));
2045 rect->set_fill_color (rv->get_fill_color ());
2046 set_canvas_cursor (_cursors->fade_trim_out);
2051 case FeatureLineItem:
2053 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
2054 line->set_outline_color (0xFF0000FF);
2059 if ( get_smart_mode() ) {
2060 set_canvas_cursor ();
2068 /* second pass to handle entered track status in a comprehensible way.
2071 switch (item_type) {
2073 case AutomationLineItem:
2074 case ControlPointItem:
2075 /* these do not affect the current entered track state */
2076 clear_entered_track = false;
2079 case AutomationTrackItem:
2080 /* handled above already */
2084 set_entered_track (0);
2092 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent*, ItemType item_type)
2100 switch (item_type) {
2101 case ControlPointItem:
2102 if (is_drawable()) {
2103 set_canvas_cursor (current_canvas_cursor);
2106 _verbose_cursor->hide ();
2109 case RegionViewNameHighlight:
2110 case LeftFrameHandle:
2111 case RightFrameHandle:
2112 case StartSelectionTrimItem:
2113 case EndSelectionTrimItem:
2114 case PlayheadCursorItem:
2116 _over_region_trim_target = false;
2118 if (is_drawable()) {
2119 set_canvas_cursor (current_canvas_cursor);
2124 case AutomationLineItem:
2125 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
2127 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
2129 line->set_outline_color (al->get_line_color());
2132 if (is_drawable()) {
2133 set_canvas_cursor (current_canvas_cursor);
2137 case RegionViewName:
2138 /* see enter_handler() for notes */
2139 _over_region_trim_target = false;
2141 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
2142 if (is_drawable() && mouse_mode == MouseObject) {
2143 set_canvas_cursor (current_canvas_cursor);
2148 case RangeMarkerBarItem:
2149 case TransportMarkerBarItem:
2150 case CdMarkerBarItem:
2154 if (is_drawable()) {
2155 set_canvas_cursor (current_canvas_cursor);
2160 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2164 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
2165 location_flags_changed (loc, this);
2168 case MeterMarkerItem:
2169 case TempoMarkerItem:
2171 if (is_drawable()) {
2172 set_canvas_cursor (current_canvas_cursor);
2177 case FadeInTrimHandleItem:
2178 case FadeOutTrimHandleItem:
2179 case FadeInHandleItem:
2180 case FadeOutHandleItem:
2182 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
2184 rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_InactiveFadeHandle());
2187 set_canvas_cursor (current_canvas_cursor);
2190 case AutomationTrackItem:
2191 if (is_drawable()) {
2192 set_canvas_cursor (current_canvas_cursor);
2193 clear_entered_track = true;
2194 Glib::signal_idle().connect (sigc::mem_fun(*this, &Editor::left_automation_track));
2197 case FeatureLineItem:
2199 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
2200 line->set_outline_color (ARDOUR_UI::config()->get_canvasvar_ZeroLine());
2212 Editor::left_automation_track ()
2214 if (clear_entered_track) {
2215 set_entered_track (0);
2216 clear_entered_track = false;
2222 Editor::scrub (framepos_t frame, double current_x)
2226 if (scrubbing_direction == 0) {
2228 _session->request_locate (frame, false);
2229 _session->request_transport_speed (0.1);
2230 scrubbing_direction = 1;
2234 if (last_scrub_x > current_x) {
2236 /* pointer moved to the left */
2238 if (scrubbing_direction > 0) {
2240 /* we reversed direction to go backwards */
2243 scrub_reverse_distance += (int) (last_scrub_x - current_x);
2247 /* still moving to the left (backwards) */
2249 scrub_reversals = 0;
2250 scrub_reverse_distance = 0;
2252 delta = 0.01 * (last_scrub_x - current_x);
2253 _session->request_transport_speed_nonzero (_session->transport_speed() - delta);
2257 /* pointer moved to the right */
2259 if (scrubbing_direction < 0) {
2260 /* we reversed direction to go forward */
2263 scrub_reverse_distance += (int) (current_x - last_scrub_x);
2266 /* still moving to the right */
2268 scrub_reversals = 0;
2269 scrub_reverse_distance = 0;
2271 delta = 0.01 * (current_x - last_scrub_x);
2272 _session->request_transport_speed_nonzero (_session->transport_speed() + delta);
2276 /* if there have been more than 2 opposite motion moves detected, or one that moves
2277 back more than 10 pixels, reverse direction
2280 if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
2282 if (scrubbing_direction > 0) {
2283 /* was forwards, go backwards */
2284 _session->request_transport_speed (-0.1);
2285 scrubbing_direction = -1;
2287 /* was backwards, go forwards */
2288 _session->request_transport_speed (0.1);
2289 scrubbing_direction = 1;
2292 scrub_reverse_distance = 0;
2293 scrub_reversals = 0;
2297 last_scrub_x = current_x;
2301 Editor::motion_handler (ArdourCanvas::Item* /*item*/, GdkEvent* event, bool from_autoscroll)
2303 _last_motion_y = event->motion.y;
2305 if (event->motion.is_hint) {
2308 /* We call this so that MOTION_NOTIFY events continue to be
2309 delivered to the canvas. We need to do this because we set
2310 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
2311 the density of the events, at the expense of a round-trip
2312 to the server. Given that this will mostly occur on cases
2313 where DISPLAY = :0.0, and given the cost of what the motion
2314 event might do, its a good tradeoff.
2317 _track_canvas->get_pointer (x, y);
2320 if (current_stepping_trackview) {
2321 /* don't keep the persistent stepped trackview if the mouse moves */
2322 current_stepping_trackview = 0;
2323 step_timeout.disconnect ();
2326 if (_session && _session->actively_recording()) {
2327 /* Sorry. no dragging stuff around while we record */
2331 JoinObjectRangeState const old = _join_object_range_state;
2332 update_join_object_range_location (event->motion.x, event->motion.y);
2334 if (!_internal_editing && _join_object_range_state != old) {
2335 set_canvas_cursor ();
2338 if (!_internal_editing && _over_region_trim_target) {
2339 set_canvas_cursor_for_region_view (event->motion.x, entered_regionview);
2342 bool handled = false;
2343 if (_drags->active ()) {
2344 handled = _drags->motion_handler (event, from_autoscroll);
2351 track_canvas_motion (event);
2356 Editor::can_remove_control_point (ArdourCanvas::Item* item)
2358 ControlPoint* control_point;
2360 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2361 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2365 AutomationLine& line = control_point->line ();
2366 if (dynamic_cast<AudioRegionGainLine*> (&line)) {
2367 /* we shouldn't remove the first or last gain point in region gain lines */
2368 if (line.is_last_point(*control_point) || line.is_first_point(*control_point)) {
2377 Editor::remove_control_point (ArdourCanvas::Item* item)
2379 if (!can_remove_control_point (item)) {
2383 ControlPoint* control_point;
2385 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2386 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2390 control_point->line().remove_point (*control_point);
2394 Editor::edit_control_point (ArdourCanvas::Item* item)
2396 ControlPoint* p = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"));
2399 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2403 ControlPointDialog d (p);
2406 if (d.run () != RESPONSE_ACCEPT) {
2410 p->line().modify_point_y (*p, d.get_y_fraction ());
2414 Editor::edit_notes (TimeAxisViewItem& tavi)
2416 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(&tavi);
2422 MidiRegionView::Selection const & s = mrv->selection();
2428 EditNoteDialog* d = new EditNoteDialog (&(*s.begin())->region_view(), s);
2432 d->signal_response().connect (sigc::bind (sigc::mem_fun (*this, &Editor::note_edit_done), d));
2436 Editor::note_edit_done (int r, EditNoteDialog* d)
2443 Editor::visible_order_range (int* low, int* high) const
2445 *low = TimeAxisView::max_order ();
2448 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
2450 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
2452 if (!rtv->hidden()) {
2454 if (*high < rtv->order()) {
2455 *high = rtv->order ();
2458 if (*low > rtv->order()) {
2459 *low = rtv->order ();
2466 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
2468 /* Either add to or set the set the region selection, unless
2469 this is an alignment click (control used)
2472 if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
2473 TimeAxisView* tv = &rv.get_time_axis_view();
2474 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
2476 if (rtv && rtv->is_track()) {
2477 speed = rtv->track()->speed();
2480 framepos_t where = get_preferred_edit_position();
2484 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
2486 align_region (rv.region(), SyncPoint, (framepos_t) (where * speed));
2488 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
2490 align_region (rv.region(), End, (framepos_t) (where * speed));
2494 align_region (rv.region(), Start, (framepos_t) (where * speed));
2501 Editor::collect_new_region_view (RegionView* rv)
2503 latest_regionviews.push_back (rv);
2507 Editor::collect_and_select_new_region_view (RegionView* rv)
2510 latest_regionviews.push_back (rv);
2514 Editor::cancel_selection ()
2516 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2517 (*i)->hide_selection ();
2520 selection->clear ();
2521 clicked_selection = 0;
2525 Editor::cancel_time_selection ()
2527 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2528 (*i)->hide_selection ();
2530 selection->time.clear ();
2531 clicked_selection = 0;
2535 Editor::point_trim (GdkEvent* event, framepos_t new_bound)
2537 RegionView* rv = clicked_regionview;
2539 /* Choose action dependant on which button was pressed */
2540 switch (event->button.button) {
2542 begin_reversible_command (_("start point trim"));
2544 if (selection->selected (rv)) {
2545 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
2546 i != selection->regions.by_layer().end(); ++i)
2548 if (!(*i)->region()->locked()) {
2549 (*i)->region()->clear_changes ();
2550 (*i)->region()->trim_front (new_bound);
2551 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2556 if (!rv->region()->locked()) {
2557 rv->region()->clear_changes ();
2558 rv->region()->trim_front (new_bound);
2559 _session->add_command(new StatefulDiffCommand (rv->region()));
2563 commit_reversible_command();
2567 begin_reversible_command (_("End point trim"));
2569 if (selection->selected (rv)) {
2571 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
2573 if (!(*i)->region()->locked()) {
2574 (*i)->region()->clear_changes();
2575 (*i)->region()->trim_end (new_bound);
2576 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2582 if (!rv->region()->locked()) {
2583 rv->region()->clear_changes ();
2584 rv->region()->trim_end (new_bound);
2585 _session->add_command (new StatefulDiffCommand (rv->region()));
2589 commit_reversible_command();
2598 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* /*event*/)
2603 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2604 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2608 Location* location = find_location_from_marker (marker, is_start);
2609 location->set_hidden (true, this);
2614 Editor::reposition_zoom_rect (framepos_t start, framepos_t end)
2616 double x1 = sample_to_pixel (start);
2617 double x2 = sample_to_pixel (end);
2618 double y2 = _full_canvas_height - 1.0;
2620 zoom_rect->set (ArdourCanvas::Rect (x1, 1.0, x2, y2));
2625 Editor::mouse_rename_region (ArdourCanvas::Item* /*item*/, GdkEvent* /*event*/)
2627 using namespace Gtkmm2ext;
2629 ArdourPrompter prompter (false);
2631 prompter.set_prompt (_("Name for region:"));
2632 prompter.set_initial_text (clicked_regionview->region()->name());
2633 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
2634 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
2635 prompter.show_all ();
2636 switch (prompter.run ()) {
2637 case Gtk::RESPONSE_ACCEPT:
2639 prompter.get_result(str);
2641 clicked_regionview->region()->set_name (str);
2650 Editor::mouse_brush_insert_region (RegionView* rv, framepos_t pos)
2652 /* no brushing without a useful snap setting */
2654 switch (_snap_mode) {
2656 return; /* can't work because it allows region to be placed anywhere */
2661 switch (_snap_type) {
2669 /* don't brush a copy over the original */
2671 if (pos == rv->region()->position()) {
2675 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
2677 if (rtv == 0 || !rtv->is_track()) {
2681 boost::shared_ptr<Playlist> playlist = rtv->playlist();
2682 double speed = rtv->track()->speed();
2684 playlist->clear_changes ();
2685 boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region(), true));
2686 playlist->add_region (new_region, (framepos_t) (pos * speed));
2687 _session->add_command (new StatefulDiffCommand (playlist));
2689 // playlist is frozen, so we have to update manually XXX this is disgusting
2691 playlist->RegionAdded (new_region); /* EMIT SIGNAL */
2695 Editor::track_height_step_timeout ()
2697 if (get_microseconds() - last_track_height_step_timestamp < 250000) {
2698 current_stepping_trackview = 0;
2705 Editor::add_region_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2707 assert (region_view);
2709 if (!region_view->region()->playlist()) {
2713 _region_motion_group->raise_to_top ();
2715 if (Config->get_edit_mode() == Splice) {
2716 _drags->add (new RegionSpliceDrag (this, item, region_view, selection->regions.by_layer()));
2718 _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), false, false));
2723 Editor::add_region_copy_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2725 assert (region_view);
2727 if (!region_view->region()->playlist()) {
2731 _region_motion_group->raise_to_top ();
2733 _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), false, true));
2737 Editor::add_region_brush_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2739 assert (region_view);
2741 if (!region_view->region()->playlist()) {
2745 if (Config->get_edit_mode() == Splice) {
2749 _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), true, false));
2751 begin_reversible_command (Operations::drag_region_brush);
2754 /** Start a grab where a time range is selected, track(s) are selected, and the
2755 * user clicks and drags a region with a modifier in order to create a new region containing
2756 * the section of the clicked region that lies within the time range.
2759 Editor::start_selection_grab (ArdourCanvas::Item* /*item*/, GdkEvent* event)
2761 if (clicked_regionview == 0) {
2765 /* lets try to create new Region for the selection */
2767 vector<boost::shared_ptr<Region> > new_regions;
2768 create_region_from_selection (new_regions);
2770 if (new_regions.empty()) {
2774 /* XXX fix me one day to use all new regions */
2776 boost::shared_ptr<Region> region (new_regions.front());
2778 /* add it to the current stream/playlist.
2780 tricky: the streamview for the track will add a new regionview. we will
2781 catch the signal it sends when it creates the regionview to
2782 set the regionview we want to then drag.
2785 latest_regionviews.clear();
2786 sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
2788 /* A selection grab currently creates two undo/redo operations, one for
2789 creating the new region and another for moving it.
2792 begin_reversible_command (Operations::selection_grab);
2794 boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
2796 playlist->clear_changes ();
2797 clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
2798 _session->add_command(new StatefulDiffCommand (playlist));
2800 commit_reversible_command ();
2804 if (latest_regionviews.empty()) {
2805 /* something went wrong */
2809 /* we need to deselect all other regionviews, and select this one
2810 i'm ignoring undo stuff, because the region creation will take care of it
2812 selection->set (latest_regionviews);
2814 _drags->set (new RegionMoveDrag (this, latest_regionviews.front()->get_canvas_group(), latest_regionviews.front(), latest_regionviews, false, false), event);
2820 if (_drags->active ()) {
2823 selection->clear ();
2828 Editor::set_internal_edit (bool yn)
2830 if (_internal_editing == yn) {
2834 _internal_editing = yn;
2837 pre_internal_mouse_mode = mouse_mode;
2838 pre_internal_snap_type = _snap_type;
2839 pre_internal_snap_mode = _snap_mode;
2841 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2842 (*i)->enter_internal_edit_mode ();
2845 set_snap_to (internal_snap_type);
2846 set_snap_mode (internal_snap_mode);
2850 internal_snap_mode = _snap_mode;
2851 internal_snap_type = _snap_type;
2853 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2854 (*i)->leave_internal_edit_mode ();
2857 if (mouse_mode == MouseDraw && pre_internal_mouse_mode != MouseDraw) {
2858 /* we were drawing .. flip back to something sensible */
2859 set_mouse_mode (pre_internal_mouse_mode);
2862 set_snap_to (pre_internal_snap_type);
2863 set_snap_mode (pre_internal_snap_mode);
2866 set_canvas_cursor ();
2869 /** Update _join_object_range_state which indicate whether we are over the top or bottom half of a region view,
2870 * used by the `join object/range' tool mode.
2873 Editor::update_join_object_range_location (double /*x*/, double y)
2875 /* XXX: actually, this decides based on whether the mouse is in the top
2876 or bottom half of a the waveform part RouteTimeAxisView;
2878 Note that entered_{track,regionview} is not always setup (e.g. if
2879 the mouse is over a TimeSelection), and to get a Region
2880 that we're over requires searching the playlist.
2883 if ( !get_smart_mode() ) {
2884 _join_object_range_state = JOIN_OBJECT_RANGE_NONE;
2888 if (mouse_mode == MouseObject) {
2889 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2890 } else if (mouse_mode == MouseRange) {
2891 _join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
2894 /* XXX: maybe we should make entered_track work in all cases, rather than resorting to this */
2895 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
2899 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
2904 rtv->canvas_display()->canvas_to_item (cx, cy);
2906 double const c = cy / (rtv->view()->child_height() - TimeAxisViewItem::NAME_HIGHLIGHT_SIZE);
2908 _join_object_range_state = c <= 0.5 ? JOIN_OBJECT_RANGE_RANGE : JOIN_OBJECT_RANGE_OBJECT;
2914 Editor::effective_mouse_mode () const
2916 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
2918 } else if (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE) {
2926 Editor::remove_midi_note (ArdourCanvas::Item* item, GdkEvent *)
2928 NoteBase* e = reinterpret_cast<NoteBase*> (item->get_data ("notebase"));
2931 e->region_view().delete_note (e->note ());
2935 Editor::set_canvas_cursor_for_region_view (double x, RegionView* rv)
2937 /* XXX: this check should not be necessary */
2942 ArdourCanvas::Group* g = rv->get_canvas_group ();
2943 ArdourCanvas::Group* p = g->parent ();
2945 /* Compute x in region view parent coordinates */
2947 p->canvas_to_item (x, dy);
2949 boost::optional<ArdourCanvas::Rect> item_bbox = g->bounding_box ();
2951 ArdourCanvas::Rect parent_bbox = g->item_to_parent (item_bbox.get ());
2953 /* First or last 10% of region is used for trimming, if the whole
2954 region is wider than 20 pixels at the current zoom level.
2957 double const w = parent_bbox.width();
2959 if (w > 20.0 && x >= parent_bbox.x0 && x < parent_bbox.x1) {
2961 Trimmable::CanTrim ct = rv->region()->can_trim ();
2963 if (((x - parent_bbox.x0) / w) < 0.10) {
2964 if (ct & Trimmable::FrontTrimEarlier) {
2965 set_canvas_cursor (_cursors->left_side_trim, true);
2967 set_canvas_cursor (_cursors->left_side_trim_right_only, true);
2969 } else if (((parent_bbox.x1 - x) / w) < 0.10) {
2970 if (ct & Trimmable::EndTrimLater) {
2971 set_canvas_cursor (_cursors->right_side_trim, true);
2973 set_canvas_cursor (_cursors->right_side_trim_left_only, true);
2979 /** Obtain the pointer position in canvas coordinates */
2981 Editor::get_pointer_position (double& x, double& y) const
2984 _track_canvas->get_pointer (px, py);
2985 _track_canvas->window_to_canvas (px, py, x, y);