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 get_track_canvas_group()->add_items_at_point (ArdourCanvas::Duple (x,y), items);
326 // first item will be the upper most
328 if (!items.empty()) {
329 const ArdourCanvas::Item* i = items.front();
331 if (i && i->parent() && i->parent()->get_data (X_("timeselection"))) {
332 pair<TimeAxisView*, int> tvp = trackview_by_y_position (_last_motion_y);
333 if (dynamic_cast<AutomationTimeAxisView*> (tvp.first)) {
334 current_canvas_cursor = _cursors->up_down;
341 set_canvas_cursor (current_canvas_cursor, true);
345 Editor::mouse_mode_object_range_toggled()
347 MouseMode m = mouse_mode;
349 Glib::RefPtr<Action> act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-range"));
351 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
354 if (tact->get_active()) {
355 m = MouseObject; //Smart mode turned to ON, force editing to Object mode
358 set_mouse_mode(m, true); //call this so the button styles can get updated
362 Editor::set_mouse_mode (MouseMode m, bool force)
364 if (_drags->active ()) {
368 if (!force && m == mouse_mode) {
372 Glib::RefPtr<Action> act;
376 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-range"));
380 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-object"));
384 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-draw"));
388 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-gain"));
392 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-zoom"));
396 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-timefx"));
400 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-audition"));
406 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
409 /* go there and back to ensure that the toggled handler is called to set up mouse_mode */
410 tact->set_active (false);
411 tact->set_active (true);
413 //NOTE: this will result in a call to mouse_mode_toggled which does the heavy lifting
417 Editor::mouse_mode_toggled (MouseMode m)
419 Glib::RefPtr<Action> act;
420 Glib::RefPtr<ToggleAction> tact;
424 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-range"));
428 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-object"));
432 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-draw"));
436 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-gain"));
440 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-zoom"));
444 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-timefx"));
448 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-audition"));
454 tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
457 if (!tact->get_active()) {
458 /* this was just the notification that the old mode has been
459 * left. we'll get called again with the new mode active in a
467 act = ActionManager::get_action (X_("MouseMode"), X_("toggle-internal-edit"));
468 tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
469 tact->set_active (true);
475 if (_session && mouse_mode == MouseAudition) {
476 /* stop transport and reset default speed to avoid oddness with
478 _session->request_transport_speed (0.0, true);
485 //TODO: set button styles for smart buttons
487 if ( smart_mode_action->get_active() ) {
488 if( mouse_mode == MouseObject ) { //smart active and object active
489 smart_mode_button.set_active(1);
490 smart_mode_button.set_name("smart mode button");
491 mouse_move_button.set_name("smart mode button");
492 } else { //smart active but object inactive
493 smart_mode_button.set_active(0);
494 smart_mode_button.set_name("smart mode button");
495 mouse_move_button.set_name("mouse mode button");
498 smart_mode_button.set_active(0);
499 smart_mode_button.set_name("mouse mode button");
500 mouse_move_button.set_name("mouse mode button");
504 set_canvas_cursor ();
505 set_gain_envelope_visibility ();
507 update_time_selection_display ();
509 MouseModeChanged (); /* EMIT SIGNAL */
513 Editor::update_time_selection_display ()
515 if (smart_mode_action->get_active()) {
516 /* not sure what to do here */
517 if (mouse_mode == MouseObject) {
521 switch (mouse_mode) {
523 selection->clear_objects ();
526 selection->clear_time ();
533 Editor::step_mouse_mode (bool next)
535 switch (current_mouse_mode()) {
538 if (Profile->get_sae()) {
539 set_mouse_mode (MouseZoom);
541 set_mouse_mode (MouseRange);
544 set_mouse_mode (MouseTimeFX);
549 if (next) set_mouse_mode (MouseDraw);
550 else set_mouse_mode (MouseObject);
554 if (next) set_mouse_mode (MouseZoom);
555 else set_mouse_mode (MouseRange);
560 if (Profile->get_sae()) {
561 set_mouse_mode (MouseTimeFX);
563 set_mouse_mode (MouseGain);
566 if (Profile->get_sae()) {
567 set_mouse_mode (MouseObject);
569 set_mouse_mode (MouseDraw);
575 if (next) set_mouse_mode (MouseTimeFX);
576 else set_mouse_mode (MouseZoom);
581 set_mouse_mode (MouseAudition);
583 if (Profile->get_sae()) {
584 set_mouse_mode (MouseZoom);
586 set_mouse_mode (MouseGain);
592 if (next) set_mouse_mode (MouseObject);
593 else set_mouse_mode (MouseTimeFX);
599 Editor::toggle_internal_editing_from_double_click (GdkEvent* event)
601 if (_drags->active()) {
602 _drags->end_grab (event);
605 ActionManager::do_action ("MouseMode", "toggle-internal-edit");
607 /* prevent reversion of edit cursor on button release */
609 pre_press_cursor = 0;
615 Editor::button_selection (ArdourCanvas::Item* /*item*/, GdkEvent* event, ItemType item_type)
617 /* in object/audition/timefx/gain-automation mode,
618 any button press sets the selection if the object
619 can be selected. this is a bit of hack, because
620 we want to avoid this if the mouse operation is a
623 note: not dbl-click or triple-click
625 Also note that there is no region selection in internal edit mode, otherwise
626 for operations operating on the selection (e.g. cut) it is not obvious whether
627 to cut notes or regions.
630 MouseMode eff_mouse_mode = mouse_mode;
632 if (get_smart_mode() && eff_mouse_mode == MouseRange && event->button.button == 3 && item_type == RegionItem) {
633 /* context clicks are always about object properties, even if
634 we're in range mode within smart mode.
636 eff_mouse_mode = MouseObject;
639 if (((mouse_mode != MouseObject) &&
640 (mouse_mode != MouseAudition || item_type != RegionItem) &&
641 (mouse_mode != MouseTimeFX || item_type != RegionItem) &&
642 (mouse_mode != MouseGain) &&
643 (mouse_mode != MouseDraw)) ||
644 ((event->type != GDK_BUTTON_PRESS && event->type != GDK_BUTTON_RELEASE) || event->button.button > 3) ||
645 (internal_editing() && mouse_mode != MouseTimeFX)) {
650 if (event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) {
652 if ((event->button.state & Keyboard::RelevantModifierKeyMask) && event->button.button != 1) {
654 /* almost no selection action on modified button-2 or button-3 events */
656 if (item_type != RegionItem && event->button.button != 2) {
662 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
663 bool press = (event->type == GDK_BUTTON_PRESS);
668 if (eff_mouse_mode != MouseRange) {
669 set_selected_regionview_from_click (press, op);
671 /* don't change the selection unless the
672 clicked track is not currently selected. if
673 so, "collapse" the selection to just this
676 if (!selection->selected (clicked_axisview)) {
677 set_selected_track_as_side_effect (Selection::Set);
681 if (eff_mouse_mode != MouseRange) {
682 set_selected_regionview_from_click (press, op);
687 case RegionViewNameHighlight:
689 case LeftFrameHandle:
690 case RightFrameHandle:
691 if (eff_mouse_mode != MouseRange) {
692 set_selected_regionview_from_click (press, op);
693 } else if (event->type == GDK_BUTTON_PRESS) {
694 set_selected_track_as_side_effect (op);
698 case FadeInHandleItem:
699 case FadeInTrimHandleItem:
701 case FadeOutHandleItem:
702 case FadeOutTrimHandleItem:
704 case StartCrossFadeItem:
705 case EndCrossFadeItem:
706 if (eff_mouse_mode != MouseRange) {
707 cerr << "Should be setting selected regionview\n";
708 set_selected_regionview_from_click (press, op);
709 } else if (event->type == GDK_BUTTON_PRESS) {
710 set_selected_track_as_side_effect (op);
714 case ControlPointItem:
715 set_selected_track_as_side_effect (op);
716 if (eff_mouse_mode != MouseRange) {
717 set_selected_control_point_from_click (press, op);
722 /* for context click, select track */
723 if (event->button.button == 3) {
724 selection->clear_tracks ();
725 set_selected_track_as_side_effect (op);
729 case AutomationTrackItem:
730 set_selected_track_as_side_effect (op);
739 Editor::button_press_handler_1 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
741 /* single mouse clicks on any of these item types operate
742 independent of mouse mode, mostly because they are
743 not on the main track canvas or because we want
748 case PlayheadCursorItem:
749 _drags->set (new CursorDrag (this, *playhead_cursor, true), event);
753 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
754 hide_marker (item, event);
756 _drags->set (new MarkerDrag (this, item), event);
760 case TempoMarkerItem:
762 TempoMarker* m = reinterpret_cast<TempoMarker*> (item->get_data ("marker"));
765 new TempoMarkerDrag (
768 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
775 case MeterMarkerItem:
777 MeterMarker* m = reinterpret_cast<MeterMarker*> (item->get_data ("marker"));
780 new MeterMarkerDrag (
783 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
791 _drags->set (new VideoTimeLineDrag (this, item), event);
798 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
799 _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
805 case RangeMarkerBarItem:
806 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
807 _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
809 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateRangeMarker), event);
814 case CdMarkerBarItem:
815 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
816 _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
818 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateCDMarker), event);
823 case TransportMarkerBarItem:
824 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
825 _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
827 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateTransportMarker), event);
836 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
837 /* special case: allow trim of range selections in joined object mode;
838 in theory eff should equal MouseRange in this case, but it doesn't
839 because entering the range selection canvas item results in entered_regionview
840 being set to 0, so update_join_object_range_location acts as if we aren't
843 if (item_type == StartSelectionTrimItem) {
844 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
845 } else if (item_type == EndSelectionTrimItem) {
846 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
850 Editing::MouseMode eff = effective_mouse_mode ();
852 /* special case: allow drag of region fade in/out in object mode with join object/range enabled */
853 if (item_type == FadeInHandleItem || item_type == FadeOutHandleItem) {
857 /* there is no Range mode when in internal edit mode */
858 if (eff == MouseRange && internal_editing()) {
865 case StartSelectionTrimItem:
866 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
869 case EndSelectionTrimItem:
870 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
874 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
875 start_selection_grab (item, event);
877 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
878 /* grab selection for moving */
879 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionMove), event);
881 double const y = event->button.y;
882 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
884 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
885 if ( get_smart_mode() && atv) {
886 /* smart "join" mode: drag automation */
887 _drags->set (new AutomationRangeDrag (this, atv, selection->time), event, _cursors->up_down);
889 /* this was debated, but decided the more common action was to
890 make a new selection */
891 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
898 if (internal_editing()) {
899 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
900 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
904 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::RangeSelectModifier)) {
905 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionExtend), event);
907 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
913 case RegionViewNameHighlight:
914 if (!clicked_regionview->region()->locked()) {
915 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
921 if (!internal_editing()) {
922 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::RangeSelectModifier)) {
923 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionExtend), event);
925 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
935 /* Existing note: allow trimming/motion */
936 if (internal_editing()) {
937 /* trim notes if we're in internal edit mode and near the ends of the note */
938 NoteBase* cn = reinterpret_cast<NoteBase*>(item->get_data ("notebase"));
940 if (cn->big_enough_to_trim() && cn->mouse_near_ends()) {
941 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
943 _drags->set (new NoteDrag (this, item), event);
949 if (internal_editing()) {
950 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
951 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
965 /* Existing note: allow trimming/motion */
966 if (internal_editing()) {
967 NoteBase* cn = reinterpret_cast<NoteBase*> (item->get_data ("notebase"));
969 if (cn->big_enough_to_trim() && cn->mouse_near_ends()) {
970 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
972 _drags->set (new NoteDrag (this, item), event);
982 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)) &&
983 event->type == GDK_BUTTON_PRESS) {
985 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
987 } else if (event->type == GDK_BUTTON_PRESS) {
990 case FadeInHandleItem:
992 _drags->set (new FadeInDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), selection->regions), event, _cursors->fade_in);
996 case FadeOutHandleItem:
998 _drags->set (new FadeOutDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), selection->regions), event, _cursors->fade_out);
1002 case StartCrossFadeItem:
1003 case EndCrossFadeItem:
1004 /* we might allow user to grab inside the fade to trim a region with preserve_fade_anchor. for not this is not fully implemented */
1005 // if (!clicked_regionview->region()->locked()) {
1006 // _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer(), true), event);
1011 case FeatureLineItem:
1013 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier)) {
1014 remove_transient(item);
1018 _drags->set (new FeatureLineDrag (this, item), event);
1024 if (dynamic_cast<AutomationRegionView*> (clicked_regionview)) {
1025 /* click on an automation region view; do nothing here and let the ARV's signal handler
1031 if (internal_editing ()) {
1035 /* click on a normal region view */
1036 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
1037 add_region_copy_drag (item, event, clicked_regionview);
1038 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
1039 add_region_brush_drag (item, event, clicked_regionview);
1041 add_region_drag (item, event, clicked_regionview);
1045 // if (!internal_editing() && (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE && !selection->regions.empty())) {
1046 // _drags->add (new SelectionDrag (this, clicked_axisview->get_selection_rect (clicked_selection)->rect, SelectionDrag::SelectionMove));
1049 _drags->start_grab (event);
1053 case RegionViewNameHighlight:
1054 case LeftFrameHandle:
1055 case RightFrameHandle:
1056 if (!clicked_regionview->region()->locked()) {
1057 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1062 case FadeInTrimHandleItem:
1063 case FadeOutTrimHandleItem:
1064 if (!clicked_regionview->region()->locked()) {
1065 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer(), true), event);
1070 case RegionViewName:
1072 /* rename happens on edit clicks */
1073 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer()), event);
1078 case ControlPointItem:
1079 _drags->set (new ControlPointDrag (this, item), event);
1083 case AutomationLineItem:
1084 _drags->set (new LineDrag (this, item), event);
1089 if (internal_editing()) {
1090 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
1091 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
1095 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
1099 case AutomationTrackItem:
1101 TimeAxisView* parent = clicked_axisview->get_parent ();
1102 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (clicked_axisview);
1104 if (parent && dynamic_cast<MidiTimeAxisView*> (parent) && atv->show_regions ()) {
1106 RouteTimeAxisView* p = dynamic_cast<RouteTimeAxisView*> (parent);
1108 boost::shared_ptr<Playlist> pl = p->track()->playlist ();
1109 if (pl->n_regions() == 0) {
1110 /* Parent has no regions; create one so that we have somewhere to put automation */
1111 _drags->set (new RegionCreateDrag (this, item, parent), event);
1113 /* See if there's a region before the click that we can extend, and extend it if so */
1114 framepos_t const t = canvas_event_sample (event);
1115 boost::shared_ptr<Region> prev = pl->find_next_region (t, End, -1);
1117 _drags->set (new RegionCreateDrag (this, item, parent), event);
1119 prev->set_length (t - prev->position ());
1123 /* rubberband drag to select automation points */
1124 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
1131 if ( get_smart_mode() ) {
1132 /* we're in "smart" joined mode, and we've clicked on a Selection */
1133 double const y = event->button.y;
1134 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
1136 /* if we're over an automation track, start a drag of its data */
1137 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
1139 _drags->set (new AutomationRangeDrag (this, atv, selection->time), event, _cursors->up_down);
1142 /* if we're over a track and a region, and in the `object' part of a region,
1143 put a selection around the region and drag both
1145 /* RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1146 if (rtv && _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
1147 boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track> (rtv->route ());
1149 boost::shared_ptr<Playlist> pl = t->playlist ();
1152 boost::shared_ptr<Region> r = pl->top_region_at (canvas_event_sample (event));
1154 RegionView* rv = rtv->view()->find_view (r);
1155 clicked_selection = select_range (rv->region()->position(),
1156 rv->region()->last_frame()+1);
1157 _drags->add (new SelectionDrag (this, item, SelectionDrag::SelectionMove));
1158 list<RegionView*> rvs;
1160 _drags->add (new RegionMoveDrag (this, item, rv, rvs, false, false));
1161 _drags->start_grab (event);
1185 switch (item_type) {
1187 _drags->set (new LineDrag (this, item), event);
1190 case ControlPointItem:
1191 _drags->set (new ControlPointDrag (this, item), event);
1197 AudioRegionView* arv = dynamic_cast<AudioRegionView *> (clicked_regionview);
1199 _drags->set (new AutomationRangeDrag (this, arv, selection->time), event, _cursors->up_down);
1200 _drags->start_grab (event);
1206 case AutomationLineItem:
1207 _drags->set (new LineDrag (this, item), event);
1217 if (event->type == GDK_BUTTON_PRESS) {
1218 _drags->set (new MouseZoomDrag (this, item), event);
1225 if (internal_editing() && item_type == NoteItem ) {
1226 /* drag notes if we're in internal edit mode */
1227 NoteBase* cn = reinterpret_cast<NoteBase*>(item->get_data ("notebase"));
1229 if (cn->big_enough_to_trim()) {
1230 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
1233 } else if (clicked_regionview) {
1235 _drags->set (new TimeFXDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1241 _drags->set (new ScrubDrag (this, item), event);
1242 scrub_reversals = 0;
1243 scrub_reverse_distance = 0;
1244 last_scrub_x = event->button.x;
1245 scrubbing_direction = 0;
1246 set_canvas_cursor (_cursors->transparent);
1258 Editor::button_press_handler_2 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1260 Editing::MouseMode const eff = effective_mouse_mode ();
1263 switch (item_type) {
1265 if (internal_editing ()) {
1266 /* no region drags in internal edit mode */
1270 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
1271 add_region_copy_drag (item, event, clicked_regionview);
1273 add_region_drag (item, event, clicked_regionview);
1275 _drags->start_grab (event);
1278 case ControlPointItem:
1279 _drags->set (new ControlPointDrag (this, item), event);
1287 switch (item_type) {
1288 case RegionViewNameHighlight:
1289 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1293 case LeftFrameHandle:
1294 case RightFrameHandle:
1295 if (!internal_editing ()) {
1296 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1301 case RegionViewName:
1302 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer()), event);
1316 /* relax till release */
1322 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1323 temporal_zoom_to_frame (false, canvas_event_sample (event));
1325 temporal_zoom_to_frame (true, canvas_event_sample(event));
1338 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1340 if (event->type == GDK_2BUTTON_PRESS) {
1341 _drags->mark_double_click ();
1342 gdk_pointer_ungrab (GDK_CURRENT_TIME);
1346 if (event->type != GDK_BUTTON_PRESS) {
1350 Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->_track_canvas_viewport->get_window();
1352 if (canvas_window) {
1353 Glib::RefPtr<const Gdk::Window> pointer_window;
1356 Gdk::ModifierType mask;
1358 pointer_window = canvas_window->get_pointer (x, y, mask);
1360 if (pointer_window == _track_canvas->get_window()) {
1361 _track_canvas->window_to_canvas (x, y, wx, wy);
1365 pre_press_cursor = current_canvas_cursor;
1367 _track_canvas->grab_focus();
1369 if (_session && _session->actively_recording()) {
1373 if (internal_editing()) {
1374 bool leave_internal_edit_mode = false;
1376 switch (item_type) {
1381 if (!dynamic_cast<MidiRegionView*> (clicked_regionview) && !dynamic_cast<AutomationRegionView*> (clicked_regionview)) {
1382 leave_internal_edit_mode = true;
1386 case PlayheadCursorItem:
1388 case TempoMarkerItem:
1389 case MeterMarkerItem:
1393 case RangeMarkerBarItem:
1394 case CdMarkerBarItem:
1395 case TransportMarkerBarItem:
1397 /* button press on these events never does anything to
1398 change the editing mode.
1406 if (leave_internal_edit_mode) {
1407 ActionManager::do_action ("MouseMode", "toggle-internal-edit");
1411 button_selection (item, event, item_type);
1413 if (!_drags->active () &&
1414 (Keyboard::is_delete_event (&event->button) ||
1415 Keyboard::is_context_menu_event (&event->button) ||
1416 Keyboard::is_edit_event (&event->button))) {
1418 /* handled by button release */
1422 //not rolling, range mode click + join_play_range : locate the PH here
1423 if ( !_drags->active () && !_session->transport_rolling() && ( effective_mouse_mode() == MouseRange ) && Config->get_always_play_range() ) {
1424 framepos_t where = canvas_event_sample (event);
1426 _session->request_locate (where, false);
1429 switch (event->button.button) {
1431 return button_press_handler_1 (item, event, item_type);
1435 return button_press_handler_2 (item, event, item_type);
1442 return button_press_dispatch (&event->button);
1451 Editor::button_press_dispatch (GdkEventButton* ev)
1453 /* this function is intended only for buttons 4 and above.
1456 Gtkmm2ext::MouseButton b (ev->state, ev->button);
1457 return button_bindings->activate (b, Gtkmm2ext::Bindings::Press);
1461 Editor::button_release_dispatch (GdkEventButton* ev)
1463 /* this function is intended only for buttons 4 and above.
1466 Gtkmm2ext::MouseButton b (ev->state, ev->button);
1467 return button_bindings->activate (b, Gtkmm2ext::Bindings::Release);
1471 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1473 framepos_t where = canvas_event_sample (event);
1474 AutomationTimeAxisView* atv = 0;
1476 if (pre_press_cursor) {
1477 set_canvas_cursor (pre_press_cursor);
1478 pre_press_cursor = 0;
1481 /* no action if we're recording */
1483 if (_session && _session->actively_recording()) {
1487 /* see if we're finishing a drag */
1489 bool were_dragging = false;
1490 if (_drags->active ()) {
1491 bool const r = _drags->end_grab (event);
1493 /* grab dragged, so do nothing else */
1497 were_dragging = true;
1500 update_region_layering_order_editor ();
1502 /* edit events get handled here */
1504 if (!_drags->active () && Keyboard::is_edit_event (&event->button)) {
1505 switch (item_type) {
1507 show_region_properties ();
1510 case TempoMarkerItem: {
1512 TempoMarker* tempo_marker;
1514 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
1515 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
1519 if ((tempo_marker = dynamic_cast<TempoMarker*> (marker)) == 0) {
1520 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
1524 edit_tempo_marker (*tempo_marker);
1528 case MeterMarkerItem: {
1530 MeterMarker* meter_marker;
1532 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
1533 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
1537 if ((meter_marker = dynamic_cast<MeterMarker*> (marker)) == 0) {
1538 fatal << _("programming error: marker for meter is not a meter marker!") << endmsg;
1541 edit_meter_marker (*meter_marker);
1545 case RegionViewName:
1546 if (clicked_regionview->name_active()) {
1547 return mouse_rename_region (item, event);
1551 case ControlPointItem:
1552 edit_control_point (item);
1561 /* context menu events get handled here */
1562 if (Keyboard::is_context_menu_event (&event->button)) {
1564 context_click_event = *event;
1566 if (!_drags->active ()) {
1568 /* no matter which button pops up the context menu, tell the menu
1569 widget to use button 1 to drive menu selection.
1572 switch (item_type) {
1574 case FadeInHandleItem:
1575 case FadeInTrimHandleItem:
1576 case StartCrossFadeItem:
1577 popup_xfade_in_context_menu (1, event->button.time, item, item_type);
1581 case FadeOutHandleItem:
1582 case FadeOutTrimHandleItem:
1583 case EndCrossFadeItem:
1584 popup_xfade_out_context_menu (1, event->button.time, item, item_type);
1588 popup_track_context_menu (1, event->button.time, item_type, false);
1592 case RegionViewNameHighlight:
1593 case LeftFrameHandle:
1594 case RightFrameHandle:
1595 case RegionViewName:
1596 popup_track_context_menu (1, event->button.time, item_type, false);
1600 popup_track_context_menu (1, event->button.time, item_type, true);
1603 case AutomationTrackItem:
1604 popup_track_context_menu (1, event->button.time, item_type, false);
1608 case RangeMarkerBarItem:
1609 case TransportMarkerBarItem:
1610 case CdMarkerBarItem:
1614 popup_ruler_menu (where, item_type);
1618 marker_context_menu (&event->button, item);
1621 case TempoMarkerItem:
1622 tempo_or_meter_marker_context_menu (&event->button, item);
1625 case MeterMarkerItem:
1626 tempo_or_meter_marker_context_menu (&event->button, item);
1629 case CrossfadeViewItem:
1630 popup_track_context_menu (1, event->button.time, item_type, false);
1633 case ControlPointItem:
1634 popup_control_point_context_menu (item, event);
1645 /* delete events get handled here */
1647 Editing::MouseMode const eff = effective_mouse_mode ();
1649 if (!_drags->active () && Keyboard::is_delete_event (&event->button)) {
1651 switch (item_type) {
1652 case TempoMarkerItem:
1653 remove_tempo_marker (item);
1656 case MeterMarkerItem:
1657 remove_meter_marker (item);
1661 remove_marker (*item, event);
1665 if (eff == MouseObject) {
1666 remove_clicked_region ();
1670 case ControlPointItem:
1671 remove_control_point (item);
1675 remove_midi_note (item, event);
1684 switch (event->button.button) {
1687 switch (item_type) {
1688 /* see comments in button_press_handler */
1689 case PlayheadCursorItem:
1692 case AutomationLineItem:
1693 case StartSelectionTrimItem:
1694 case EndSelectionTrimItem:
1698 if (!_dragging_playhead) {
1699 snap_to_with_modifier (where, event, 0, true);
1700 mouse_add_new_marker (where);
1704 case CdMarkerBarItem:
1705 if (!_dragging_playhead) {
1706 // if we get here then a dragged range wasn't done
1707 snap_to_with_modifier (where, event, 0, true);
1708 mouse_add_new_marker (where, true);
1713 if (!_dragging_playhead) {
1714 snap_to_with_modifier (where, event);
1715 mouse_add_new_tempo_event (where);
1720 if (!_dragging_playhead) {
1721 mouse_add_new_meter_event (pixel_to_sample (event->button.x));
1732 switch (item_type) {
1733 case AutomationTrackItem:
1734 atv = dynamic_cast<AutomationTimeAxisView*>(clicked_axisview);
1736 bool with_guard_points = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
1737 atv->add_automation_event (event, where, event->button.y, with_guard_points);
1747 switch (item_type) {
1750 /* check that we didn't drag before releasing, since
1751 its really annoying to create new control
1752 points when doing this.
1754 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (clicked_regionview);
1755 if (!were_dragging && arv) {
1756 bool with_guard_points = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
1757 arv->add_gain_point_event (item, event, with_guard_points);
1763 case AutomationTrackItem: {
1764 bool with_guard_points = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
1765 dynamic_cast<AutomationTimeAxisView*>(clicked_axisview)->
1766 add_automation_event (event, where, event->button.y, with_guard_points);
1776 set_canvas_cursor (current_canvas_cursor);
1777 if (scrubbing_direction == 0) {
1778 /* no drag, just a click */
1779 switch (item_type) {
1781 play_selected_region ();
1787 /* make sure we stop */
1788 _session->request_transport_speed (0.0);
1797 /* do any (de)selection operations that should occur on button release */
1798 button_selection (item, event, item_type);
1807 switch (item_type) {
1809 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1811 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier|Keyboard::SecondaryModifier))) {
1814 // Button2 click is unused
1829 // x_style_paste (where, 1.0);
1850 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1857 switch (item_type) {
1858 case ControlPointItem:
1859 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1860 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1865 at_y = cp->get_y ();
1866 cp->i2w (at_x, at_y);
1870 fraction = 1.0 - (cp->get_y() / cp->line().height());
1872 if (is_drawable() && !_drags->active ()) {
1873 set_canvas_cursor (_cursors->fader);
1876 _verbose_cursor->set (cp->line().get_verbose_cursor_string (fraction), at_x, at_y);
1877 _verbose_cursor->show ();
1882 if (mouse_mode == MouseGain) {
1883 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1885 line->set_outline_color (ARDOUR_UI::config()->get_canvasvar_EnteredGainLine());
1887 if (is_drawable()) {
1888 set_canvas_cursor (_cursors->fader);
1893 case AutomationLineItem:
1894 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1895 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1897 line->set_outline_color (ARDOUR_UI::config()->get_canvasvar_EnteredAutomationLine());
1899 if (is_drawable()) {
1900 set_canvas_cursor (_cursors->fader);
1905 case RegionViewNameHighlight:
1906 if (is_drawable() && effective_mouse_mode() == MouseObject && entered_regionview) {
1907 set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1908 _over_region_trim_target = true;
1912 case LeftFrameHandle:
1913 case RightFrameHandle:
1914 if (is_drawable() && effective_mouse_mode() == MouseObject && !internal_editing() && entered_regionview) {
1915 set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1920 switch (effective_mouse_mode()) {
1922 set_canvas_cursor (_cursors->selector);
1925 set_canvas_cursor (which_grabber_cursor());
1930 case StartSelectionTrimItem:
1931 if (is_drawable()) {
1932 set_canvas_cursor (_cursors->left_side_trim);
1935 case EndSelectionTrimItem:
1936 if (is_drawable()) {
1937 set_canvas_cursor (_cursors->right_side_trim);
1941 case PlayheadCursorItem:
1942 if (is_drawable()) {
1943 switch (_edit_point) {
1945 set_canvas_cursor (_cursors->grabber_edit_point);
1948 set_canvas_cursor (_cursors->grabber);
1955 case RegionViewName:
1957 /* when the name is not an active item, the entire name highlight is for trimming */
1959 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1960 if (mouse_mode == MouseObject && is_drawable()) {
1961 set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1962 _over_region_trim_target = true;
1968 case AutomationTrackItem:
1969 if (is_drawable()) {
1970 Gdk::Cursor *cursor;
1971 switch (mouse_mode) {
1973 cursor = _cursors->selector;
1976 cursor = _cursors->zoom_in;
1979 cursor = _cursors->cross_hair;
1983 set_canvas_cursor (cursor);
1985 AutomationTimeAxisView* atv;
1986 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1987 clear_entered_track = false;
1988 set_entered_track (atv);
1994 case RangeMarkerBarItem:
1995 case TransportMarkerBarItem:
1996 case CdMarkerBarItem:
1999 if (is_drawable()) {
2000 set_canvas_cursor (_cursors->timebar);
2005 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2008 entered_marker = marker;
2009 marker->set_color_rgba (ARDOUR_UI::config()->get_canvasvar_EnteredMarker());
2011 case MeterMarkerItem:
2012 case TempoMarkerItem:
2013 if (is_drawable()) {
2014 set_canvas_cursor (_cursors->timebar);
2018 case FadeInHandleItem:
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_in);
2029 case FadeInTrimHandleItem:
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_trim_in);
2040 case FadeOutHandleItem:
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_out);
2051 case FadeOutTrimHandleItem:
2052 if (mouse_mode == MouseObject && !internal_editing()) {
2053 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
2055 RegionView* rv = static_cast<RegionView*>(item->get_data ("regionview"));
2056 rect->set_fill_color (rv->get_fill_color ());
2057 set_canvas_cursor (_cursors->fade_trim_out);
2062 case FeatureLineItem:
2064 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
2065 line->set_outline_color (0xFF0000FF);
2070 if ( get_smart_mode() ) {
2071 set_canvas_cursor ();
2079 /* second pass to handle entered track status in a comprehensible way.
2082 switch (item_type) {
2084 case AutomationLineItem:
2085 case ControlPointItem:
2086 /* these do not affect the current entered track state */
2087 clear_entered_track = false;
2090 case AutomationTrackItem:
2091 /* handled above already */
2095 set_entered_track (0);
2103 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent*, ItemType item_type)
2111 switch (item_type) {
2112 case ControlPointItem:
2113 if (is_drawable()) {
2114 set_canvas_cursor (current_canvas_cursor);
2117 _verbose_cursor->hide ();
2120 case RegionViewNameHighlight:
2121 case LeftFrameHandle:
2122 case RightFrameHandle:
2123 case StartSelectionTrimItem:
2124 case EndSelectionTrimItem:
2125 case PlayheadCursorItem:
2127 _over_region_trim_target = false;
2129 if (is_drawable()) {
2130 set_canvas_cursor (current_canvas_cursor);
2135 case AutomationLineItem:
2136 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
2138 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
2140 line->set_outline_color (al->get_line_color());
2143 if (is_drawable()) {
2144 set_canvas_cursor (current_canvas_cursor);
2148 case RegionViewName:
2149 /* see enter_handler() for notes */
2150 _over_region_trim_target = false;
2152 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
2153 if (is_drawable() && mouse_mode == MouseObject) {
2154 set_canvas_cursor (current_canvas_cursor);
2159 case RangeMarkerBarItem:
2160 case TransportMarkerBarItem:
2161 case CdMarkerBarItem:
2165 if (is_drawable()) {
2166 set_canvas_cursor (current_canvas_cursor);
2171 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2175 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
2176 location_flags_changed (loc, this);
2179 case MeterMarkerItem:
2180 case TempoMarkerItem:
2182 if (is_drawable()) {
2183 set_canvas_cursor (current_canvas_cursor);
2188 case FadeInTrimHandleItem:
2189 case FadeOutTrimHandleItem:
2190 case FadeInHandleItem:
2191 case FadeOutHandleItem:
2193 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
2195 rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_InactiveFadeHandle());
2198 set_canvas_cursor (current_canvas_cursor);
2201 case AutomationTrackItem:
2202 if (is_drawable()) {
2203 set_canvas_cursor (current_canvas_cursor);
2204 clear_entered_track = true;
2205 Glib::signal_idle().connect (sigc::mem_fun(*this, &Editor::left_automation_track));
2208 case FeatureLineItem:
2210 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
2211 line->set_outline_color (ARDOUR_UI::config()->get_canvasvar_ZeroLine());
2223 Editor::left_automation_track ()
2225 if (clear_entered_track) {
2226 set_entered_track (0);
2227 clear_entered_track = false;
2233 Editor::scrub (framepos_t frame, double current_x)
2237 if (scrubbing_direction == 0) {
2239 _session->request_locate (frame, false);
2240 _session->request_transport_speed (0.1);
2241 scrubbing_direction = 1;
2245 if (last_scrub_x > current_x) {
2247 /* pointer moved to the left */
2249 if (scrubbing_direction > 0) {
2251 /* we reversed direction to go backwards */
2254 scrub_reverse_distance += (int) (last_scrub_x - current_x);
2258 /* still moving to the left (backwards) */
2260 scrub_reversals = 0;
2261 scrub_reverse_distance = 0;
2263 delta = 0.01 * (last_scrub_x - current_x);
2264 _session->request_transport_speed_nonzero (_session->transport_speed() - delta);
2268 /* pointer moved to the right */
2270 if (scrubbing_direction < 0) {
2271 /* we reversed direction to go forward */
2274 scrub_reverse_distance += (int) (current_x - last_scrub_x);
2277 /* still moving to the right */
2279 scrub_reversals = 0;
2280 scrub_reverse_distance = 0;
2282 delta = 0.01 * (current_x - last_scrub_x);
2283 _session->request_transport_speed_nonzero (_session->transport_speed() + delta);
2287 /* if there have been more than 2 opposite motion moves detected, or one that moves
2288 back more than 10 pixels, reverse direction
2291 if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
2293 if (scrubbing_direction > 0) {
2294 /* was forwards, go backwards */
2295 _session->request_transport_speed (-0.1);
2296 scrubbing_direction = -1;
2298 /* was backwards, go forwards */
2299 _session->request_transport_speed (0.1);
2300 scrubbing_direction = 1;
2303 scrub_reverse_distance = 0;
2304 scrub_reversals = 0;
2308 last_scrub_x = current_x;
2312 Editor::motion_handler (ArdourCanvas::Item* /*item*/, GdkEvent* event, bool from_autoscroll)
2314 _last_motion_y = event->motion.y;
2316 if (event->motion.is_hint) {
2319 /* We call this so that MOTION_NOTIFY events continue to be
2320 delivered to the canvas. We need to do this because we set
2321 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
2322 the density of the events, at the expense of a round-trip
2323 to the server. Given that this will mostly occur on cases
2324 where DISPLAY = :0.0, and given the cost of what the motion
2325 event might do, its a good tradeoff.
2328 _track_canvas->get_pointer (x, y);
2331 if (current_stepping_trackview) {
2332 /* don't keep the persistent stepped trackview if the mouse moves */
2333 current_stepping_trackview = 0;
2334 step_timeout.disconnect ();
2337 if (_session && _session->actively_recording()) {
2338 /* Sorry. no dragging stuff around while we record */
2342 JoinObjectRangeState const old = _join_object_range_state;
2343 update_join_object_range_location (event->motion.x, event->motion.y);
2345 if (!_internal_editing && _join_object_range_state != old) {
2346 set_canvas_cursor ();
2349 if (!_internal_editing && _over_region_trim_target) {
2350 set_canvas_cursor_for_region_view (event->motion.x, entered_regionview);
2353 bool handled = false;
2354 if (_drags->active ()) {
2355 handled = _drags->motion_handler (event, from_autoscroll);
2362 track_canvas_motion (event);
2367 Editor::can_remove_control_point (ArdourCanvas::Item* item)
2369 ControlPoint* control_point;
2371 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2372 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2376 AutomationLine& line = control_point->line ();
2377 if (dynamic_cast<AudioRegionGainLine*> (&line)) {
2378 /* we shouldn't remove the first or last gain point in region gain lines */
2379 if (line.is_last_point(*control_point) || line.is_first_point(*control_point)) {
2388 Editor::remove_control_point (ArdourCanvas::Item* item)
2390 if (!can_remove_control_point (item)) {
2394 ControlPoint* control_point;
2396 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2397 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2401 control_point->line().remove_point (*control_point);
2405 Editor::edit_control_point (ArdourCanvas::Item* item)
2407 ControlPoint* p = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"));
2410 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2414 ControlPointDialog d (p);
2417 if (d.run () != RESPONSE_ACCEPT) {
2421 p->line().modify_point_y (*p, d.get_y_fraction ());
2425 Editor::edit_notes (TimeAxisViewItem& tavi)
2427 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(&tavi);
2433 MidiRegionView::Selection const & s = mrv->selection();
2439 EditNoteDialog* d = new EditNoteDialog (&(*s.begin())->region_view(), s);
2443 d->signal_response().connect (sigc::bind (sigc::mem_fun (*this, &Editor::note_edit_done), d));
2447 Editor::note_edit_done (int r, EditNoteDialog* d)
2454 Editor::visible_order_range (int* low, int* high) const
2456 *low = TimeAxisView::max_order ();
2459 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
2461 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
2463 if (!rtv->hidden()) {
2465 if (*high < rtv->order()) {
2466 *high = rtv->order ();
2469 if (*low > rtv->order()) {
2470 *low = rtv->order ();
2477 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
2479 /* Either add to or set the set the region selection, unless
2480 this is an alignment click (control used)
2483 if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
2484 TimeAxisView* tv = &rv.get_time_axis_view();
2485 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
2487 if (rtv && rtv->is_track()) {
2488 speed = rtv->track()->speed();
2491 framepos_t where = get_preferred_edit_position();
2495 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
2497 align_region (rv.region(), SyncPoint, (framepos_t) (where * speed));
2499 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
2501 align_region (rv.region(), End, (framepos_t) (where * speed));
2505 align_region (rv.region(), Start, (framepos_t) (where * speed));
2512 Editor::collect_new_region_view (RegionView* rv)
2514 latest_regionviews.push_back (rv);
2518 Editor::collect_and_select_new_region_view (RegionView* rv)
2521 latest_regionviews.push_back (rv);
2525 Editor::cancel_selection ()
2527 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2528 (*i)->hide_selection ();
2531 selection->clear ();
2532 clicked_selection = 0;
2536 Editor::cancel_time_selection ()
2538 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2539 (*i)->hide_selection ();
2541 selection->time.clear ();
2542 clicked_selection = 0;
2546 Editor::point_trim (GdkEvent* event, framepos_t new_bound)
2548 RegionView* rv = clicked_regionview;
2550 /* Choose action dependant on which button was pressed */
2551 switch (event->button.button) {
2553 begin_reversible_command (_("start point trim"));
2555 if (selection->selected (rv)) {
2556 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
2557 i != selection->regions.by_layer().end(); ++i)
2559 if (!(*i)->region()->locked()) {
2560 (*i)->region()->clear_changes ();
2561 (*i)->region()->trim_front (new_bound);
2562 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2567 if (!rv->region()->locked()) {
2568 rv->region()->clear_changes ();
2569 rv->region()->trim_front (new_bound);
2570 _session->add_command(new StatefulDiffCommand (rv->region()));
2574 commit_reversible_command();
2578 begin_reversible_command (_("End point trim"));
2580 if (selection->selected (rv)) {
2582 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
2584 if (!(*i)->region()->locked()) {
2585 (*i)->region()->clear_changes();
2586 (*i)->region()->trim_end (new_bound);
2587 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2593 if (!rv->region()->locked()) {
2594 rv->region()->clear_changes ();
2595 rv->region()->trim_end (new_bound);
2596 _session->add_command (new StatefulDiffCommand (rv->region()));
2600 commit_reversible_command();
2609 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* /*event*/)
2614 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2615 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2619 Location* location = find_location_from_marker (marker, is_start);
2620 location->set_hidden (true, this);
2625 Editor::reposition_zoom_rect (framepos_t start, framepos_t end)
2627 double x1 = sample_to_pixel (start);
2628 double x2 = sample_to_pixel (end);
2629 double y2 = _full_canvas_height - 1.0;
2631 zoom_rect->set (ArdourCanvas::Rect (x1, 1.0, x2, y2));
2636 Editor::mouse_rename_region (ArdourCanvas::Item* /*item*/, GdkEvent* /*event*/)
2638 using namespace Gtkmm2ext;
2640 ArdourPrompter prompter (false);
2642 prompter.set_prompt (_("Name for region:"));
2643 prompter.set_initial_text (clicked_regionview->region()->name());
2644 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
2645 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
2646 prompter.show_all ();
2647 switch (prompter.run ()) {
2648 case Gtk::RESPONSE_ACCEPT:
2650 prompter.get_result(str);
2652 clicked_regionview->region()->set_name (str);
2661 Editor::mouse_brush_insert_region (RegionView* rv, framepos_t pos)
2663 /* no brushing without a useful snap setting */
2665 switch (_snap_mode) {
2667 return; /* can't work because it allows region to be placed anywhere */
2672 switch (_snap_type) {
2680 /* don't brush a copy over the original */
2682 if (pos == rv->region()->position()) {
2686 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
2688 if (rtv == 0 || !rtv->is_track()) {
2692 boost::shared_ptr<Playlist> playlist = rtv->playlist();
2693 double speed = rtv->track()->speed();
2695 playlist->clear_changes ();
2696 boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region(), true));
2697 playlist->add_region (new_region, (framepos_t) (pos * speed));
2698 _session->add_command (new StatefulDiffCommand (playlist));
2700 // playlist is frozen, so we have to update manually XXX this is disgusting
2702 playlist->RegionAdded (new_region); /* EMIT SIGNAL */
2706 Editor::track_height_step_timeout ()
2708 if (get_microseconds() - last_track_height_step_timestamp < 250000) {
2709 current_stepping_trackview = 0;
2716 Editor::add_region_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2718 assert (region_view);
2720 if (!region_view->region()->playlist()) {
2724 _region_motion_group->raise_to_top ();
2726 if (Config->get_edit_mode() == Splice) {
2727 _drags->add (new RegionSpliceDrag (this, item, region_view, selection->regions.by_layer()));
2729 _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), false, false));
2734 Editor::add_region_copy_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2736 assert (region_view);
2738 if (!region_view->region()->playlist()) {
2742 _region_motion_group->raise_to_top ();
2744 _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), false, true));
2748 Editor::add_region_brush_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2750 assert (region_view);
2752 if (!region_view->region()->playlist()) {
2756 if (Config->get_edit_mode() == Splice) {
2760 _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), true, false));
2762 begin_reversible_command (Operations::drag_region_brush);
2765 /** Start a grab where a time range is selected, track(s) are selected, and the
2766 * user clicks and drags a region with a modifier in order to create a new region containing
2767 * the section of the clicked region that lies within the time range.
2770 Editor::start_selection_grab (ArdourCanvas::Item* /*item*/, GdkEvent* event)
2772 if (clicked_regionview == 0) {
2776 /* lets try to create new Region for the selection */
2778 vector<boost::shared_ptr<Region> > new_regions;
2779 create_region_from_selection (new_regions);
2781 if (new_regions.empty()) {
2785 /* XXX fix me one day to use all new regions */
2787 boost::shared_ptr<Region> region (new_regions.front());
2789 /* add it to the current stream/playlist.
2791 tricky: the streamview for the track will add a new regionview. we will
2792 catch the signal it sends when it creates the regionview to
2793 set the regionview we want to then drag.
2796 latest_regionviews.clear();
2797 sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
2799 /* A selection grab currently creates two undo/redo operations, one for
2800 creating the new region and another for moving it.
2803 begin_reversible_command (Operations::selection_grab);
2805 boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
2807 playlist->clear_changes ();
2808 clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
2809 _session->add_command(new StatefulDiffCommand (playlist));
2811 commit_reversible_command ();
2815 if (latest_regionviews.empty()) {
2816 /* something went wrong */
2820 /* we need to deselect all other regionviews, and select this one
2821 i'm ignoring undo stuff, because the region creation will take care of it
2823 selection->set (latest_regionviews);
2825 _drags->set (new RegionMoveDrag (this, latest_regionviews.front()->get_canvas_group(), latest_regionviews.front(), latest_regionviews, false, false), event);
2831 if (_drags->active ()) {
2834 selection->clear ();
2839 Editor::set_internal_edit (bool yn)
2841 if (_internal_editing == yn) {
2845 _internal_editing = yn;
2848 pre_internal_mouse_mode = mouse_mode;
2849 pre_internal_snap_type = _snap_type;
2850 pre_internal_snap_mode = _snap_mode;
2852 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2853 (*i)->enter_internal_edit_mode ();
2856 set_snap_to (internal_snap_type);
2857 set_snap_mode (internal_snap_mode);
2861 internal_snap_mode = _snap_mode;
2862 internal_snap_type = _snap_type;
2864 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2865 (*i)->leave_internal_edit_mode ();
2868 if (mouse_mode == MouseDraw && pre_internal_mouse_mode != MouseDraw) {
2869 /* we were drawing .. flip back to something sensible */
2870 set_mouse_mode (pre_internal_mouse_mode);
2873 set_snap_to (pre_internal_snap_type);
2874 set_snap_mode (pre_internal_snap_mode);
2877 set_canvas_cursor ();
2880 /** Update _join_object_range_state which indicate whether we are over the top or bottom half of a region view,
2881 * used by the `join object/range' tool mode.
2884 Editor::update_join_object_range_location (double /*x*/, double y)
2886 /* XXX: actually, this decides based on whether the mouse is in the top
2887 or bottom half of a the waveform part RouteTimeAxisView;
2889 Note that entered_{track,regionview} is not always setup (e.g. if
2890 the mouse is over a TimeSelection), and to get a Region
2891 that we're over requires searching the playlist.
2894 if ( !get_smart_mode() ) {
2895 _join_object_range_state = JOIN_OBJECT_RANGE_NONE;
2899 if (mouse_mode == MouseObject) {
2900 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2901 } else if (mouse_mode == MouseRange) {
2902 _join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
2905 /* XXX: maybe we should make entered_track work in all cases, rather than resorting to this */
2906 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
2910 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
2915 rtv->canvas_display()->canvas_to_item (cx, cy);
2917 double const c = cy / (rtv->view()->child_height() - TimeAxisViewItem::NAME_HIGHLIGHT_SIZE);
2919 _join_object_range_state = c <= 0.5 ? JOIN_OBJECT_RANGE_RANGE : JOIN_OBJECT_RANGE_OBJECT;
2925 Editor::effective_mouse_mode () const
2927 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
2929 } else if (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE) {
2937 Editor::remove_midi_note (ArdourCanvas::Item* item, GdkEvent *)
2939 NoteBase* e = reinterpret_cast<NoteBase*> (item->get_data ("notebase"));
2942 e->region_view().delete_note (e->note ());
2946 Editor::set_canvas_cursor_for_region_view (double x, RegionView* rv)
2948 /* XXX: this check should not be necessary */
2953 ArdourCanvas::Group* g = rv->get_canvas_group ();
2954 ArdourCanvas::Group* p = g->parent ();
2956 /* Compute x in region view parent coordinates */
2958 p->canvas_to_item (x, dy);
2960 boost::optional<ArdourCanvas::Rect> item_bbox = g->bounding_box ();
2962 ArdourCanvas::Rect parent_bbox = g->item_to_parent (item_bbox.get ());
2964 /* First or last 10% of region is used for trimming, if the whole
2965 region is wider than 20 pixels at the current zoom level.
2968 double const w = parent_bbox.width();
2970 if (w > 20.0 && x >= parent_bbox.x0 && x < parent_bbox.x1) {
2972 Trimmable::CanTrim ct = rv->region()->can_trim ();
2974 if (((x - parent_bbox.x0) / w) < 0.10) {
2975 if (ct & Trimmable::FrontTrimEarlier) {
2976 set_canvas_cursor (_cursors->left_side_trim, true);
2978 set_canvas_cursor (_cursors->left_side_trim_right_only, true);
2980 } else if (((parent_bbox.x1 - x) / w) < 0.10) {
2981 if (ct & Trimmable::EndTrimLater) {
2982 set_canvas_cursor (_cursors->right_side_trim, true);
2984 set_canvas_cursor (_cursors->right_side_trim_left_only, true);
2990 /** Obtain the pointer position in canvas coordinates */
2992 Editor::get_pointer_position (double& x, double& y) const
2995 _track_canvas->get_pointer (px, py);
2996 _track_canvas->window_to_canvas (px, py, x, y);