2 Copyright (C) 2000-2001 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
29 #include "pbd/error.h"
30 #include "pbd/enumwriter.h"
31 #include "pbd/memento_command.h"
32 #include "pbd/basename.h"
33 #include "pbd/stateful_diff_command.h"
35 #include "gtkmm2ext/bindings.h"
36 #include "gtkmm2ext/utils.h"
37 #include "gtkmm2ext/tearoff.h"
39 #include "canvas/canvas.h"
41 #include "ardour/audioregion.h"
42 #include "ardour/operations.h"
43 #include "ardour/playlist.h"
44 #include "ardour/profile.h"
45 #include "ardour/region_factory.h"
46 #include "ardour/route.h"
47 #include "ardour/session.h"
48 #include "ardour/types.h"
50 #include "ardour_ui.h"
53 #include "time_axis_view.h"
54 #include "audio_time_axis.h"
55 #include "audio_region_view.h"
56 #include "midi_region_view.h"
58 #include "streamview.h"
59 #include "region_gain_line.h"
60 #include "automation_time_axis.h"
61 #include "control_point.h"
64 #include "selection.h"
67 #include "rgb_macros.h"
68 #include "control_point_dialog.h"
69 #include "editor_drag.h"
70 #include "automation_region_view.h"
71 #include "edit_note_dialog.h"
72 #include "mouse_cursors.h"
73 #include "editor_cursors.h"
74 #include "verbose_cursor.h"
80 using namespace ARDOUR;
83 using namespace Editing;
84 using Gtkmm2ext::Keyboard;
87 Editor::mouse_frame (framepos_t& where, bool& in_track_canvas) const
89 /* gdk_window_get_pointer() has X11's XQueryPointer semantics in that it only
90 pays attentions to subwindows. this means that menu windows are ignored, and
91 if the pointer is in a menu, the return window from the call will be the
92 the regular subwindow *under* the menu.
94 this matters quite a lot if the pointer is moving around in a menu that overlaps
95 the track canvas because we will believe that we are within the track canvas
96 when we are not. therefore, we track enter/leave events for the track canvas
97 and allow that to override the result of gdk_window_get_pointer().
100 if (!within_track_canvas) {
105 Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->_track_canvas->get_window();
107 if (!canvas_window) {
111 Glib::RefPtr<const Gdk::Window> pointer_window = Gdk::Display::get_default()->get_window_at_pointer (x, y);
113 if (!pointer_window) {
117 if (pointer_window != canvas_window && pointer_window != _time_bars_canvas->get_window()) {
118 in_track_canvas = false;
122 in_track_canvas = true;
125 event.type = GDK_BUTTON_RELEASE;
129 where = window_event_sample (&event, 0, 0);
135 Editor::window_event_sample (GdkEvent const * event, double* pcx, double* pcy) const
140 if (!gdk_event_get_coords (event, &x, &y)) {
144 /* event coordinates are in window units, so convert to canvas
145 * (i.e. account for scrolling)
148 ArdourCanvas::Duple d = _track_canvas->window_to_canvas (ArdourCanvas::Duple (x, y));
158 return pixel_to_sample (d.x);
162 Editor::canvas_event_sample (GdkEvent const * event, double* pcx, double* pcy) const
167 /* event coordinates are already in canvas units */
169 if (!gdk_event_get_coords (event, &x, &y)) {
170 cerr << "!NO c COORDS for event type " << event->type << endl;
182 /* note that pixel_to_sample_from_event() never returns less than zero, so even if the pixel
183 position is negative (as can be the case with motion events in particular),
184 the frame location is always positive.
187 return pixel_to_sample_from_event (x);
191 Editor::which_grabber_cursor ()
193 Gdk::Cursor* c = _cursors->grabber;
195 if (_internal_editing) {
196 switch (mouse_mode) {
198 c = _cursors->midi_pencil;
202 c = _cursors->grabber_note;
206 c = _cursors->midi_resize;
210 c = _cursors->grabber_note;
219 switch (_edit_point) {
221 c = _cursors->grabber_edit_point;
224 boost::shared_ptr<Movable> m = _movable.lock();
225 if (m && m->locked()) {
226 c = _cursors->speaker;
236 Editor::set_current_trimmable (boost::shared_ptr<Trimmable> t)
238 boost::shared_ptr<Trimmable> st = _trimmable.lock();
240 if (!st || st == t) {
242 set_canvas_cursor ();
247 Editor::set_current_movable (boost::shared_ptr<Movable> m)
249 boost::shared_ptr<Movable> sm = _movable.lock();
251 if (!sm || sm != m) {
253 set_canvas_cursor ();
258 Editor::set_canvas_cursor ()
260 switch (mouse_mode) {
262 current_canvas_cursor = _cursors->selector;
263 if (_internal_editing) {
264 current_canvas_cursor = which_grabber_cursor();
269 current_canvas_cursor = which_grabber_cursor();
273 current_canvas_cursor = _cursors->midi_pencil;
277 current_canvas_cursor = _cursors->cross_hair;
281 if (Keyboard::the_keyboard().key_is_down (GDK_Control_L)) {
282 current_canvas_cursor = _cursors->zoom_out;
284 current_canvas_cursor = _cursors->zoom_in;
289 current_canvas_cursor = _cursors->time_fx; // just use playhead
293 current_canvas_cursor = _cursors->speaker;
297 if (!_internal_editing) {
298 switch (_join_object_range_state) {
299 case JOIN_OBJECT_RANGE_NONE:
301 case JOIN_OBJECT_RANGE_OBJECT:
302 current_canvas_cursor = which_grabber_cursor ();
304 case JOIN_OBJECT_RANGE_RANGE:
305 current_canvas_cursor = _cursors->selector;
310 /* up-down cursor as a cue that automation can be dragged up and down when in join object/range mode */
311 if (!_internal_editing && get_smart_mode() ) {
314 get_pointer_position (x, y);
316 if (x >= 0 && y >= 0) {
318 vector<ArdourCanvas::Item const *> items;
320 _track_canvas->root()->add_items_at_point (ArdourCanvas::Duple (x,y), items);
322 // first item will be the upper most
324 if (!items.empty()) {
325 const ArdourCanvas::Item* i = items.front();
327 if (i && i->parent() && i->parent()->get_data (X_("timeselection"))) {
328 pair<TimeAxisView*, int> tvp = trackview_by_y_position (_last_motion_y);
329 if (dynamic_cast<AutomationTimeAxisView*> (tvp.first)) {
330 current_canvas_cursor = _cursors->up_down;
337 set_canvas_cursor (current_canvas_cursor, true);
341 Editor::mouse_mode_object_range_toggled()
343 MouseMode m = mouse_mode;
345 Glib::RefPtr<Action> act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-range"));
347 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
350 if (tact->get_active()) {
351 m = MouseObject; //Smart mode turned to ON, force editing to Object mode
354 set_mouse_mode(m, true); //call this so the button styles can get updated
358 Editor::set_mouse_mode (MouseMode m, bool force)
360 if (_drags->active ()) {
364 if (!force && m == mouse_mode) {
368 Glib::RefPtr<Action> act;
372 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-range"));
376 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-object"));
380 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-draw"));
384 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-gain"));
388 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-zoom"));
392 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-timefx"));
396 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-audition"));
402 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
405 /* go there and back to ensure that the toggled handler is called to set up mouse_mode */
406 tact->set_active (false);
407 tact->set_active (true);
409 //NOTE: this will result in a call to mouse_mode_toggled which does the heavy lifting
413 Editor::mouse_mode_toggled (MouseMode m)
415 Glib::RefPtr<Action> act;
416 Glib::RefPtr<ToggleAction> tact;
420 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-range"));
424 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-object"));
428 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-draw"));
432 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-gain"));
436 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-zoom"));
440 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-timefx"));
444 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-audition"));
450 tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
453 if (!tact->get_active()) {
454 /* this was just the notification that the old mode has been
455 * left. we'll get called again with the new mode active in a
463 act = ActionManager::get_action (X_("MouseMode"), X_("toggle-internal-edit"));
464 tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
465 tact->set_active (true);
471 if (_session && mouse_mode == MouseAudition) {
472 /* stop transport and reset default speed to avoid oddness with
474 _session->request_transport_speed (0.0, true);
481 //TODO: set button styles for smart buttons
483 if ( smart_mode_action->get_active() ) {
484 if( mouse_mode == MouseObject ) { //smart active and object active
485 smart_mode_button.set_active(1);
486 smart_mode_button.set_name("smart mode button");
487 mouse_move_button.set_name("smart mode button");
488 } else { //smart active but object inactive
489 smart_mode_button.set_active(0);
490 smart_mode_button.set_name("smart mode button");
491 mouse_move_button.set_name("mouse mode button");
494 smart_mode_button.set_active(0);
495 smart_mode_button.set_name("mouse mode button");
496 mouse_move_button.set_name("mouse mode button");
500 set_canvas_cursor ();
501 set_gain_envelope_visibility ();
503 update_time_selection_display ();
505 MouseModeChanged (); /* EMIT SIGNAL */
509 Editor::update_time_selection_display ()
511 if (smart_mode_action->get_active()) {
512 /* not sure what to do here */
513 if (mouse_mode == MouseObject) {
517 switch (mouse_mode) {
519 selection->clear_objects ();
522 selection->clear_time ();
529 Editor::step_mouse_mode (bool next)
531 switch (current_mouse_mode()) {
534 if (Profile->get_sae()) {
535 set_mouse_mode (MouseZoom);
537 set_mouse_mode (MouseRange);
540 set_mouse_mode (MouseTimeFX);
545 if (next) set_mouse_mode (MouseDraw);
546 else set_mouse_mode (MouseObject);
550 if (next) set_mouse_mode (MouseZoom);
551 else set_mouse_mode (MouseRange);
556 if (Profile->get_sae()) {
557 set_mouse_mode (MouseTimeFX);
559 set_mouse_mode (MouseGain);
562 if (Profile->get_sae()) {
563 set_mouse_mode (MouseObject);
565 set_mouse_mode (MouseDraw);
571 if (next) set_mouse_mode (MouseTimeFX);
572 else set_mouse_mode (MouseZoom);
577 set_mouse_mode (MouseAudition);
579 if (Profile->get_sae()) {
580 set_mouse_mode (MouseZoom);
582 set_mouse_mode (MouseGain);
588 if (next) set_mouse_mode (MouseObject);
589 else set_mouse_mode (MouseTimeFX);
595 Editor::toggle_internal_editing_from_double_click (GdkEvent* event)
597 if (_drags->active()) {
598 _drags->end_grab (event);
601 ActionManager::do_action ("MouseMode", "toggle-internal-edit");
603 /* prevent reversion of edit cursor on button release */
605 pre_press_cursor = 0;
611 Editor::button_selection (ArdourCanvas::Item* /*item*/, GdkEvent* event, ItemType item_type)
613 /* in object/audition/timefx/gain-automation mode,
614 any button press sets the selection if the object
615 can be selected. this is a bit of hack, because
616 we want to avoid this if the mouse operation is a
619 note: not dbl-click or triple-click
621 Also note that there is no region selection in internal edit mode, otherwise
622 for operations operating on the selection (e.g. cut) it is not obvious whether
623 to cut notes or regions.
626 MouseMode eff_mouse_mode = mouse_mode;
628 if (get_smart_mode() && eff_mouse_mode == MouseRange && event->button.button == 3 && item_type == RegionItem) {
629 /* context clicks are always about object properties, even if
630 we're in range mode within smart mode.
632 eff_mouse_mode = MouseObject;
635 if (((mouse_mode != MouseObject) &&
636 (mouse_mode != MouseAudition || item_type != RegionItem) &&
637 (mouse_mode != MouseTimeFX || item_type != RegionItem) &&
638 (mouse_mode != MouseGain) &&
639 (mouse_mode != MouseDraw)) ||
640 ((event->type != GDK_BUTTON_PRESS && event->type != GDK_BUTTON_RELEASE) || event->button.button > 3) ||
641 (internal_editing() && mouse_mode != MouseTimeFX)) {
646 if (event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) {
648 if ((event->button.state & Keyboard::RelevantModifierKeyMask) && event->button.button != 1) {
650 /* almost no selection action on modified button-2 or button-3 events */
652 if (item_type != RegionItem && event->button.button != 2) {
658 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
659 bool press = (event->type == GDK_BUTTON_PRESS);
664 if (eff_mouse_mode != MouseRange) {
665 set_selected_regionview_from_click (press, op);
667 /* don't change the selection unless the
668 clicked track is not currently selected. if
669 so, "collapse" the selection to just this
672 if (!selection->selected (clicked_axisview)) {
673 set_selected_track_as_side_effect (Selection::Set);
677 if (eff_mouse_mode != MouseRange) {
678 set_selected_regionview_from_click (press, op);
683 case RegionViewNameHighlight:
685 case LeftFrameHandle:
686 case RightFrameHandle:
687 if (eff_mouse_mode != MouseRange) {
688 set_selected_regionview_from_click (press, op);
689 } else if (event->type == GDK_BUTTON_PRESS) {
690 set_selected_track_as_side_effect (op);
694 case FadeInHandleItem:
696 case FadeOutHandleItem:
698 case StartCrossFadeItem:
699 case EndCrossFadeItem:
700 if (eff_mouse_mode != MouseRange) {
701 cerr << "Should be setting selected regionview\n";
702 set_selected_regionview_from_click (press, op);
703 } else if (event->type == GDK_BUTTON_PRESS) {
704 set_selected_track_as_side_effect (op);
708 case ControlPointItem:
709 set_selected_track_as_side_effect (op);
710 if (eff_mouse_mode != MouseRange) {
711 set_selected_control_point_from_click (press, op);
716 /* for context click, select track */
717 if (event->button.button == 3) {
718 selection->clear_tracks ();
719 set_selected_track_as_side_effect (op);
723 case AutomationTrackItem:
724 set_selected_track_as_side_effect (op);
733 Editor::button_press_handler_1 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
735 /* single mouse clicks on any of these item types operate
736 independent of mouse mode, mostly because they are
737 not on the main track canvas or because we want
742 case PlayheadCursorItem:
743 _drags->set (new CursorDrag (this, *playhead_cursor, true), event);
747 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
748 hide_marker (item, event);
750 _drags->set (new MarkerDrag (this, item), event);
754 case TempoMarkerItem:
756 TempoMarker* m = reinterpret_cast<TempoMarker*> (item->get_data ("marker"));
759 new TempoMarkerDrag (
762 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
769 case MeterMarkerItem:
771 MeterMarker* m = reinterpret_cast<MeterMarker*> (item->get_data ("marker"));
774 new MeterMarkerDrag (
777 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
785 _drags->set (new VideoTimeLineDrag (this, item), event);
792 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
793 _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
799 case RangeMarkerBarItem:
800 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
801 _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
803 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateRangeMarker), event);
808 case CdMarkerBarItem:
809 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
810 _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
812 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateCDMarker), event);
817 case TransportMarkerBarItem:
818 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
819 _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
821 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateTransportMarker), event);
830 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
831 /* special case: allow trim of range selections in joined object mode;
832 in theory eff should equal MouseRange in this case, but it doesn't
833 because entering the range selection canvas item results in entered_regionview
834 being set to 0, so update_join_object_range_location acts as if we aren't
837 if (item_type == StartSelectionTrimItem) {
838 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
839 } else if (item_type == EndSelectionTrimItem) {
840 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
844 Editing::MouseMode eff = effective_mouse_mode ();
846 /* special case: allow drag of region fade in/out in object mode with join object/range enabled */
847 if (item_type == FadeInHandleItem || item_type == FadeOutHandleItem) {
851 /* there is no Range mode when in internal edit mode */
852 if (eff == MouseRange && internal_editing()) {
859 case StartSelectionTrimItem:
860 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
863 case EndSelectionTrimItem:
864 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
868 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
869 start_selection_grab (item, event);
871 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
872 /* grab selection for moving */
873 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionMove), event);
875 double const y = event->button.y;
876 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
878 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
879 if ( get_smart_mode() && atv) {
880 /* smart "join" mode: drag automation */
881 _drags->set (new AutomationRangeDrag (this, atv, selection->time), event, _cursors->up_down);
883 /* this was debated, but decided the more common action was to
884 make a new selection */
885 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
892 if (internal_editing()) {
893 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
894 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
898 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::RangeSelectModifier)) {
899 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionExtend), event);
901 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
907 case RegionViewNameHighlight:
908 if (!clicked_regionview->region()->locked()) {
909 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
915 if (!internal_editing()) {
916 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::RangeSelectModifier)) {
917 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionExtend), event);
919 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
929 /* Existing note: allow trimming/motion */
930 if (internal_editing()) {
931 /* trim notes if we're in internal edit mode and near the ends of the note */
932 NoteBase* cn = reinterpret_cast<NoteBase*>(item->get_data ("notebase"));
934 if (cn->big_enough_to_trim() && cn->mouse_near_ends()) {
935 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
937 _drags->set (new NoteDrag (this, item), event);
943 if (internal_editing()) {
944 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
945 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
959 /* Existing note: allow trimming/motion */
960 if (internal_editing()) {
961 NoteBase* cn = reinterpret_cast<NoteBase*> (item->get_data ("notebase"));
963 if (cn->big_enough_to_trim() && cn->mouse_near_ends()) {
964 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
966 _drags->set (new NoteDrag (this, item), event);
976 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)) &&
977 event->type == GDK_BUTTON_PRESS) {
979 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
981 } else if (event->type == GDK_BUTTON_PRESS) {
984 case FadeInHandleItem:
986 _drags->set (new FadeInDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), selection->regions), event, _cursors->fade_in);
990 case FadeOutHandleItem:
992 _drags->set (new FadeOutDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), selection->regions), event, _cursors->fade_out);
996 case StartCrossFadeItem:
997 case EndCrossFadeItem:
998 /* we might allow user to grab inside the fade to trim a region with preserve_fade_anchor. for not this is not fully implemented */
999 // if (!clicked_regionview->region()->locked()) {
1000 // _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer(), true), event);
1005 case FeatureLineItem:
1007 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier)) {
1008 remove_transient(item);
1012 _drags->set (new FeatureLineDrag (this, item), event);
1018 if (dynamic_cast<AutomationRegionView*> (clicked_regionview)) {
1019 /* click on an automation region view; do nothing here and let the ARV's signal handler
1025 if (internal_editing ()) {
1029 /* click on a normal region view */
1030 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
1031 add_region_copy_drag (item, event, clicked_regionview);
1032 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
1033 add_region_brush_drag (item, event, clicked_regionview);
1035 add_region_drag (item, event, clicked_regionview);
1039 // if (!internal_editing() && (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE && !selection->regions.empty())) {
1040 // _drags->add (new SelectionDrag (this, clicked_axisview->get_selection_rect (clicked_selection)->rect, SelectionDrag::SelectionMove));
1043 _drags->start_grab (event);
1047 case RegionViewNameHighlight:
1048 case LeftFrameHandle:
1049 case RightFrameHandle:
1050 if (!clicked_regionview->region()->locked()) {
1051 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1056 case RegionViewName:
1058 /* rename happens on edit clicks */
1059 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer()), event);
1064 case ControlPointItem:
1065 _drags->set (new ControlPointDrag (this, item), event);
1069 case AutomationLineItem:
1070 _drags->set (new LineDrag (this, item), event);
1075 if (internal_editing()) {
1076 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
1077 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
1081 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
1085 case AutomationTrackItem:
1087 TimeAxisView* parent = clicked_axisview->get_parent ();
1088 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (clicked_axisview);
1090 if (parent && dynamic_cast<MidiTimeAxisView*> (parent) && atv->show_regions ()) {
1092 RouteTimeAxisView* p = dynamic_cast<RouteTimeAxisView*> (parent);
1094 boost::shared_ptr<Playlist> pl = p->track()->playlist ();
1095 if (pl->n_regions() == 0) {
1096 /* Parent has no regions; create one so that we have somewhere to put automation */
1097 _drags->set (new RegionCreateDrag (this, item, parent), event);
1099 /* See if there's a region before the click that we can extend, and extend it if so */
1100 framepos_t const t = canvas_event_sample (event);
1101 boost::shared_ptr<Region> prev = pl->find_next_region (t, End, -1);
1103 _drags->set (new RegionCreateDrag (this, item, parent), event);
1105 prev->set_length (t - prev->position ());
1109 /* rubberband drag to select automation points */
1110 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
1117 if ( get_smart_mode() ) {
1118 /* we're in "smart" joined mode, and we've clicked on a Selection */
1119 double const y = event->button.y;
1120 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
1122 /* if we're over an automation track, start a drag of its data */
1123 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
1125 _drags->set (new AutomationRangeDrag (this, atv, selection->time), event, _cursors->up_down);
1128 /* if we're over a track and a region, and in the `object' part of a region,
1129 put a selection around the region and drag both
1131 /* RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1132 if (rtv && _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
1133 boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track> (rtv->route ());
1135 boost::shared_ptr<Playlist> pl = t->playlist ();
1138 boost::shared_ptr<Region> r = pl->top_region_at (canvas_event_sample (event));
1140 RegionView* rv = rtv->view()->find_view (r);
1141 clicked_selection = select_range (rv->region()->position(),
1142 rv->region()->last_frame()+1);
1143 _drags->add (new SelectionDrag (this, item, SelectionDrag::SelectionMove));
1144 list<RegionView*> rvs;
1146 _drags->add (new RegionMoveDrag (this, item, rv, rvs, false, false));
1147 _drags->start_grab (event);
1171 switch (item_type) {
1173 _drags->set (new LineDrag (this, item), event);
1176 case ControlPointItem:
1177 _drags->set (new ControlPointDrag (this, item), event);
1183 AudioRegionView* arv = dynamic_cast<AudioRegionView *> (clicked_regionview);
1185 _drags->set (new AutomationRangeDrag (this, arv, selection->time), event, _cursors->up_down);
1186 _drags->start_grab (event);
1192 case AutomationLineItem:
1193 _drags->set (new LineDrag (this, item), event);
1203 if (event->type == GDK_BUTTON_PRESS) {
1204 _drags->set (new MouseZoomDrag (this, item), event);
1211 if (internal_editing() && item_type == NoteItem ) {
1212 /* drag notes if we're in internal edit mode */
1213 NoteBase* cn = reinterpret_cast<NoteBase*>(item->get_data ("notebase"));
1215 if (cn->big_enough_to_trim()) {
1216 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
1219 } else if (clicked_regionview) {
1221 _drags->set (new TimeFXDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1227 _drags->set (new ScrubDrag (this, item), event);
1228 scrub_reversals = 0;
1229 scrub_reverse_distance = 0;
1230 last_scrub_x = event->button.x;
1231 scrubbing_direction = 0;
1232 set_canvas_cursor (_cursors->transparent);
1244 Editor::button_press_handler_2 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1246 Editing::MouseMode const eff = effective_mouse_mode ();
1249 switch (item_type) {
1251 if (internal_editing ()) {
1252 /* no region drags in internal edit mode */
1256 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
1257 add_region_copy_drag (item, event, clicked_regionview);
1259 add_region_drag (item, event, clicked_regionview);
1261 _drags->start_grab (event);
1264 case ControlPointItem:
1265 _drags->set (new ControlPointDrag (this, item), event);
1273 switch (item_type) {
1274 case RegionViewNameHighlight:
1275 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1279 case LeftFrameHandle:
1280 case RightFrameHandle:
1281 if (!internal_editing ()) {
1282 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1287 case RegionViewName:
1288 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer()), event);
1302 /* relax till release */
1308 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1309 temporal_zoom_to_frame (false, canvas_event_sample (event));
1311 temporal_zoom_to_frame (true, canvas_event_sample(event));
1324 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1326 if (event->type == GDK_2BUTTON_PRESS) {
1327 _drags->mark_double_click ();
1328 gdk_pointer_ungrab (GDK_CURRENT_TIME);
1332 if (event->type != GDK_BUTTON_PRESS) {
1336 Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->_track_canvas_viewport->get_window();
1338 if (canvas_window) {
1339 Glib::RefPtr<const Gdk::Window> pointer_window;
1342 Gdk::ModifierType mask;
1344 pointer_window = canvas_window->get_pointer (x, y, mask);
1346 if (pointer_window == _track_canvas->get_window()) {
1347 _track_canvas->window_to_canvas (x, y, wx, wy);
1351 pre_press_cursor = current_canvas_cursor;
1353 _track_canvas->grab_focus();
1355 if (_session && _session->actively_recording()) {
1359 if (internal_editing()) {
1360 bool leave_internal_edit_mode = false;
1362 switch (item_type) {
1367 if (!dynamic_cast<MidiRegionView*> (clicked_regionview) && !dynamic_cast<AutomationRegionView*> (clicked_regionview)) {
1368 leave_internal_edit_mode = true;
1372 case PlayheadCursorItem:
1374 case TempoMarkerItem:
1375 case MeterMarkerItem:
1379 case RangeMarkerBarItem:
1380 case CdMarkerBarItem:
1381 case TransportMarkerBarItem:
1383 /* button press on these events never does anything to
1384 change the editing mode.
1392 if (leave_internal_edit_mode) {
1393 ActionManager::do_action ("MouseMode", "toggle-internal-edit");
1397 button_selection (item, event, item_type);
1399 if (!_drags->active () &&
1400 (Keyboard::is_delete_event (&event->button) ||
1401 Keyboard::is_context_menu_event (&event->button) ||
1402 Keyboard::is_edit_event (&event->button))) {
1404 /* handled by button release */
1408 //not rolling, range mode click + join_play_range : locate the PH here
1409 if ( !_drags->active () && !_session->transport_rolling() && ( effective_mouse_mode() == MouseRange ) && Config->get_always_play_range() ) {
1410 framepos_t where = canvas_event_sample (event);
1412 _session->request_locate (where, false);
1415 switch (event->button.button) {
1417 return button_press_handler_1 (item, event, item_type);
1421 return button_press_handler_2 (item, event, item_type);
1428 return button_press_dispatch (&event->button);
1437 Editor::button_press_dispatch (GdkEventButton* ev)
1439 /* this function is intended only for buttons 4 and above.
1442 Gtkmm2ext::MouseButton b (ev->state, ev->button);
1443 return button_bindings->activate (b, Gtkmm2ext::Bindings::Press);
1447 Editor::button_release_dispatch (GdkEventButton* ev)
1449 /* this function is intended only for buttons 4 and above.
1452 Gtkmm2ext::MouseButton b (ev->state, ev->button);
1453 return button_bindings->activate (b, Gtkmm2ext::Bindings::Release);
1457 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1459 framepos_t where = canvas_event_sample (event);
1460 AutomationTimeAxisView* atv = 0;
1462 if (pre_press_cursor) {
1463 set_canvas_cursor (pre_press_cursor);
1464 pre_press_cursor = 0;
1467 /* no action if we're recording */
1469 if (_session && _session->actively_recording()) {
1473 /* see if we're finishing a drag */
1475 bool were_dragging = false;
1476 if (_drags->active ()) {
1477 bool const r = _drags->end_grab (event);
1479 /* grab dragged, so do nothing else */
1483 were_dragging = true;
1486 update_region_layering_order_editor ();
1488 /* edit events get handled here */
1490 if (!_drags->active () && Keyboard::is_edit_event (&event->button)) {
1491 switch (item_type) {
1493 show_region_properties ();
1496 case TempoMarkerItem: {
1498 TempoMarker* tempo_marker;
1500 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
1501 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
1505 if ((tempo_marker = dynamic_cast<TempoMarker*> (marker)) == 0) {
1506 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
1510 edit_tempo_marker (*tempo_marker);
1514 case MeterMarkerItem: {
1516 MeterMarker* meter_marker;
1518 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
1519 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
1523 if ((meter_marker = dynamic_cast<MeterMarker*> (marker)) == 0) {
1524 fatal << _("programming error: marker for meter is not a meter marker!") << endmsg;
1527 edit_meter_marker (*meter_marker);
1531 case RegionViewName:
1532 if (clicked_regionview->name_active()) {
1533 return mouse_rename_region (item, event);
1537 case ControlPointItem:
1538 edit_control_point (item);
1547 /* context menu events get handled here */
1548 if (Keyboard::is_context_menu_event (&event->button)) {
1550 context_click_event = *event;
1552 if (!_drags->active ()) {
1554 /* no matter which button pops up the context menu, tell the menu
1555 widget to use button 1 to drive menu selection.
1558 switch (item_type) {
1560 case FadeInHandleItem:
1562 case FadeOutHandleItem:
1563 popup_fade_context_menu (1, event->button.time, item, item_type);
1566 case StartCrossFadeItem:
1567 popup_xfade_in_context_menu (1, event->button.time, item, item_type);
1570 case EndCrossFadeItem:
1571 popup_xfade_out_context_menu (1, event->button.time, item, item_type);
1575 popup_track_context_menu (1, event->button.time, item_type, false);
1579 case RegionViewNameHighlight:
1580 case LeftFrameHandle:
1581 case RightFrameHandle:
1582 case RegionViewName:
1583 popup_track_context_menu (1, event->button.time, item_type, false);
1587 popup_track_context_menu (1, event->button.time, item_type, true);
1590 case AutomationTrackItem:
1591 popup_track_context_menu (1, event->button.time, item_type, false);
1595 case RangeMarkerBarItem:
1596 case TransportMarkerBarItem:
1597 case CdMarkerBarItem:
1601 popup_ruler_menu (where, item_type);
1605 marker_context_menu (&event->button, item);
1608 case TempoMarkerItem:
1609 tempo_or_meter_marker_context_menu (&event->button, item);
1612 case MeterMarkerItem:
1613 tempo_or_meter_marker_context_menu (&event->button, item);
1616 case CrossfadeViewItem:
1617 popup_track_context_menu (1, event->button.time, item_type, false);
1620 case ControlPointItem:
1621 popup_control_point_context_menu (item, event);
1632 /* delete events get handled here */
1634 Editing::MouseMode const eff = effective_mouse_mode ();
1636 if (!_drags->active () && Keyboard::is_delete_event (&event->button)) {
1638 switch (item_type) {
1639 case TempoMarkerItem:
1640 remove_tempo_marker (item);
1643 case MeterMarkerItem:
1644 remove_meter_marker (item);
1648 remove_marker (*item, event);
1652 if (eff == MouseObject) {
1653 remove_clicked_region ();
1657 case ControlPointItem:
1658 remove_control_point (item);
1662 remove_midi_note (item, event);
1671 switch (event->button.button) {
1674 switch (item_type) {
1675 /* see comments in button_press_handler */
1676 case PlayheadCursorItem:
1679 case AutomationLineItem:
1680 case StartSelectionTrimItem:
1681 case EndSelectionTrimItem:
1685 if (!_dragging_playhead) {
1686 snap_to_with_modifier (where, event, 0, true);
1687 mouse_add_new_marker (where);
1691 case CdMarkerBarItem:
1692 if (!_dragging_playhead) {
1693 // if we get here then a dragged range wasn't done
1694 snap_to_with_modifier (where, event, 0, true);
1695 mouse_add_new_marker (where, true);
1700 if (!_dragging_playhead) {
1701 snap_to_with_modifier (where, event);
1702 mouse_add_new_tempo_event (where);
1707 if (!_dragging_playhead) {
1708 mouse_add_new_meter_event (pixel_to_sample (event->button.x));
1719 switch (item_type) {
1720 case AutomationTrackItem:
1721 atv = dynamic_cast<AutomationTimeAxisView*>(clicked_axisview);
1723 bool with_guard_points = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
1724 atv->add_automation_event (event, where, event->button.y, with_guard_points);
1734 switch (item_type) {
1737 /* check that we didn't drag before releasing, since
1738 its really annoying to create new control
1739 points when doing this.
1741 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (clicked_regionview);
1742 if (!were_dragging && arv) {
1743 bool with_guard_points = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
1744 arv->add_gain_point_event (item, event, with_guard_points);
1750 case AutomationTrackItem: {
1751 bool with_guard_points = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
1752 dynamic_cast<AutomationTimeAxisView*>(clicked_axisview)->
1753 add_automation_event (event, where, event->button.y, with_guard_points);
1763 set_canvas_cursor (current_canvas_cursor);
1764 if (scrubbing_direction == 0) {
1765 /* no drag, just a click */
1766 switch (item_type) {
1768 play_selected_region ();
1774 /* make sure we stop */
1775 _session->request_transport_speed (0.0);
1784 /* do any (de)selection operations that should occur on button release */
1785 button_selection (item, event, item_type);
1794 switch (item_type) {
1796 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1798 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier|Keyboard::SecondaryModifier))) {
1801 // Button2 click is unused
1816 // x_style_paste (where, 1.0);
1837 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1844 switch (item_type) {
1845 case ControlPointItem:
1846 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1847 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1852 at_y = cp->get_y ();
1853 cp->i2w (at_x, at_y);
1857 fraction = 1.0 - (cp->get_y() / cp->line().height());
1859 if (is_drawable() && !_drags->active ()) {
1860 set_canvas_cursor (_cursors->fader);
1863 _verbose_cursor->set (cp->line().get_verbose_cursor_string (fraction), at_x, at_y);
1864 _verbose_cursor->show ();
1869 if (mouse_mode == MouseGain) {
1870 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1872 line->set_outline_color (ARDOUR_UI::config()->get_canvasvar_EnteredGainLine());
1874 if (is_drawable()) {
1875 set_canvas_cursor (_cursors->fader);
1880 case AutomationLineItem:
1881 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1882 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1884 line->set_outline_color (ARDOUR_UI::config()->get_canvasvar_EnteredAutomationLine());
1886 if (is_drawable()) {
1887 set_canvas_cursor (_cursors->fader);
1892 case RegionViewNameHighlight:
1893 if (is_drawable() && effective_mouse_mode() == MouseObject && entered_regionview) {
1894 set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1895 _over_region_trim_target = true;
1899 case LeftFrameHandle:
1900 case RightFrameHandle:
1901 if (is_drawable() && effective_mouse_mode() == MouseObject && !internal_editing() && entered_regionview) {
1902 set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1907 switch (effective_mouse_mode()) {
1909 set_canvas_cursor (_cursors->selector);
1912 set_canvas_cursor (which_grabber_cursor());
1917 case StartSelectionTrimItem:
1918 if (is_drawable()) {
1919 set_canvas_cursor (_cursors->left_side_trim);
1922 case EndSelectionTrimItem:
1923 if (is_drawable()) {
1924 set_canvas_cursor (_cursors->right_side_trim);
1928 case PlayheadCursorItem:
1929 if (is_drawable()) {
1930 switch (_edit_point) {
1932 set_canvas_cursor (_cursors->grabber_edit_point);
1935 set_canvas_cursor (_cursors->grabber);
1942 case RegionViewName:
1944 /* when the name is not an active item, the entire name highlight is for trimming */
1946 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1947 if (mouse_mode == MouseObject && is_drawable()) {
1948 set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1949 _over_region_trim_target = true;
1955 case AutomationTrackItem:
1956 if (is_drawable()) {
1957 Gdk::Cursor *cursor;
1958 switch (mouse_mode) {
1960 cursor = _cursors->selector;
1963 cursor = _cursors->zoom_in;
1966 cursor = _cursors->cross_hair;
1970 set_canvas_cursor (cursor);
1972 AutomationTimeAxisView* atv;
1973 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1974 clear_entered_track = false;
1975 set_entered_track (atv);
1981 case RangeMarkerBarItem:
1982 case TransportMarkerBarItem:
1983 case CdMarkerBarItem:
1986 if (is_drawable()) {
1987 set_canvas_cursor (_cursors->timebar);
1992 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1995 entered_marker = marker;
1996 marker->set_color_rgba (ARDOUR_UI::config()->get_canvasvar_EnteredMarker());
1998 case MeterMarkerItem:
1999 case TempoMarkerItem:
2000 if (is_drawable()) {
2001 set_canvas_cursor (_cursors->timebar);
2005 case FadeInHandleItem:
2006 if (mouse_mode == MouseObject && !internal_editing()) {
2007 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
2009 RegionView* rv = static_cast<RegionView*>(item->get_data ("regionview"));
2010 rect->set_fill_color (rv->get_fill_color());
2011 set_canvas_cursor (_cursors->fade_in);
2016 case FadeOutHandleItem:
2017 if (mouse_mode == MouseObject && !internal_editing()) {
2018 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
2020 RegionView* rv = static_cast<RegionView*>(item->get_data ("regionview"));
2021 rect->set_fill_color (rv->get_fill_color ());
2022 set_canvas_cursor (_cursors->fade_out);
2026 case FeatureLineItem:
2028 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
2029 line->set_outline_color (0xFF0000FF);
2034 if ( get_smart_mode() ) {
2035 set_canvas_cursor ();
2043 /* second pass to handle entered track status in a comprehensible way.
2046 switch (item_type) {
2048 case AutomationLineItem:
2049 case ControlPointItem:
2050 /* these do not affect the current entered track state */
2051 clear_entered_track = false;
2054 case AutomationTrackItem:
2055 /* handled above already */
2059 set_entered_track (0);
2067 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent*, ItemType item_type)
2076 switch (item_type) {
2077 case ControlPointItem:
2078 if (is_drawable()) {
2079 set_canvas_cursor (current_canvas_cursor);
2082 _verbose_cursor->hide ();
2085 case RegionViewNameHighlight:
2086 case LeftFrameHandle:
2087 case RightFrameHandle:
2088 case StartSelectionTrimItem:
2089 case EndSelectionTrimItem:
2090 case PlayheadCursorItem:
2092 _over_region_trim_target = false;
2094 if (is_drawable()) {
2095 set_canvas_cursor (current_canvas_cursor);
2100 case AutomationLineItem:
2101 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
2103 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
2105 line->set_outline_color (al->get_line_color());
2108 if (is_drawable()) {
2109 set_canvas_cursor (current_canvas_cursor);
2113 case RegionViewName:
2114 /* see enter_handler() for notes */
2115 _over_region_trim_target = false;
2117 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
2118 if (is_drawable() && mouse_mode == MouseObject) {
2119 set_canvas_cursor (current_canvas_cursor);
2124 case RangeMarkerBarItem:
2125 case TransportMarkerBarItem:
2126 case CdMarkerBarItem:
2130 if (is_drawable()) {
2131 set_canvas_cursor (current_canvas_cursor);
2136 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2140 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
2141 location_flags_changed (loc, this);
2144 case MeterMarkerItem:
2145 case TempoMarkerItem:
2147 if (is_drawable()) {
2148 set_canvas_cursor (current_canvas_cursor);
2153 case FadeInHandleItem:
2154 case FadeOutHandleItem:
2155 rv = static_cast<RegionView*>(item->get_data ("regionview"));
2157 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
2159 rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_InactiveFadeHandle());
2162 set_canvas_cursor (current_canvas_cursor);
2165 case AutomationTrackItem:
2166 if (is_drawable()) {
2167 set_canvas_cursor (current_canvas_cursor);
2168 clear_entered_track = true;
2169 Glib::signal_idle().connect (sigc::mem_fun(*this, &Editor::left_automation_track));
2172 case FeatureLineItem:
2174 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
2175 line->set_outline_color (ARDOUR_UI::config()->get_canvasvar_ZeroLine());
2187 Editor::left_automation_track ()
2189 if (clear_entered_track) {
2190 set_entered_track (0);
2191 clear_entered_track = false;
2197 Editor::scrub (framepos_t frame, double current_x)
2201 if (scrubbing_direction == 0) {
2203 _session->request_locate (frame, false);
2204 _session->request_transport_speed (0.1);
2205 scrubbing_direction = 1;
2209 if (last_scrub_x > current_x) {
2211 /* pointer moved to the left */
2213 if (scrubbing_direction > 0) {
2215 /* we reversed direction to go backwards */
2218 scrub_reverse_distance += (int) (last_scrub_x - current_x);
2222 /* still moving to the left (backwards) */
2224 scrub_reversals = 0;
2225 scrub_reverse_distance = 0;
2227 delta = 0.01 * (last_scrub_x - current_x);
2228 _session->request_transport_speed_nonzero (_session->transport_speed() - delta);
2232 /* pointer moved to the right */
2234 if (scrubbing_direction < 0) {
2235 /* we reversed direction to go forward */
2238 scrub_reverse_distance += (int) (current_x - last_scrub_x);
2241 /* still moving to the right */
2243 scrub_reversals = 0;
2244 scrub_reverse_distance = 0;
2246 delta = 0.01 * (current_x - last_scrub_x);
2247 _session->request_transport_speed_nonzero (_session->transport_speed() + delta);
2251 /* if there have been more than 2 opposite motion moves detected, or one that moves
2252 back more than 10 pixels, reverse direction
2255 if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
2257 if (scrubbing_direction > 0) {
2258 /* was forwards, go backwards */
2259 _session->request_transport_speed (-0.1);
2260 scrubbing_direction = -1;
2262 /* was backwards, go forwards */
2263 _session->request_transport_speed (0.1);
2264 scrubbing_direction = 1;
2267 scrub_reverse_distance = 0;
2268 scrub_reversals = 0;
2272 last_scrub_x = current_x;
2276 Editor::motion_handler (ArdourCanvas::Item* /*item*/, GdkEvent* event, bool from_autoscroll)
2278 _last_motion_y = event->motion.y;
2280 if (event->motion.is_hint) {
2283 /* We call this so that MOTION_NOTIFY events continue to be
2284 delivered to the canvas. We need to do this because we set
2285 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
2286 the density of the events, at the expense of a round-trip
2287 to the server. Given that this will mostly occur on cases
2288 where DISPLAY = :0.0, and given the cost of what the motion
2289 event might do, its a good tradeoff.
2292 _track_canvas->get_pointer (x, y);
2295 if (current_stepping_trackview) {
2296 /* don't keep the persistent stepped trackview if the mouse moves */
2297 current_stepping_trackview = 0;
2298 step_timeout.disconnect ();
2301 if (_session && _session->actively_recording()) {
2302 /* Sorry. no dragging stuff around while we record */
2306 JoinObjectRangeState const old = _join_object_range_state;
2307 update_join_object_range_location (event->motion.x, event->motion.y);
2309 if (!_internal_editing && _join_object_range_state != old) {
2310 set_canvas_cursor ();
2313 if (!_internal_editing && _over_region_trim_target) {
2314 set_canvas_cursor_for_region_view (event->motion.x, entered_regionview);
2317 bool handled = false;
2318 if (_drags->active ()) {
2319 handled = _drags->motion_handler (event, from_autoscroll);
2326 track_canvas_motion (event);
2331 Editor::can_remove_control_point (ArdourCanvas::Item* item)
2333 ControlPoint* control_point;
2335 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2336 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2340 AutomationLine& line = control_point->line ();
2341 if (dynamic_cast<AudioRegionGainLine*> (&line)) {
2342 /* we shouldn't remove the first or last gain point in region gain lines */
2343 if (line.is_last_point(*control_point) || line.is_first_point(*control_point)) {
2352 Editor::remove_control_point (ArdourCanvas::Item* item)
2354 if (!can_remove_control_point (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 control_point->line().remove_point (*control_point);
2369 Editor::edit_control_point (ArdourCanvas::Item* item)
2371 ControlPoint* p = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"));
2374 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2378 ControlPointDialog d (p);
2381 if (d.run () != RESPONSE_ACCEPT) {
2385 p->line().modify_point_y (*p, d.get_y_fraction ());
2389 Editor::edit_notes (TimeAxisViewItem& tavi)
2391 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(&tavi);
2397 MidiRegionView::Selection const & s = mrv->selection();
2403 EditNoteDialog* d = new EditNoteDialog (&(*s.begin())->region_view(), s);
2407 d->signal_response().connect (sigc::bind (sigc::mem_fun (*this, &Editor::note_edit_done), d));
2411 Editor::note_edit_done (int r, EditNoteDialog* d)
2418 Editor::visible_order_range (int* low, int* high) const
2420 *low = TimeAxisView::max_order ();
2423 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
2425 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
2427 if (!rtv->hidden()) {
2429 if (*high < rtv->order()) {
2430 *high = rtv->order ();
2433 if (*low > rtv->order()) {
2434 *low = rtv->order ();
2441 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
2443 /* Either add to or set the set the region selection, unless
2444 this is an alignment click (control used)
2447 if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
2448 TimeAxisView* tv = &rv.get_time_axis_view();
2449 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
2451 if (rtv && rtv->is_track()) {
2452 speed = rtv->track()->speed();
2455 framepos_t where = get_preferred_edit_position();
2459 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
2461 align_region (rv.region(), SyncPoint, (framepos_t) (where * speed));
2463 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
2465 align_region (rv.region(), End, (framepos_t) (where * speed));
2469 align_region (rv.region(), Start, (framepos_t) (where * speed));
2476 Editor::collect_new_region_view (RegionView* rv)
2478 latest_regionviews.push_back (rv);
2482 Editor::collect_and_select_new_region_view (RegionView* rv)
2485 latest_regionviews.push_back (rv);
2489 Editor::cancel_selection ()
2491 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2492 (*i)->hide_selection ();
2495 selection->clear ();
2496 clicked_selection = 0;
2500 Editor::cancel_time_selection ()
2502 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2503 (*i)->hide_selection ();
2505 selection->time.clear ();
2506 clicked_selection = 0;
2510 Editor::point_trim (GdkEvent* event, framepos_t new_bound)
2512 RegionView* rv = clicked_regionview;
2514 /* Choose action dependant on which button was pressed */
2515 switch (event->button.button) {
2517 begin_reversible_command (_("start point trim"));
2519 if (selection->selected (rv)) {
2520 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
2521 i != selection->regions.by_layer().end(); ++i)
2523 if (!(*i)->region()->locked()) {
2524 (*i)->region()->clear_changes ();
2525 (*i)->region()->trim_front (new_bound);
2526 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2531 if (!rv->region()->locked()) {
2532 rv->region()->clear_changes ();
2533 rv->region()->trim_front (new_bound);
2534 _session->add_command(new StatefulDiffCommand (rv->region()));
2538 commit_reversible_command();
2542 begin_reversible_command (_("End point trim"));
2544 if (selection->selected (rv)) {
2546 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
2548 if (!(*i)->region()->locked()) {
2549 (*i)->region()->clear_changes();
2550 (*i)->region()->trim_end (new_bound);
2551 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2557 if (!rv->region()->locked()) {
2558 rv->region()->clear_changes ();
2559 rv->region()->trim_end (new_bound);
2560 _session->add_command (new StatefulDiffCommand (rv->region()));
2564 commit_reversible_command();
2573 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* /*event*/)
2578 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2579 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2583 Location* location = find_location_from_marker (marker, is_start);
2584 location->set_hidden (true, this);
2589 Editor::reposition_zoom_rect (framepos_t start, framepos_t end)
2591 double x1 = sample_to_pixel (start);
2592 double x2 = sample_to_pixel (end);
2593 double y2 = _full_canvas_height - 1.0;
2595 zoom_rect->set (ArdourCanvas::Rect (x1, 1.0, x2, y2));
2600 Editor::mouse_rename_region (ArdourCanvas::Item* /*item*/, GdkEvent* /*event*/)
2602 using namespace Gtkmm2ext;
2604 ArdourPrompter prompter (false);
2606 prompter.set_prompt (_("Name for region:"));
2607 prompter.set_initial_text (clicked_regionview->region()->name());
2608 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
2609 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
2610 prompter.show_all ();
2611 switch (prompter.run ()) {
2612 case Gtk::RESPONSE_ACCEPT:
2614 prompter.get_result(str);
2616 clicked_regionview->region()->set_name (str);
2625 Editor::mouse_brush_insert_region (RegionView* rv, framepos_t pos)
2627 /* no brushing without a useful snap setting */
2629 switch (_snap_mode) {
2631 return; /* can't work because it allows region to be placed anywhere */
2636 switch (_snap_type) {
2644 /* don't brush a copy over the original */
2646 if (pos == rv->region()->position()) {
2650 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
2652 if (rtv == 0 || !rtv->is_track()) {
2656 boost::shared_ptr<Playlist> playlist = rtv->playlist();
2657 double speed = rtv->track()->speed();
2659 playlist->clear_changes ();
2660 boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region(), true));
2661 playlist->add_region (new_region, (framepos_t) (pos * speed));
2662 _session->add_command (new StatefulDiffCommand (playlist));
2664 // playlist is frozen, so we have to update manually XXX this is disgusting
2666 playlist->RegionAdded (new_region); /* EMIT SIGNAL */
2670 Editor::track_height_step_timeout ()
2672 if (get_microseconds() - last_track_height_step_timestamp < 250000) {
2673 current_stepping_trackview = 0;
2680 Editor::add_region_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2682 assert (region_view);
2684 if (!region_view->region()->playlist()) {
2688 _region_motion_group->raise_to_top ();
2690 if (Config->get_edit_mode() == Splice) {
2691 _drags->add (new RegionSpliceDrag (this, item, region_view, selection->regions.by_layer()));
2693 _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), false, false));
2698 Editor::add_region_copy_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2700 assert (region_view);
2702 if (!region_view->region()->playlist()) {
2706 _region_motion_group->raise_to_top ();
2708 _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), false, true));
2712 Editor::add_region_brush_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2714 assert (region_view);
2716 if (!region_view->region()->playlist()) {
2720 if (Config->get_edit_mode() == Splice) {
2724 _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), true, false));
2726 begin_reversible_command (Operations::drag_region_brush);
2729 /** Start a grab where a time range is selected, track(s) are selected, and the
2730 * user clicks and drags a region with a modifier in order to create a new region containing
2731 * the section of the clicked region that lies within the time range.
2734 Editor::start_selection_grab (ArdourCanvas::Item* /*item*/, GdkEvent* event)
2736 if (clicked_regionview == 0) {
2740 /* lets try to create new Region for the selection */
2742 vector<boost::shared_ptr<Region> > new_regions;
2743 create_region_from_selection (new_regions);
2745 if (new_regions.empty()) {
2749 /* XXX fix me one day to use all new regions */
2751 boost::shared_ptr<Region> region (new_regions.front());
2753 /* add it to the current stream/playlist.
2755 tricky: the streamview for the track will add a new regionview. we will
2756 catch the signal it sends when it creates the regionview to
2757 set the regionview we want to then drag.
2760 latest_regionviews.clear();
2761 sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
2763 /* A selection grab currently creates two undo/redo operations, one for
2764 creating the new region and another for moving it.
2767 begin_reversible_command (Operations::selection_grab);
2769 boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
2771 playlist->clear_changes ();
2772 clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
2773 _session->add_command(new StatefulDiffCommand (playlist));
2775 commit_reversible_command ();
2779 if (latest_regionviews.empty()) {
2780 /* something went wrong */
2784 /* we need to deselect all other regionviews, and select this one
2785 i'm ignoring undo stuff, because the region creation will take care of it
2787 selection->set (latest_regionviews);
2789 _drags->set (new RegionMoveDrag (this, latest_regionviews.front()->get_canvas_group(), latest_regionviews.front(), latest_regionviews, false, false), event);
2795 if (_drags->active ()) {
2798 selection->clear ();
2803 Editor::set_internal_edit (bool yn)
2805 if (_internal_editing == yn) {
2809 _internal_editing = yn;
2812 pre_internal_mouse_mode = mouse_mode;
2813 pre_internal_snap_type = _snap_type;
2814 pre_internal_snap_mode = _snap_mode;
2816 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2817 (*i)->enter_internal_edit_mode ();
2820 set_snap_to (internal_snap_type);
2821 set_snap_mode (internal_snap_mode);
2825 internal_snap_mode = _snap_mode;
2826 internal_snap_type = _snap_type;
2828 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2829 (*i)->leave_internal_edit_mode ();
2832 if (mouse_mode == MouseDraw && pre_internal_mouse_mode != MouseDraw) {
2833 /* we were drawing .. flip back to something sensible */
2834 set_mouse_mode (pre_internal_mouse_mode);
2837 set_snap_to (pre_internal_snap_type);
2838 set_snap_mode (pre_internal_snap_mode);
2841 set_canvas_cursor ();
2844 /** Update _join_object_range_state which indicate whether we are over the top or bottom half of a region view,
2845 * used by the `join object/range' tool mode.
2848 Editor::update_join_object_range_location (double /*x*/, double y)
2850 /* XXX: actually, this decides based on whether the mouse is in the top
2851 or bottom half of a the waveform part RouteTimeAxisView;
2853 Note that entered_{track,regionview} is not always setup (e.g. if
2854 the mouse is over a TimeSelection), and to get a Region
2855 that we're over requires searching the playlist.
2858 if ( !get_smart_mode() ) {
2859 _join_object_range_state = JOIN_OBJECT_RANGE_NONE;
2863 if (mouse_mode == MouseObject) {
2864 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2865 } else if (mouse_mode == MouseRange) {
2866 _join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
2869 /* XXX: maybe we should make entered_track work in all cases, rather than resorting to this */
2870 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
2874 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
2879 rtv->canvas_display()->canvas_to_item (cx, cy);
2881 double const c = cy / (rtv->view()->child_height() - TimeAxisViewItem::NAME_HIGHLIGHT_SIZE);
2883 _join_object_range_state = c <= 0.5 ? JOIN_OBJECT_RANGE_RANGE : JOIN_OBJECT_RANGE_OBJECT;
2889 Editor::effective_mouse_mode () const
2891 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
2893 } else if (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE) {
2901 Editor::remove_midi_note (ArdourCanvas::Item* item, GdkEvent *)
2903 NoteBase* e = reinterpret_cast<NoteBase*> (item->get_data ("notebase"));
2906 e->region_view().delete_note (e->note ());
2910 Editor::set_canvas_cursor_for_region_view (double x, RegionView* rv)
2912 /* XXX: this check should not be necessary */
2919 ArdourCanvas::Group* g = rv->get_canvas_group ();
2920 ArdourCanvas::Group* p = g->parent ();
2922 /* Compute x in region view parent coordinates */
2924 p->canvas_to_item (x, dy);
2926 boost::optional<ArdourCanvas::Rect> item_bbox = g->bounding_box ();
2928 ArdourCanvas::Rect parent_bbox = g->item_to_parent (item_bbox.get ());
2930 /* Halfway across the region */
2931 double const h = (parent_bbox.x0 + parent_bbox.x1) / 2;
2933 Trimmable::CanTrim ct = rv->region()->can_trim ();
2935 if (ct & Trimmable::FrontTrimEarlier) {
2936 set_canvas_cursor (_cursors->left_side_trim, true);
2938 set_canvas_cursor (_cursors->left_side_trim_right_only, true);
2941 if (ct & Trimmable::EndTrimLater) {
2942 set_canvas_cursor (_cursors->right_side_trim, true);
2944 set_canvas_cursor (_cursors->right_side_trim_left_only, true);
2949 /** Obtain the pointer position in canvas coordinates */
2951 Editor::get_pointer_position (double& x, double& y) const
2954 _track_canvas->get_pointer (px, py);
2955 _track_canvas->window_to_canvas (px, py, x, y);