2 Copyright (C) 2000-2001 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
29 #include "pbd/error.h"
30 #include "pbd/enumwriter.h"
31 #include "pbd/memento_command.h"
32 #include "pbd/basename.h"
33 #include "pbd/stateful_diff_command.h"
35 #include "gtkmm2ext/bindings.h"
36 #include "gtkmm2ext/utils.h"
37 #include "gtkmm2ext/tearoff.h"
39 #include "canvas/canvas.h"
41 #include "ardour/audioregion.h"
42 #include "ardour/operations.h"
43 #include "ardour/playlist.h"
44 #include "ardour/profile.h"
45 #include "ardour/region_factory.h"
46 #include "ardour/route.h"
47 #include "ardour/session.h"
48 #include "ardour/types.h"
50 #include "ardour_ui.h"
53 #include "time_axis_view.h"
54 #include "audio_time_axis.h"
55 #include "audio_region_view.h"
56 #include "midi_region_view.h"
58 #include "streamview.h"
59 #include "region_gain_line.h"
60 #include "automation_time_axis.h"
61 #include "control_point.h"
64 #include "selection.h"
67 #include "rgb_macros.h"
68 #include "control_point_dialog.h"
69 #include "editor_drag.h"
70 #include "automation_region_view.h"
71 #include "edit_note_dialog.h"
72 #include "mouse_cursors.h"
73 #include "editor_cursors.h"
74 #include "verbose_cursor.h"
80 using namespace ARDOUR;
83 using namespace Editing;
84 using Gtkmm2ext::Keyboard;
87 Editor::mouse_frame (framepos_t& where, bool& in_track_canvas) const
89 /* gdk_window_get_pointer() has X11's XQueryPointer semantics in that it only
90 pays attentions to subwindows. this means that menu windows are ignored, and
91 if the pointer is in a menu, the return window from the call will be the
92 the regular subwindow *under* the menu.
94 this matters quite a lot if the pointer is moving around in a menu that overlaps
95 the track canvas because we will believe that we are within the track canvas
96 when we are not. therefore, we track enter/leave events for the track canvas
97 and allow that to override the result of gdk_window_get_pointer().
100 if (!within_track_canvas) {
105 Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->_track_canvas->get_window();
107 if (!canvas_window) {
111 Glib::RefPtr<const Gdk::Window> pointer_window = Gdk::Display::get_default()->get_window_at_pointer (x, y);
113 if (!pointer_window) {
117 if (pointer_window != canvas_window && pointer_window != _time_bars_canvas->get_window()) {
118 in_track_canvas = false;
122 in_track_canvas = true;
125 event.type = GDK_BUTTON_RELEASE;
129 where = window_event_sample (&event, 0, 0);
135 Editor::window_event_sample (GdkEvent const * event, double* pcx, double* pcy) const
140 if (!gdk_event_get_coords (event, &x, &y)) {
144 /* event coordinates are in window units, so convert to canvas
145 * (i.e. account for scrolling)
148 ArdourCanvas::Duple d = _track_canvas->window_to_canvas (ArdourCanvas::Duple (x, y));
158 return pixel_to_sample (d.x);
162 Editor::canvas_event_sample (GdkEvent const * event, double* pcx, double* pcy) const
167 /* event coordinates are already in canvas units */
169 if (!gdk_event_get_coords (event, &x, &y)) {
170 cerr << "!NO c COORDS for event type " << event->type << endl;
182 /* note that pixel_to_sample() never returns less than zero, so even if the pixel
183 position is negative (as can be the case with motion events in particular),
184 the frame location is always positive.
187 return pixel_to_sample (x);
191 Editor::which_grabber_cursor ()
193 Gdk::Cursor* c = _cursors->grabber;
195 if (_internal_editing) {
196 switch (mouse_mode) {
198 c = _cursors->midi_pencil;
202 c = _cursors->grabber_note;
206 c = _cursors->midi_resize;
210 c = _cursors->grabber_note;
219 switch (_edit_point) {
221 c = _cursors->grabber_edit_point;
224 boost::shared_ptr<Movable> m = _movable.lock();
225 if (m && m->locked()) {
226 c = _cursors->speaker;
236 Editor::set_current_trimmable (boost::shared_ptr<Trimmable> t)
238 boost::shared_ptr<Trimmable> st = _trimmable.lock();
240 if (!st || st == t) {
242 set_canvas_cursor ();
247 Editor::set_current_movable (boost::shared_ptr<Movable> m)
249 boost::shared_ptr<Movable> sm = _movable.lock();
251 if (!sm || sm != m) {
253 set_canvas_cursor ();
258 Editor::set_canvas_cursor ()
260 switch (mouse_mode) {
262 current_canvas_cursor = _cursors->selector;
263 if (_internal_editing) {
264 current_canvas_cursor = which_grabber_cursor();
269 current_canvas_cursor = which_grabber_cursor();
273 current_canvas_cursor = _cursors->midi_pencil;
277 current_canvas_cursor = _cursors->cross_hair;
281 if (Keyboard::the_keyboard().key_is_down (GDK_Control_L)) {
282 current_canvas_cursor = _cursors->zoom_out;
284 current_canvas_cursor = _cursors->zoom_in;
289 current_canvas_cursor = _cursors->time_fx; // just use playhead
293 current_canvas_cursor = _cursors->speaker;
297 if (!_internal_editing) {
298 switch (_join_object_range_state) {
299 case JOIN_OBJECT_RANGE_NONE:
301 case JOIN_OBJECT_RANGE_OBJECT:
302 current_canvas_cursor = which_grabber_cursor ();
304 case JOIN_OBJECT_RANGE_RANGE:
305 current_canvas_cursor = _cursors->selector;
310 /* up-down cursor as a cue that automation can be dragged up and down when in join object/range mode */
311 if (!_internal_editing && get_smart_mode() ) {
314 get_pointer_position (x, y);
316 if (x >= 0 && y >= 0) {
318 vector<ArdourCanvas::Item const *> items;
320 _track_canvas->root()->add_items_at_point (ArdourCanvas::Duple (x,y), items);
322 // first item will be the upper most
324 if (!items.empty()) {
325 const ArdourCanvas::Item* i = items.front();
327 if (i && i->parent() && i->parent()->get_data (X_("timeselection"))) {
328 pair<TimeAxisView*, int> tvp = trackview_by_y_position (_last_motion_y);
329 if (dynamic_cast<AutomationTimeAxisView*> (tvp.first)) {
330 current_canvas_cursor = _cursors->up_down;
337 set_canvas_cursor (current_canvas_cursor, true);
341 Editor::mouse_mode_object_range_toggled()
343 MouseMode m = mouse_mode;
345 Glib::RefPtr<Action> act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-range"));
347 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
350 if (tact->get_active()) {
351 m = MouseObject; //Smart mode turned to ON, force editing to Object mode
354 set_mouse_mode(m, true); //call this so the button styles can get updated
358 Editor::set_mouse_mode (MouseMode m, bool force)
360 if (_drags->active ()) {
364 if (!force && m == mouse_mode) {
368 Glib::RefPtr<Action> act;
372 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-range"));
376 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-object"));
380 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-draw"));
384 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-gain"));
388 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-zoom"));
392 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-timefx"));
396 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-audition"));
402 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
405 /* go there and back to ensure that the toggled handler is called to set up mouse_mode */
406 tact->set_active (false);
407 tact->set_active (true);
409 //NOTE: this will result in a call to mouse_mode_toggled which does the heavy lifting
413 Editor::mouse_mode_toggled (MouseMode m)
415 Glib::RefPtr<Action> act;
416 Glib::RefPtr<ToggleAction> tact;
420 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-range"));
424 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-object"));
428 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-draw"));
432 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-gain"));
436 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-zoom"));
440 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-timefx"));
444 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-audition"));
450 tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
453 if (!tact->get_active()) {
454 /* this was just the notification that the old mode has been
455 * left. we'll get called again with the new mode active in a
463 act = ActionManager::get_action (X_("MouseMode"), X_("toggle-internal-edit"));
464 tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
465 tact->set_active (true);
471 if (_session && mouse_mode == MouseAudition) {
472 /* stop transport and reset default speed to avoid oddness with
474 _session->request_transport_speed (0.0, true);
481 //TODO: set button styles for smart buttons
483 if ( smart_mode_action->get_active() ) {
484 if( mouse_mode == MouseObject ) { //smart active and object active
485 smart_mode_button.set_active(1);
486 smart_mode_button.set_name("smart mode button");
487 mouse_move_button.set_name("smart mode button");
488 } else { //smart active but object inactive
489 smart_mode_button.set_active(0);
490 smart_mode_button.set_name("smart mode button");
491 mouse_move_button.set_name("mouse mode button");
494 smart_mode_button.set_active(0);
495 smart_mode_button.set_name("mouse mode button");
496 mouse_move_button.set_name("mouse mode button");
500 set_canvas_cursor ();
501 set_gain_envelope_visibility ();
503 update_time_selection_display ();
505 MouseModeChanged (); /* EMIT SIGNAL */
509 Editor::update_time_selection_display ()
511 if (smart_mode_action->get_active()) {
512 /* not sure what to do here */
513 if (mouse_mode == MouseObject) {
517 switch (mouse_mode) {
519 selection->clear_objects ();
522 selection->clear_time ();
529 Editor::step_mouse_mode (bool next)
531 switch (current_mouse_mode()) {
534 if (Profile->get_sae()) {
535 set_mouse_mode (MouseZoom);
537 set_mouse_mode (MouseRange);
540 set_mouse_mode (MouseTimeFX);
545 if (next) set_mouse_mode (MouseDraw);
546 else set_mouse_mode (MouseObject);
550 if (next) set_mouse_mode (MouseZoom);
551 else set_mouse_mode (MouseRange);
556 if (Profile->get_sae()) {
557 set_mouse_mode (MouseTimeFX);
559 set_mouse_mode (MouseGain);
562 if (Profile->get_sae()) {
563 set_mouse_mode (MouseObject);
565 set_mouse_mode (MouseDraw);
571 if (next) set_mouse_mode (MouseTimeFX);
572 else set_mouse_mode (MouseZoom);
577 set_mouse_mode (MouseAudition);
579 if (Profile->get_sae()) {
580 set_mouse_mode (MouseZoom);
582 set_mouse_mode (MouseGain);
588 if (next) set_mouse_mode (MouseObject);
589 else set_mouse_mode (MouseTimeFX);
595 Editor::toggle_internal_editing_from_double_click (GdkEvent* event)
597 if (_drags->active()) {
598 _drags->end_grab (event);
601 ActionManager::do_action ("MouseMode", "toggle-internal-edit");
603 /* prevent reversion of edit cursor on button release */
605 pre_press_cursor = 0;
611 Editor::button_selection (ArdourCanvas::Item* /*item*/, GdkEvent* event, ItemType item_type)
613 /* in object/audition/timefx/gain-automation mode,
614 any button press sets the selection if the object
615 can be selected. this is a bit of hack, because
616 we want to avoid this if the mouse operation is a
619 note: not dbl-click or triple-click
621 Also note that there is no region selection in internal edit mode, otherwise
622 for operations operating on the selection (e.g. cut) it is not obvious whether
623 to cut notes or regions.
626 MouseMode eff_mouse_mode = 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)) {
648 if (event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) {
650 if ((event->button.state & Keyboard::RelevantModifierKeyMask) && event->button.button != 1) {
652 /* almost no selection action on modified button-2 or button-3 events */
654 if (item_type != RegionItem && event->button.button != 2) {
660 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
661 bool press = (event->type == GDK_BUTTON_PRESS);
666 if (eff_mouse_mode != MouseRange) {
667 set_selected_regionview_from_click (press, op);
669 /* don't change the selection unless the
670 clicked track is not currently selected. if
671 so, "collapse" the selection to just this
674 if (!selection->selected (clicked_axisview)) {
675 set_selected_track_as_side_effect (Selection::Set);
679 if (eff_mouse_mode != MouseRange) {
680 set_selected_regionview_from_click (press, op);
685 case RegionViewNameHighlight:
687 case LeftFrameHandle:
688 case RightFrameHandle:
689 if (eff_mouse_mode != MouseRange) {
690 set_selected_regionview_from_click (press, op);
691 } else if (event->type == GDK_BUTTON_PRESS) {
692 set_selected_track_as_side_effect (op);
696 case FadeInHandleItem:
698 case FadeOutHandleItem:
700 case StartCrossFadeItem:
701 case EndCrossFadeItem:
702 if (eff_mouse_mode != MouseRange) {
703 cerr << "Should be setting selected regionview\n";
704 set_selected_regionview_from_click (press, op);
705 } else if (event->type == GDK_BUTTON_PRESS) {
706 set_selected_track_as_side_effect (op);
710 case ControlPointItem:
711 set_selected_track_as_side_effect (op);
712 if (eff_mouse_mode != MouseRange) {
713 set_selected_control_point_from_click (press, op);
718 /* for context click, select track */
719 if (event->button.button == 3) {
720 selection->clear_tracks ();
721 set_selected_track_as_side_effect (op);
725 case AutomationTrackItem:
726 set_selected_track_as_side_effect (op);
735 Editor::button_press_handler_1 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
737 /* single mouse clicks on any of these item types operate
738 independent of mouse mode, mostly because they are
739 not on the main track canvas or because we want
744 case PlayheadCursorItem:
745 _drags->set (new CursorDrag (this, *playhead_cursor, true), event);
749 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
750 hide_marker (item, event);
752 _drags->set (new MarkerDrag (this, item), event);
756 case TempoMarkerItem:
758 TempoMarker* m = reinterpret_cast<TempoMarker*> (item->get_data ("marker"));
761 new TempoMarkerDrag (
764 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
771 case MeterMarkerItem:
773 MeterMarker* m = reinterpret_cast<MeterMarker*> (item->get_data ("marker"));
776 new MeterMarkerDrag (
779 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
787 _drags->set (new VideoTimeLineDrag (this, item), event);
794 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
795 _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
801 case RangeMarkerBarItem:
802 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
803 _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
805 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateRangeMarker), event);
810 case CdMarkerBarItem:
811 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
812 _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
814 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateCDMarker), event);
819 case TransportMarkerBarItem:
820 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
821 _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
823 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateTransportMarker), event);
832 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
833 /* special case: allow trim of range selections in joined object mode;
834 in theory eff should equal MouseRange in this case, but it doesn't
835 because entering the range selection canvas item results in entered_regionview
836 being set to 0, so update_join_object_range_location acts as if we aren't
839 if (item_type == StartSelectionTrimItem) {
840 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
841 } else if (item_type == EndSelectionTrimItem) {
842 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
846 Editing::MouseMode eff = effective_mouse_mode ();
848 /* special case: allow drag of region fade in/out in object mode with join object/range enabled */
849 if (item_type == FadeInHandleItem || item_type == FadeOutHandleItem) {
853 /* there is no Range mode when in internal edit mode */
854 if (eff == MouseRange && internal_editing()) {
861 case StartSelectionTrimItem:
862 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
865 case EndSelectionTrimItem:
866 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
870 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
871 start_selection_grab (item, event);
873 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
874 /* grab selection for moving */
875 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionMove), event);
877 double const y = event->button.y;
878 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
880 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
881 if ( get_smart_mode() && atv) {
882 /* smart "join" mode: drag automation */
883 _drags->set (new AutomationRangeDrag (this, atv, selection->time), event, _cursors->up_down);
885 /* this was debated, but decided the more common action was to
886 make a new selection */
887 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
894 if (internal_editing()) {
895 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
896 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
900 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::RangeSelectModifier)) {
901 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionExtend), event);
903 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
909 case RegionViewNameHighlight:
910 if (!clicked_regionview->region()->locked()) {
911 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
917 if (!internal_editing()) {
918 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::RangeSelectModifier)) {
919 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionExtend), event);
921 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
931 /* Existing note: allow trimming/motion */
932 if (internal_editing()) {
933 /* trim notes if we're in internal edit mode and near the ends of the note */
934 NoteBase* cn = reinterpret_cast<NoteBase*>(item->get_data ("notebase"));
936 if (cn->big_enough_to_trim() && cn->mouse_near_ends()) {
937 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
939 _drags->set (new NoteDrag (this, item), event);
945 if (internal_editing()) {
946 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
947 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
961 /* Existing note: allow trimming/motion */
962 if (internal_editing()) {
963 NoteBase* cn = reinterpret_cast<NoteBase*> (item->get_data ("notebase"));
965 if (cn->big_enough_to_trim() && cn->mouse_near_ends()) {
966 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
968 _drags->set (new NoteDrag (this, item), event);
978 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)) &&
979 event->type == GDK_BUTTON_PRESS) {
981 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
983 } else if (event->type == GDK_BUTTON_PRESS) {
986 case FadeInHandleItem:
988 _drags->set (new FadeInDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), selection->regions), event, _cursors->fade_in);
992 case FadeOutHandleItem:
994 _drags->set (new FadeOutDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), selection->regions), event, _cursors->fade_out);
998 case StartCrossFadeItem:
999 case EndCrossFadeItem:
1000 /* we might allow user to grab inside the fade to trim a region with preserve_fade_anchor. for not this is not fully implemented */
1001 // if (!clicked_regionview->region()->locked()) {
1002 // _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer(), true), event);
1007 case FeatureLineItem:
1009 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier)) {
1010 remove_transient(item);
1014 _drags->set (new FeatureLineDrag (this, item), event);
1020 if (dynamic_cast<AutomationRegionView*> (clicked_regionview)) {
1021 /* click on an automation region view; do nothing here and let the ARV's signal handler
1027 if (internal_editing ()) {
1031 /* click on a normal region view */
1032 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
1033 add_region_copy_drag (item, event, clicked_regionview);
1034 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
1035 add_region_brush_drag (item, event, clicked_regionview);
1037 add_region_drag (item, event, clicked_regionview);
1041 // if (!internal_editing() && (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE && !selection->regions.empty())) {
1042 // _drags->add (new SelectionDrag (this, clicked_axisview->get_selection_rect (clicked_selection)->rect, SelectionDrag::SelectionMove));
1045 _drags->start_grab (event);
1049 case RegionViewNameHighlight:
1050 case LeftFrameHandle:
1051 case RightFrameHandle:
1052 if (!clicked_regionview->region()->locked()) {
1053 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1058 case RegionViewName:
1060 /* rename happens on edit clicks */
1061 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer()), event);
1066 case ControlPointItem:
1067 _drags->set (new ControlPointDrag (this, item), event);
1071 case AutomationLineItem:
1072 _drags->set (new LineDrag (this, item), event);
1077 if (internal_editing()) {
1078 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
1079 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
1083 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
1087 case AutomationTrackItem:
1089 TimeAxisView* parent = clicked_axisview->get_parent ();
1090 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (clicked_axisview);
1092 if (parent && dynamic_cast<MidiTimeAxisView*> (parent) && atv->show_regions ()) {
1094 RouteTimeAxisView* p = dynamic_cast<RouteTimeAxisView*> (parent);
1096 boost::shared_ptr<Playlist> pl = p->track()->playlist ();
1097 if (pl->n_regions() == 0) {
1098 /* Parent has no regions; create one so that we have somewhere to put automation */
1099 _drags->set (new RegionCreateDrag (this, item, parent), event);
1101 /* See if there's a region before the click that we can extend, and extend it if so */
1102 framepos_t const t = canvas_event_sample (event);
1103 boost::shared_ptr<Region> prev = pl->find_next_region (t, End, -1);
1105 _drags->set (new RegionCreateDrag (this, item, parent), event);
1107 prev->set_length (t - prev->position ());
1111 /* rubberband drag to select automation points */
1112 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
1119 if ( get_smart_mode() ) {
1120 /* we're in "smart" joined mode, and we've clicked on a Selection */
1121 double const y = event->button.y;
1122 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
1124 /* if we're over an automation track, start a drag of its data */
1125 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
1127 _drags->set (new AutomationRangeDrag (this, atv, selection->time), event, _cursors->up_down);
1130 /* if we're over a track and a region, and in the `object' part of a region,
1131 put a selection around the region and drag both
1133 /* RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1134 if (rtv && _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
1135 boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track> (rtv->route ());
1137 boost::shared_ptr<Playlist> pl = t->playlist ();
1140 boost::shared_ptr<Region> r = pl->top_region_at (canvas_event_sample (event));
1142 RegionView* rv = rtv->view()->find_view (r);
1143 clicked_selection = select_range (rv->region()->position(),
1144 rv->region()->last_frame()+1);
1145 _drags->add (new SelectionDrag (this, item, SelectionDrag::SelectionMove));
1146 list<RegionView*> rvs;
1148 _drags->add (new RegionMoveDrag (this, item, rv, rvs, false, false));
1149 _drags->start_grab (event);
1173 switch (item_type) {
1175 _drags->set (new LineDrag (this, item), event);
1178 case ControlPointItem:
1179 _drags->set (new ControlPointDrag (this, item), event);
1185 AudioRegionView* arv = dynamic_cast<AudioRegionView *> (clicked_regionview);
1187 _drags->set (new AutomationRangeDrag (this, arv, selection->time), event, _cursors->up_down);
1188 _drags->start_grab (event);
1194 case AutomationLineItem:
1195 _drags->set (new LineDrag (this, item), event);
1205 if (event->type == GDK_BUTTON_PRESS) {
1206 _drags->set (new MouseZoomDrag (this, item), event);
1213 if (internal_editing() && item_type == NoteItem ) {
1214 /* drag notes if we're in internal edit mode */
1215 NoteBase* cn = reinterpret_cast<NoteBase*>(item->get_data ("notebase"));
1217 if (cn->big_enough_to_trim()) {
1218 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
1221 } else if (clicked_regionview) {
1223 _drags->set (new TimeFXDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1229 _drags->set (new ScrubDrag (this, item), event);
1230 scrub_reversals = 0;
1231 scrub_reverse_distance = 0;
1232 last_scrub_x = event->button.x;
1233 scrubbing_direction = 0;
1234 set_canvas_cursor (_cursors->transparent);
1246 Editor::button_press_handler_2 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1248 Editing::MouseMode const eff = effective_mouse_mode ();
1251 switch (item_type) {
1253 if (internal_editing ()) {
1254 /* no region drags in internal edit mode */
1258 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
1259 add_region_copy_drag (item, event, clicked_regionview);
1261 add_region_drag (item, event, clicked_regionview);
1263 _drags->start_grab (event);
1266 case ControlPointItem:
1267 _drags->set (new ControlPointDrag (this, item), event);
1275 switch (item_type) {
1276 case RegionViewNameHighlight:
1277 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1281 case LeftFrameHandle:
1282 case RightFrameHandle:
1283 if (!internal_editing ()) {
1284 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1289 case RegionViewName:
1290 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer()), event);
1304 /* relax till release */
1310 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1311 temporal_zoom_to_frame (false, canvas_event_sample (event));
1313 temporal_zoom_to_frame (true, canvas_event_sample(event));
1326 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1328 if (event->type == GDK_2BUTTON_PRESS) {
1329 _drags->mark_double_click ();
1330 gdk_pointer_ungrab (GDK_CURRENT_TIME);
1334 if (event->type != GDK_BUTTON_PRESS) {
1338 Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->_track_canvas_viewport->get_window();
1340 if (canvas_window) {
1341 Glib::RefPtr<const Gdk::Window> pointer_window;
1344 Gdk::ModifierType mask;
1346 pointer_window = canvas_window->get_pointer (x, y, mask);
1348 if (pointer_window == _track_canvas->get_window()) {
1349 _track_canvas->window_to_canvas (x, y, wx, wy);
1353 pre_press_cursor = current_canvas_cursor;
1355 _track_canvas->grab_focus();
1357 if (_session && _session->actively_recording()) {
1361 if (internal_editing()) {
1362 bool leave_internal_edit_mode = false;
1364 switch (item_type) {
1369 if (!dynamic_cast<MidiRegionView*> (clicked_regionview) && !dynamic_cast<AutomationRegionView*> (clicked_regionview)) {
1370 leave_internal_edit_mode = true;
1374 case PlayheadCursorItem:
1376 case TempoMarkerItem:
1377 case MeterMarkerItem:
1381 case RangeMarkerBarItem:
1382 case CdMarkerBarItem:
1383 case TransportMarkerBarItem:
1385 /* button press on these events never does anything to
1386 change the editing mode.
1394 if (leave_internal_edit_mode) {
1395 ActionManager::do_action ("MouseMode", "toggle-internal-edit");
1399 button_selection (item, event, item_type);
1401 if (!_drags->active () &&
1402 (Keyboard::is_delete_event (&event->button) ||
1403 Keyboard::is_context_menu_event (&event->button) ||
1404 Keyboard::is_edit_event (&event->button))) {
1406 /* handled by button release */
1410 //not rolling, range mode click + join_play_range : locate the PH here
1411 if ( !_drags->active () && !_session->transport_rolling() && ( effective_mouse_mode() == MouseRange ) && Config->get_always_play_range() ) {
1412 framepos_t where = canvas_event_sample (event);
1414 _session->request_locate (where, false);
1417 switch (event->button.button) {
1419 return button_press_handler_1 (item, event, item_type);
1423 return button_press_handler_2 (item, event, item_type);
1430 return button_press_dispatch (&event->button);
1439 Editor::button_press_dispatch (GdkEventButton* ev)
1441 /* this function is intended only for buttons 4 and above.
1444 Gtkmm2ext::MouseButton b (ev->state, ev->button);
1445 return button_bindings->activate (b, Gtkmm2ext::Bindings::Press);
1449 Editor::button_release_dispatch (GdkEventButton* ev)
1451 /* this function is intended only for buttons 4 and above.
1454 Gtkmm2ext::MouseButton b (ev->state, ev->button);
1455 return button_bindings->activate (b, Gtkmm2ext::Bindings::Release);
1459 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1461 framepos_t where = canvas_event_sample (event);
1462 AutomationTimeAxisView* atv = 0;
1464 if (pre_press_cursor) {
1465 set_canvas_cursor (pre_press_cursor);
1466 pre_press_cursor = 0;
1469 /* no action if we're recording */
1471 if (_session && _session->actively_recording()) {
1475 /* see if we're finishing a drag */
1477 bool were_dragging = false;
1478 if (_drags->active ()) {
1479 bool const r = _drags->end_grab (event);
1481 /* grab dragged, so do nothing else */
1485 were_dragging = true;
1488 update_region_layering_order_editor ();
1490 /* edit events get handled here */
1492 if (!_drags->active () && Keyboard::is_edit_event (&event->button)) {
1493 switch (item_type) {
1495 show_region_properties ();
1498 case TempoMarkerItem: {
1500 TempoMarker* tempo_marker;
1502 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
1503 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
1507 if ((tempo_marker = dynamic_cast<TempoMarker*> (marker)) == 0) {
1508 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
1512 edit_tempo_marker (*tempo_marker);
1516 case MeterMarkerItem: {
1518 MeterMarker* meter_marker;
1520 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
1521 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
1525 if ((meter_marker = dynamic_cast<MeterMarker*> (marker)) == 0) {
1526 fatal << _("programming error: marker for meter is not a meter marker!") << endmsg;
1529 edit_meter_marker (*meter_marker);
1533 case RegionViewName:
1534 if (clicked_regionview->name_active()) {
1535 return mouse_rename_region (item, event);
1539 case ControlPointItem:
1540 edit_control_point (item);
1549 /* context menu events get handled here */
1550 if (Keyboard::is_context_menu_event (&event->button)) {
1552 context_click_event = *event;
1554 if (!_drags->active ()) {
1556 /* no matter which button pops up the context menu, tell the menu
1557 widget to use button 1 to drive menu selection.
1560 switch (item_type) {
1562 case FadeInHandleItem:
1564 case FadeOutHandleItem:
1565 popup_fade_context_menu (1, event->button.time, item, item_type);
1568 case StartCrossFadeItem:
1569 popup_xfade_in_context_menu (1, event->button.time, item, item_type);
1572 case EndCrossFadeItem:
1573 popup_xfade_out_context_menu (1, event->button.time, item, item_type);
1577 popup_track_context_menu (1, event->button.time, item_type, false);
1581 case RegionViewNameHighlight:
1582 case LeftFrameHandle:
1583 case RightFrameHandle:
1584 case RegionViewName:
1585 popup_track_context_menu (1, event->button.time, item_type, false);
1589 popup_track_context_menu (1, event->button.time, item_type, true);
1592 case AutomationTrackItem:
1593 popup_track_context_menu (1, event->button.time, item_type, false);
1597 case RangeMarkerBarItem:
1598 case TransportMarkerBarItem:
1599 case CdMarkerBarItem:
1603 popup_ruler_menu (where, item_type);
1607 marker_context_menu (&event->button, item);
1610 case TempoMarkerItem:
1611 tempo_or_meter_marker_context_menu (&event->button, item);
1614 case MeterMarkerItem:
1615 tempo_or_meter_marker_context_menu (&event->button, item);
1618 case CrossfadeViewItem:
1619 popup_track_context_menu (1, event->button.time, item_type, false);
1622 case ControlPointItem:
1623 popup_control_point_context_menu (item, event);
1634 /* delete events get handled here */
1636 Editing::MouseMode const eff = effective_mouse_mode ();
1638 if (!_drags->active () && Keyboard::is_delete_event (&event->button)) {
1640 switch (item_type) {
1641 case TempoMarkerItem:
1642 remove_tempo_marker (item);
1645 case MeterMarkerItem:
1646 remove_meter_marker (item);
1650 remove_marker (*item, event);
1654 if (eff == MouseObject) {
1655 remove_clicked_region ();
1659 case ControlPointItem:
1660 remove_control_point (item);
1664 remove_midi_note (item, event);
1673 switch (event->button.button) {
1676 switch (item_type) {
1677 /* see comments in button_press_handler */
1678 case PlayheadCursorItem:
1681 case AutomationLineItem:
1682 case StartSelectionTrimItem:
1683 case EndSelectionTrimItem:
1687 if (!_dragging_playhead) {
1688 snap_to_with_modifier (where, event, 0, true);
1689 mouse_add_new_marker (where);
1693 case CdMarkerBarItem:
1694 if (!_dragging_playhead) {
1695 // if we get here then a dragged range wasn't done
1696 snap_to_with_modifier (where, event, 0, true);
1697 mouse_add_new_marker (where, true);
1702 if (!_dragging_playhead) {
1703 snap_to_with_modifier (where, event);
1704 mouse_add_new_tempo_event (where);
1709 if (!_dragging_playhead) {
1710 mouse_add_new_meter_event (pixel_to_sample (event->button.x));
1721 switch (item_type) {
1722 case AutomationTrackItem:
1723 atv = dynamic_cast<AutomationTimeAxisView*>(clicked_axisview);
1725 bool with_guard_points = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
1726 atv->add_automation_event (event, where, event->button.y, with_guard_points);
1736 switch (item_type) {
1739 /* check that we didn't drag before releasing, since
1740 its really annoying to create new control
1741 points when doing this.
1743 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (clicked_regionview);
1744 if (!were_dragging && arv) {
1745 bool with_guard_points = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
1746 arv->add_gain_point_event (item, event, with_guard_points);
1752 case AutomationTrackItem: {
1753 bool with_guard_points = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
1754 dynamic_cast<AutomationTimeAxisView*>(clicked_axisview)->
1755 add_automation_event (event, where, event->button.y, with_guard_points);
1765 set_canvas_cursor (current_canvas_cursor);
1766 if (scrubbing_direction == 0) {
1767 /* no drag, just a click */
1768 switch (item_type) {
1770 play_selected_region ();
1776 /* make sure we stop */
1777 _session->request_transport_speed (0.0);
1786 /* do any (de)selection operations that should occur on button release */
1787 button_selection (item, event, item_type);
1796 switch (item_type) {
1798 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1800 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier|Keyboard::SecondaryModifier))) {
1803 // Button2 click is unused
1818 // x_style_paste (where, 1.0);
1839 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1846 switch (item_type) {
1847 case ControlPointItem:
1848 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1849 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1854 at_y = cp->get_y ();
1855 cp->i2w (at_x, at_y);
1859 fraction = 1.0 - (cp->get_y() / cp->line().height());
1861 if (is_drawable() && !_drags->active ()) {
1862 set_canvas_cursor (_cursors->fader);
1865 _verbose_cursor->set (cp->line().get_verbose_cursor_string (fraction), at_x, at_y);
1866 _verbose_cursor->show ();
1871 if (mouse_mode == MouseGain) {
1872 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1874 line->set_outline_color (ARDOUR_UI::config()->get_canvasvar_EnteredGainLine());
1876 if (is_drawable()) {
1877 set_canvas_cursor (_cursors->fader);
1882 case AutomationLineItem:
1883 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1884 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1886 line->set_outline_color (ARDOUR_UI::config()->get_canvasvar_EnteredAutomationLine());
1888 if (is_drawable()) {
1889 set_canvas_cursor (_cursors->fader);
1894 case RegionViewNameHighlight:
1895 if (is_drawable() && effective_mouse_mode() == MouseObject && entered_regionview) {
1896 set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1897 _over_region_trim_target = true;
1901 case LeftFrameHandle:
1902 case RightFrameHandle:
1903 if (is_drawable() && effective_mouse_mode() == MouseObject && !internal_editing() && entered_regionview) {
1904 set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1909 switch (effective_mouse_mode()) {
1911 set_canvas_cursor (_cursors->selector);
1914 set_canvas_cursor (which_grabber_cursor());
1919 case StartSelectionTrimItem:
1920 if (is_drawable()) {
1921 set_canvas_cursor (_cursors->left_side_trim);
1924 case EndSelectionTrimItem:
1925 if (is_drawable()) {
1926 set_canvas_cursor (_cursors->right_side_trim);
1930 case PlayheadCursorItem:
1931 if (is_drawable()) {
1932 switch (_edit_point) {
1934 set_canvas_cursor (_cursors->grabber_edit_point);
1937 set_canvas_cursor (_cursors->grabber);
1944 case RegionViewName:
1946 /* when the name is not an active item, the entire name highlight is for trimming */
1948 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1949 if (mouse_mode == MouseObject && is_drawable()) {
1950 set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1951 _over_region_trim_target = true;
1957 case AutomationTrackItem:
1958 if (is_drawable()) {
1959 Gdk::Cursor *cursor;
1960 switch (mouse_mode) {
1962 cursor = _cursors->selector;
1965 cursor = _cursors->zoom_in;
1968 cursor = _cursors->cross_hair;
1972 set_canvas_cursor (cursor);
1974 AutomationTimeAxisView* atv;
1975 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1976 clear_entered_track = false;
1977 set_entered_track (atv);
1983 case RangeMarkerBarItem:
1984 case TransportMarkerBarItem:
1985 case CdMarkerBarItem:
1988 if (is_drawable()) {
1989 set_canvas_cursor (_cursors->timebar);
1994 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1997 entered_marker = marker;
1998 marker->set_color_rgba (ARDOUR_UI::config()->get_canvasvar_EnteredMarker());
2000 case MeterMarkerItem:
2001 case TempoMarkerItem:
2002 if (is_drawable()) {
2003 set_canvas_cursor (_cursors->timebar);
2007 case FadeInHandleItem:
2008 if (mouse_mode == MouseObject && !internal_editing()) {
2009 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
2011 RegionView* rv = static_cast<RegionView*>(item->get_data ("regionview"));
2012 rect->set_fill_color (rv->get_fill_color());
2013 set_canvas_cursor (_cursors->fade_in);
2018 case FadeOutHandleItem:
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_out);
2028 case FeatureLineItem:
2030 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
2031 line->set_outline_color (0xFF0000FF);
2036 if ( get_smart_mode() ) {
2037 set_canvas_cursor ();
2045 /* second pass to handle entered track status in a comprehensible way.
2048 switch (item_type) {
2050 case AutomationLineItem:
2051 case ControlPointItem:
2052 /* these do not affect the current entered track state */
2053 clear_entered_track = false;
2056 case AutomationTrackItem:
2057 /* handled above already */
2061 set_entered_track (0);
2069 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent*, ItemType item_type)
2078 switch (item_type) {
2079 case ControlPointItem:
2080 if (is_drawable()) {
2081 set_canvas_cursor (current_canvas_cursor);
2084 _verbose_cursor->hide ();
2087 case RegionViewNameHighlight:
2088 case LeftFrameHandle:
2089 case RightFrameHandle:
2090 case StartSelectionTrimItem:
2091 case EndSelectionTrimItem:
2092 case PlayheadCursorItem:
2094 _over_region_trim_target = false;
2096 if (is_drawable()) {
2097 set_canvas_cursor (current_canvas_cursor);
2102 case AutomationLineItem:
2103 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
2105 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
2107 line->set_outline_color (al->get_line_color());
2110 if (is_drawable()) {
2111 set_canvas_cursor (current_canvas_cursor);
2115 case RegionViewName:
2116 /* see enter_handler() for notes */
2117 _over_region_trim_target = false;
2119 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
2120 if (is_drawable() && mouse_mode == MouseObject) {
2121 set_canvas_cursor (current_canvas_cursor);
2126 case RangeMarkerBarItem:
2127 case TransportMarkerBarItem:
2128 case CdMarkerBarItem:
2132 if (is_drawable()) {
2133 set_canvas_cursor (current_canvas_cursor);
2138 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2142 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
2143 location_flags_changed (loc, this);
2146 case MeterMarkerItem:
2147 case TempoMarkerItem:
2149 if (is_drawable()) {
2150 set_canvas_cursor (current_canvas_cursor);
2155 case FadeInHandleItem:
2156 case FadeOutHandleItem:
2157 rv = static_cast<RegionView*>(item->get_data ("regionview"));
2159 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
2161 rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_InactiveFadeHandle());
2164 set_canvas_cursor (current_canvas_cursor);
2167 case AutomationTrackItem:
2168 if (is_drawable()) {
2169 set_canvas_cursor (current_canvas_cursor);
2170 clear_entered_track = true;
2171 Glib::signal_idle().connect (sigc::mem_fun(*this, &Editor::left_automation_track));
2174 case FeatureLineItem:
2176 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
2177 line->set_outline_color (ARDOUR_UI::config()->get_canvasvar_ZeroLine());
2189 Editor::left_automation_track ()
2191 if (clear_entered_track) {
2192 set_entered_track (0);
2193 clear_entered_track = false;
2199 Editor::scrub (framepos_t frame, double current_x)
2203 if (scrubbing_direction == 0) {
2205 _session->request_locate (frame, false);
2206 _session->request_transport_speed (0.1);
2207 scrubbing_direction = 1;
2211 if (last_scrub_x > current_x) {
2213 /* pointer moved to the left */
2215 if (scrubbing_direction > 0) {
2217 /* we reversed direction to go backwards */
2220 scrub_reverse_distance += (int) (last_scrub_x - current_x);
2224 /* still moving to the left (backwards) */
2226 scrub_reversals = 0;
2227 scrub_reverse_distance = 0;
2229 delta = 0.01 * (last_scrub_x - current_x);
2230 _session->request_transport_speed_nonzero (_session->transport_speed() - delta);
2234 /* pointer moved to the right */
2236 if (scrubbing_direction < 0) {
2237 /* we reversed direction to go forward */
2240 scrub_reverse_distance += (int) (current_x - last_scrub_x);
2243 /* still moving to the right */
2245 scrub_reversals = 0;
2246 scrub_reverse_distance = 0;
2248 delta = 0.01 * (current_x - last_scrub_x);
2249 _session->request_transport_speed_nonzero (_session->transport_speed() + delta);
2253 /* if there have been more than 2 opposite motion moves detected, or one that moves
2254 back more than 10 pixels, reverse direction
2257 if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
2259 if (scrubbing_direction > 0) {
2260 /* was forwards, go backwards */
2261 _session->request_transport_speed (-0.1);
2262 scrubbing_direction = -1;
2264 /* was backwards, go forwards */
2265 _session->request_transport_speed (0.1);
2266 scrubbing_direction = 1;
2269 scrub_reverse_distance = 0;
2270 scrub_reversals = 0;
2274 last_scrub_x = current_x;
2278 Editor::motion_handler (ArdourCanvas::Item* /*item*/, GdkEvent* event, bool from_autoscroll)
2280 _last_motion_y = event->motion.y;
2282 if (event->motion.is_hint) {
2285 /* We call this so that MOTION_NOTIFY events continue to be
2286 delivered to the canvas. We need to do this because we set
2287 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
2288 the density of the events, at the expense of a round-trip
2289 to the server. Given that this will mostly occur on cases
2290 where DISPLAY = :0.0, and given the cost of what the motion
2291 event might do, its a good tradeoff.
2294 _track_canvas->get_pointer (x, y);
2297 if (current_stepping_trackview) {
2298 /* don't keep the persistent stepped trackview if the mouse moves */
2299 current_stepping_trackview = 0;
2300 step_timeout.disconnect ();
2303 if (_session && _session->actively_recording()) {
2304 /* Sorry. no dragging stuff around while we record */
2308 JoinObjectRangeState const old = _join_object_range_state;
2309 update_join_object_range_location (event->motion.x, event->motion.y);
2311 if (!_internal_editing && _join_object_range_state != old) {
2312 set_canvas_cursor ();
2315 if (!_internal_editing && _over_region_trim_target) {
2316 set_canvas_cursor_for_region_view (event->motion.x, entered_regionview);
2319 bool handled = false;
2320 if (_drags->active ()) {
2321 handled = _drags->motion_handler (event, from_autoscroll);
2328 track_canvas_motion (event);
2333 Editor::can_remove_control_point (ArdourCanvas::Item* item)
2335 ControlPoint* control_point;
2337 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2338 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2342 AutomationLine& line = control_point->line ();
2343 if (dynamic_cast<AudioRegionGainLine*> (&line)) {
2344 /* we shouldn't remove the first or last gain point in region gain lines */
2345 if (line.is_last_point(*control_point) || line.is_first_point(*control_point)) {
2354 Editor::remove_control_point (ArdourCanvas::Item* item)
2356 if (!can_remove_control_point (item)) {
2360 ControlPoint* control_point;
2362 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2363 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2367 control_point->line().remove_point (*control_point);
2371 Editor::edit_control_point (ArdourCanvas::Item* item)
2373 ControlPoint* p = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"));
2376 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2380 ControlPointDialog d (p);
2383 if (d.run () != RESPONSE_ACCEPT) {
2387 p->line().modify_point_y (*p, d.get_y_fraction ());
2391 Editor::edit_notes (TimeAxisViewItem& tavi)
2393 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(&tavi);
2399 MidiRegionView::Selection const & s = mrv->selection();
2405 EditNoteDialog* d = new EditNoteDialog (&(*s.begin())->region_view(), s);
2409 d->signal_response().connect (sigc::bind (sigc::mem_fun (*this, &Editor::note_edit_done), d));
2413 Editor::note_edit_done (int r, EditNoteDialog* d)
2420 Editor::visible_order_range (int* low, int* high) const
2422 *low = TimeAxisView::max_order ();
2425 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
2427 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
2429 if (!rtv->hidden()) {
2431 if (*high < rtv->order()) {
2432 *high = rtv->order ();
2435 if (*low > rtv->order()) {
2436 *low = rtv->order ();
2443 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
2445 /* Either add to or set the set the region selection, unless
2446 this is an alignment click (control used)
2449 if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
2450 TimeAxisView* tv = &rv.get_time_axis_view();
2451 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
2453 if (rtv && rtv->is_track()) {
2454 speed = rtv->track()->speed();
2457 framepos_t where = get_preferred_edit_position();
2461 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
2463 align_region (rv.region(), SyncPoint, (framepos_t) (where * speed));
2465 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
2467 align_region (rv.region(), End, (framepos_t) (where * speed));
2471 align_region (rv.region(), Start, (framepos_t) (where * speed));
2478 Editor::collect_new_region_view (RegionView* rv)
2480 latest_regionviews.push_back (rv);
2484 Editor::collect_and_select_new_region_view (RegionView* rv)
2487 latest_regionviews.push_back (rv);
2491 Editor::cancel_selection ()
2493 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2494 (*i)->hide_selection ();
2497 selection->clear ();
2498 clicked_selection = 0;
2502 Editor::cancel_time_selection ()
2504 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2505 (*i)->hide_selection ();
2507 selection->time.clear ();
2508 clicked_selection = 0;
2512 Editor::point_trim (GdkEvent* event, framepos_t new_bound)
2514 RegionView* rv = clicked_regionview;
2516 /* Choose action dependant on which button was pressed */
2517 switch (event->button.button) {
2519 begin_reversible_command (_("start point trim"));
2521 if (selection->selected (rv)) {
2522 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
2523 i != selection->regions.by_layer().end(); ++i)
2525 if (!(*i)->region()->locked()) {
2526 (*i)->region()->clear_changes ();
2527 (*i)->region()->trim_front (new_bound);
2528 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2533 if (!rv->region()->locked()) {
2534 rv->region()->clear_changes ();
2535 rv->region()->trim_front (new_bound);
2536 _session->add_command(new StatefulDiffCommand (rv->region()));
2540 commit_reversible_command();
2544 begin_reversible_command (_("End point trim"));
2546 if (selection->selected (rv)) {
2548 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
2550 if (!(*i)->region()->locked()) {
2551 (*i)->region()->clear_changes();
2552 (*i)->region()->trim_end (new_bound);
2553 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2559 if (!rv->region()->locked()) {
2560 rv->region()->clear_changes ();
2561 rv->region()->trim_end (new_bound);
2562 _session->add_command (new StatefulDiffCommand (rv->region()));
2566 commit_reversible_command();
2575 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* /*event*/)
2580 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2581 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2585 Location* location = find_location_from_marker (marker, is_start);
2586 location->set_hidden (true, this);
2591 Editor::reposition_zoom_rect (framepos_t start, framepos_t end)
2593 double x1 = sample_to_pixel (start);
2594 double x2 = sample_to_pixel (end);
2595 double y2 = _full_canvas_height - 1.0;
2597 zoom_rect->set (ArdourCanvas::Rect (x1, 1.0, x2, y2));
2602 Editor::mouse_rename_region (ArdourCanvas::Item* /*item*/, GdkEvent* /*event*/)
2604 using namespace Gtkmm2ext;
2606 ArdourPrompter prompter (false);
2608 prompter.set_prompt (_("Name for region:"));
2609 prompter.set_initial_text (clicked_regionview->region()->name());
2610 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
2611 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
2612 prompter.show_all ();
2613 switch (prompter.run ()) {
2614 case Gtk::RESPONSE_ACCEPT:
2616 prompter.get_result(str);
2618 clicked_regionview->region()->set_name (str);
2627 Editor::mouse_brush_insert_region (RegionView* rv, framepos_t pos)
2629 /* no brushing without a useful snap setting */
2631 switch (_snap_mode) {
2633 return; /* can't work because it allows region to be placed anywhere */
2638 switch (_snap_type) {
2646 /* don't brush a copy over the original */
2648 if (pos == rv->region()->position()) {
2652 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
2654 if (rtv == 0 || !rtv->is_track()) {
2658 boost::shared_ptr<Playlist> playlist = rtv->playlist();
2659 double speed = rtv->track()->speed();
2661 playlist->clear_changes ();
2662 boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region(), true));
2663 playlist->add_region (new_region, (framepos_t) (pos * speed));
2664 _session->add_command (new StatefulDiffCommand (playlist));
2666 // playlist is frozen, so we have to update manually XXX this is disgusting
2668 playlist->RegionAdded (new_region); /* EMIT SIGNAL */
2672 Editor::track_height_step_timeout ()
2674 if (get_microseconds() - last_track_height_step_timestamp < 250000) {
2675 current_stepping_trackview = 0;
2682 Editor::add_region_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2684 assert (region_view);
2686 if (!region_view->region()->playlist()) {
2690 _region_motion_group->raise_to_top ();
2692 if (Config->get_edit_mode() == Splice) {
2693 _drags->add (new RegionSpliceDrag (this, item, region_view, selection->regions.by_layer()));
2695 _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), false, false));
2700 Editor::add_region_copy_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2702 assert (region_view);
2704 if (!region_view->region()->playlist()) {
2708 _region_motion_group->raise_to_top ();
2710 _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), false, true));
2714 Editor::add_region_brush_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2716 assert (region_view);
2718 if (!region_view->region()->playlist()) {
2722 if (Config->get_edit_mode() == Splice) {
2726 _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), true, false));
2728 begin_reversible_command (Operations::drag_region_brush);
2731 /** Start a grab where a time range is selected, track(s) are selected, and the
2732 * user clicks and drags a region with a modifier in order to create a new region containing
2733 * the section of the clicked region that lies within the time range.
2736 Editor::start_selection_grab (ArdourCanvas::Item* /*item*/, GdkEvent* event)
2738 if (clicked_regionview == 0) {
2742 /* lets try to create new Region for the selection */
2744 vector<boost::shared_ptr<Region> > new_regions;
2745 create_region_from_selection (new_regions);
2747 if (new_regions.empty()) {
2751 /* XXX fix me one day to use all new regions */
2753 boost::shared_ptr<Region> region (new_regions.front());
2755 /* add it to the current stream/playlist.
2757 tricky: the streamview for the track will add a new regionview. we will
2758 catch the signal it sends when it creates the regionview to
2759 set the regionview we want to then drag.
2762 latest_regionviews.clear();
2763 sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
2765 /* A selection grab currently creates two undo/redo operations, one for
2766 creating the new region and another for moving it.
2769 begin_reversible_command (Operations::selection_grab);
2771 boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
2773 playlist->clear_changes ();
2774 clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
2775 _session->add_command(new StatefulDiffCommand (playlist));
2777 commit_reversible_command ();
2781 if (latest_regionviews.empty()) {
2782 /* something went wrong */
2786 /* we need to deselect all other regionviews, and select this one
2787 i'm ignoring undo stuff, because the region creation will take care of it
2789 selection->set (latest_regionviews);
2791 _drags->set (new RegionMoveDrag (this, latest_regionviews.front()->get_canvas_group(), latest_regionviews.front(), latest_regionviews, false, false), event);
2797 if (_drags->active ()) {
2800 selection->clear ();
2805 Editor::set_internal_edit (bool yn)
2807 if (_internal_editing == yn) {
2811 _internal_editing = yn;
2814 pre_internal_mouse_mode = mouse_mode;
2815 pre_internal_snap_type = _snap_type;
2816 pre_internal_snap_mode = _snap_mode;
2818 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2819 (*i)->enter_internal_edit_mode ();
2822 set_snap_to (internal_snap_type);
2823 set_snap_mode (internal_snap_mode);
2827 internal_snap_mode = _snap_mode;
2828 internal_snap_type = _snap_type;
2830 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2831 (*i)->leave_internal_edit_mode ();
2834 if (mouse_mode == MouseDraw && pre_internal_mouse_mode != MouseDraw) {
2835 /* we were drawing .. flip back to something sensible */
2836 set_mouse_mode (pre_internal_mouse_mode);
2839 set_snap_to (pre_internal_snap_type);
2840 set_snap_mode (pre_internal_snap_mode);
2843 set_canvas_cursor ();
2846 /** Update _join_object_range_state which indicate whether we are over the top or bottom half of a region view,
2847 * used by the `join object/range' tool mode.
2850 Editor::update_join_object_range_location (double /*x*/, double y)
2852 /* XXX: actually, this decides based on whether the mouse is in the top
2853 or bottom half of a the waveform part RouteTimeAxisView;
2855 Note that entered_{track,regionview} is not always setup (e.g. if
2856 the mouse is over a TimeSelection), and to get a Region
2857 that we're over requires searching the playlist.
2860 if ( !get_smart_mode() ) {
2861 _join_object_range_state = JOIN_OBJECT_RANGE_NONE;
2865 if (mouse_mode == MouseObject) {
2866 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2867 } else if (mouse_mode == MouseRange) {
2868 _join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
2871 /* XXX: maybe we should make entered_track work in all cases, rather than resorting to this */
2872 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
2876 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
2881 rtv->canvas_display()->canvas_to_item (cx, cy);
2883 double const c = cy / (rtv->view()->child_height() - TimeAxisViewItem::NAME_HIGHLIGHT_SIZE);
2885 _join_object_range_state = c <= 0.5 ? JOIN_OBJECT_RANGE_RANGE : JOIN_OBJECT_RANGE_OBJECT;
2891 Editor::effective_mouse_mode () const
2893 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
2895 } else if (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE) {
2903 Editor::remove_midi_note (ArdourCanvas::Item* item, GdkEvent *)
2905 NoteBase* e = reinterpret_cast<NoteBase*> (item->get_data ("notebase"));
2908 e->region_view().delete_note (e->note ());
2912 Editor::set_canvas_cursor_for_region_view (double x, RegionView* rv)
2914 /* XXX: this check should not be necessary */
2921 ArdourCanvas::Group* g = rv->get_canvas_group ();
2922 ArdourCanvas::Group* p = g->parent ();
2924 /* Compute x in region view parent coordinates */
2926 p->canvas_to_item (x, dy);
2928 boost::optional<ArdourCanvas::Rect> item_bbox = g->bounding_box ();
2930 ArdourCanvas::Rect parent_bbox = g->item_to_parent (item_bbox.get ());
2932 /* Halfway across the region */
2933 double const h = (parent_bbox.x0 + parent_bbox.x1) / 2;
2935 Trimmable::CanTrim ct = rv->region()->can_trim ();
2937 if (ct & Trimmable::FrontTrimEarlier) {
2938 set_canvas_cursor (_cursors->left_side_trim, true);
2940 set_canvas_cursor (_cursors->left_side_trim_right_only, true);
2943 if (ct & Trimmable::EndTrimLater) {
2944 set_canvas_cursor (_cursors->right_side_trim, true);
2946 set_canvas_cursor (_cursors->right_side_trim_left_only, true);
2951 /** Obtain the pointer position in canvas coordinates */
2953 Editor::get_pointer_position (double& x, double& y) const
2956 _track_canvas->get_pointer (px, py);
2957 _track_canvas->window_to_canvas (px, py, x, y);