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_frame (&event, 0, 0);
135 Editor::window_event_frame (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_frame (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);
349 if (tact->get_active())
350 m = MouseObject; //Smart mode turned to ON, force editing to Object mode
352 set_mouse_mode(m, true); //call this so the button styles can get updated
356 Editor::set_mouse_mode (MouseMode m, bool force)
358 if (_drags->active ()) {
362 if (!force && m == mouse_mode) {
366 Glib::RefPtr<Action> act;
370 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-range"));
374 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-object"));
378 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-draw"));
382 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-gain"));
386 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-zoom"));
390 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-timefx"));
394 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-audition"));
400 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
403 /* go there and back to ensure that the toggled handler is called to set up mouse_mode */
404 tact->set_active (false);
405 tact->set_active (true);
407 //NOTE: this will result in a call to mouse_mode_toggled which does the heavy lifting
411 Editor::mouse_mode_toggled (MouseMode m)
413 Glib::RefPtr<Action> act;
414 Glib::RefPtr<ToggleAction> tact;
418 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-range"));
422 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-object"));
426 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-draw"));
430 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-gain"));
434 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-zoom"));
438 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-timefx"));
442 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-audition"));
448 tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
451 if (!tact->get_active()) {
452 /* this was just the notification that the old mode has been
453 * left. we'll get called again with the new mode active in a
461 act = ActionManager::get_action (X_("MouseMode"), X_("toggle-internal-edit"));
462 tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
463 tact->set_active (true);
469 if (_session && mouse_mode == MouseAudition) {
470 /* stop transport and reset default speed to avoid oddness with
472 _session->request_transport_speed (0.0, true);
479 //TODO: set button styles for smart buttons
481 if ( smart_mode_action->get_active() ) {
482 if( mouse_mode == MouseObject ) { //smart active and object active
483 smart_mode_button.set_active(1);
484 smart_mode_button.set_name("smart mode button");
485 mouse_move_button.set_name("smart mode button");
486 } else { //smart active but object inactive
487 smart_mode_button.set_active(0);
488 smart_mode_button.set_name("smart mode button");
489 mouse_move_button.set_name("mouse mode button");
492 smart_mode_button.set_active(0);
493 smart_mode_button.set_name("mouse mode button");
494 mouse_move_button.set_name("mouse mode button");
498 set_canvas_cursor ();
499 set_gain_envelope_visibility ();
501 MouseModeChanged (); /* EMIT SIGNAL */
505 Editor::step_mouse_mode (bool next)
507 switch (current_mouse_mode()) {
510 if (Profile->get_sae()) {
511 set_mouse_mode (MouseZoom);
513 set_mouse_mode (MouseRange);
516 set_mouse_mode (MouseTimeFX);
521 if (next) set_mouse_mode (MouseDraw);
522 else set_mouse_mode (MouseObject);
526 if (next) set_mouse_mode (MouseZoom);
527 else set_mouse_mode (MouseRange);
532 if (Profile->get_sae()) {
533 set_mouse_mode (MouseTimeFX);
535 set_mouse_mode (MouseGain);
538 if (Profile->get_sae()) {
539 set_mouse_mode (MouseObject);
541 set_mouse_mode (MouseDraw);
547 if (next) set_mouse_mode (MouseTimeFX);
548 else set_mouse_mode (MouseZoom);
553 set_mouse_mode (MouseAudition);
555 if (Profile->get_sae()) {
556 set_mouse_mode (MouseZoom);
558 set_mouse_mode (MouseGain);
564 if (next) set_mouse_mode (MouseObject);
565 else set_mouse_mode (MouseTimeFX);
571 Editor::toggle_internal_editing_from_double_click (GdkEvent* event)
573 if (_drags->active()) {
574 _drags->end_grab (event);
577 ActionManager::do_action ("MouseMode", "toggle-internal-edit");
579 /* prevent reversion of edit cursor on button release */
581 pre_press_cursor = 0;
587 Editor::button_selection (ArdourCanvas::Item* /*item*/, GdkEvent* event, ItemType item_type)
589 /* in object/audition/timefx/gain-automation mode,
590 any button press sets the selection if the object
591 can be selected. this is a bit of hack, because
592 we want to avoid this if the mouse operation is a
595 note: not dbl-click or triple-click
597 Also note that there is no region selection in internal edit mode, otherwise
598 for operations operating on the selection (e.g. cut) it is not obvious whether
599 to cut notes or regions.
602 if (((mouse_mode != MouseObject) &&
603 (mouse_mode != MouseAudition || item_type != RegionItem) &&
604 (mouse_mode != MouseTimeFX || item_type != RegionItem) &&
605 (mouse_mode != MouseGain) &&
606 (mouse_mode != MouseDraw)) ||
607 ((event->type != GDK_BUTTON_PRESS && event->type != GDK_BUTTON_RELEASE) || event->button.button > 3) ||
608 (internal_editing() && mouse_mode != MouseTimeFX)) {
613 if (event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) {
615 if ((event->button.state & Keyboard::RelevantModifierKeyMask) && event->button.button != 1) {
617 /* almost no selection action on modified button-2 or button-3 events */
619 if (item_type != RegionItem && event->button.button != 2) {
625 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
626 bool press = (event->type == GDK_BUTTON_PRESS);
630 if (!get_smart_mode() || (_join_object_range_state != JOIN_OBJECT_RANGE_RANGE)) {
632 if (mouse_mode != MouseRange) {
633 set_selected_regionview_from_click (press, op);
635 /* don't change the selection unless the
636 clicked track is not currently selected. if
637 so, "collapse" the selection to just this
640 if (!selection->selected (clicked_axisview)) {
641 set_selected_track_as_side_effect (Selection::Set);
645 if (mouse_mode != MouseRange) {
646 set_selected_regionview_from_click (press, op);
652 case RegionViewNameHighlight:
654 case LeftFrameHandle:
655 case RightFrameHandle:
656 if ( mouse_mode != MouseRange ) {
657 set_selected_regionview_from_click (press, op);
658 } else if (event->type == GDK_BUTTON_PRESS) {
659 set_selected_track_as_side_effect (op);
663 case FadeInHandleItem:
665 case FadeOutHandleItem:
667 case StartCrossFadeItem:
668 case EndCrossFadeItem:
669 if ( mouse_mode != MouseRange ) {
670 set_selected_regionview_from_click (press, op);
671 } else if (event->type == GDK_BUTTON_PRESS) {
672 set_selected_track_as_side_effect (op);
676 case ControlPointItem:
677 set_selected_track_as_side_effect (op);
678 if ( mouse_mode != MouseRange ) {
679 set_selected_control_point_from_click (press, op);
684 /* for context click, select track */
685 if (event->button.button == 3) {
686 selection->clear_tracks ();
687 set_selected_track_as_side_effect (op);
691 case AutomationTrackItem:
692 set_selected_track_as_side_effect (op);
701 Editor::button_press_handler_1 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
703 /* single mouse clicks on any of these item types operate
704 independent of mouse mode, mostly because they are
705 not on the main track canvas or because we want
710 case PlayheadCursorItem:
711 _drags->set (new CursorDrag (this, *playhead_cursor, true), event);
715 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
716 hide_marker (item, event);
718 _drags->set (new MarkerDrag (this, item), event);
722 case TempoMarkerItem:
724 TempoMarker* m = reinterpret_cast<TempoMarker*> (item->get_data ("marker"));
726 if (m->tempo().movable ()) {
728 new TempoMarkerDrag (
731 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
741 case MeterMarkerItem:
743 MeterMarker* m = reinterpret_cast<MeterMarker*> (item->get_data ("marker"));
745 if (m->meter().movable ()) {
747 new MeterMarkerDrag (
750 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
761 _drags->set (new VideoTimeLineDrag (this, item), event);
768 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
769 _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
775 case RangeMarkerBarItem:
776 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
777 _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
779 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateRangeMarker), event);
784 case CdMarkerBarItem:
785 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
786 _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
788 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateCDMarker), event);
793 case TransportMarkerBarItem:
794 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
795 _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
797 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateTransportMarker), event);
806 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
807 /* special case: allow trim of range selections in joined object mode;
808 in theory eff should equal MouseRange in this case, but it doesn't
809 because entering the range selection canvas item results in entered_regionview
810 being set to 0, so update_join_object_range_location acts as if we aren't
813 if (item_type == StartSelectionTrimItem) {
814 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
815 } else if (item_type == EndSelectionTrimItem) {
816 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
820 Editing::MouseMode eff = effective_mouse_mode ();
822 /* special case: allow drag of region fade in/out in object mode with join object/range enabled */
823 if (item_type == FadeInHandleItem || item_type == FadeOutHandleItem) {
827 /* there is no Range mode when in internal edit mode */
828 if (eff == MouseRange && internal_editing()) {
835 case StartSelectionTrimItem:
836 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
839 case EndSelectionTrimItem:
840 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
844 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
845 start_selection_grab (item, event);
847 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
848 /* grab selection for moving */
849 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionMove), event);
851 double const y = event->button.y;
852 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
854 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
855 if ( get_smart_mode() && atv) {
856 /* smart "join" mode: drag automation */
857 _drags->set (new AutomationRangeDrag (this, atv, selection->time), event, _cursors->up_down);
859 /* this was debated, but decided the more common action was to
860 make a new selection */
861 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
868 if (internal_editing()) {
869 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
870 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
874 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::RangeSelectModifier)) {
875 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionExtend), event);
877 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
883 case RegionViewNameHighlight:
884 if (!clicked_regionview->region()->locked()) {
885 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
891 if (!internal_editing()) {
892 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::RangeSelectModifier)) {
893 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionExtend), event);
895 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
905 if (internal_editing()) {
906 /* trim notes if we're in internal edit mode and near the ends of the note */
907 NoteBase* cn = reinterpret_cast<NoteBase*>(item->get_data ("notebase"));
909 if (cn && cn->big_enough_to_trim() && cn->mouse_near_ends()) {
910 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
912 _drags->set (new NoteDrag (this, item), event);
918 if (internal_editing()) {
919 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
920 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
934 if (internal_editing()) {
935 NoteBase* cn = reinterpret_cast<NoteBase*> (item->get_data ("notebase"));
937 if (cn->mouse_near_ends()) {
938 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
940 _drags->set (new NoteDrag (this, item), event);
950 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)) &&
951 event->type == GDK_BUTTON_PRESS) {
953 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
955 } else if (event->type == GDK_BUTTON_PRESS) {
958 case FadeInHandleItem:
960 _drags->set (new FadeInDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), selection->regions), event, _cursors->fade_in);
964 case FadeOutHandleItem:
966 _drags->set (new FadeOutDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), selection->regions), event, _cursors->fade_out);
970 case StartCrossFadeItem:
971 case EndCrossFadeItem:
972 /* we might allow user to grab inside the fade to trim a region with preserve_fade_anchor. for not this is not fully implemented */
973 // if (!clicked_regionview->region()->locked()) {
974 // _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer(), true), event);
979 case FeatureLineItem:
981 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier)) {
982 remove_transient(item);
986 _drags->set (new FeatureLineDrag (this, item), event);
992 if (dynamic_cast<AutomationRegionView*> (clicked_regionview)) {
993 /* click on an automation region view; do nothing here and let the ARV's signal handler
999 if (internal_editing ()) {
1003 /* click on a normal region view */
1004 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
1005 add_region_copy_drag (item, event, clicked_regionview);
1006 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
1007 add_region_brush_drag (item, event, clicked_regionview);
1009 add_region_drag (item, event, clicked_regionview);
1013 // if (!internal_editing() && (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE && !selection->regions.empty())) {
1014 // _drags->add (new SelectionDrag (this, clicked_axisview->get_selection_rect (clicked_selection)->rect, SelectionDrag::SelectionMove));
1017 _drags->start_grab (event);
1021 case RegionViewNameHighlight:
1022 case LeftFrameHandle:
1023 case RightFrameHandle:
1024 if (!clicked_regionview->region()->locked()) {
1025 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1030 case RegionViewName:
1032 /* rename happens on edit clicks */
1033 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer()), event);
1038 case ControlPointItem:
1039 _drags->set (new ControlPointDrag (this, item), event);
1043 case AutomationLineItem:
1044 _drags->set (new LineDrag (this, item), event);
1049 if (internal_editing()) {
1050 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
1051 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
1055 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
1059 case AutomationTrackItem:
1061 TimeAxisView* parent = clicked_axisview->get_parent ();
1062 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (clicked_axisview);
1064 if (parent && dynamic_cast<MidiTimeAxisView*> (parent) && atv->show_regions ()) {
1066 RouteTimeAxisView* p = dynamic_cast<RouteTimeAxisView*> (parent);
1068 boost::shared_ptr<Playlist> pl = p->track()->playlist ();
1069 if (pl->n_regions() == 0) {
1070 /* Parent has no regions; create one so that we have somewhere to put automation */
1071 _drags->set (new RegionCreateDrag (this, item, parent), event);
1073 /* See if there's a region before the click that we can extend, and extend it if so */
1074 framepos_t const t = canvas_event_frame (event);
1075 boost::shared_ptr<Region> prev = pl->find_next_region (t, End, -1);
1077 _drags->set (new RegionCreateDrag (this, item, parent), event);
1079 prev->set_length (t - prev->position ());
1083 /* rubberband drag to select automation points */
1084 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
1091 if ( get_smart_mode() ) {
1092 /* we're in "smart" joined mode, and we've clicked on a Selection */
1093 double const y = event->button.y;
1094 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
1096 /* if we're over an automation track, start a drag of its data */
1097 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
1099 _drags->set (new AutomationRangeDrag (this, atv, selection->time), event, _cursors->up_down);
1102 /* if we're over a track and a region, and in the `object' part of a region,
1103 put a selection around the region and drag both
1105 /* RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1106 if (rtv && _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
1107 boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track> (rtv->route ());
1109 boost::shared_ptr<Playlist> pl = t->playlist ();
1112 boost::shared_ptr<Region> r = pl->top_region_at (canvas_event_frame (event));
1114 RegionView* rv = rtv->view()->find_view (r);
1115 clicked_selection = select_range (rv->region()->position(),
1116 rv->region()->last_frame()+1);
1117 _drags->add (new SelectionDrag (this, item, SelectionDrag::SelectionMove));
1118 list<RegionView*> rvs;
1120 _drags->add (new RegionMoveDrag (this, item, rv, rvs, false, false));
1121 _drags->start_grab (event);
1145 switch (item_type) {
1147 _drags->set (new LineDrag (this, item), event);
1150 case ControlPointItem:
1151 _drags->set (new ControlPointDrag (this, item), event);
1157 AudioRegionView* arv = dynamic_cast<AudioRegionView *> (clicked_regionview);
1159 _drags->set (new AutomationRangeDrag (this, arv, selection->time), event, _cursors->up_down);
1160 _drags->start_grab (event);
1166 case AutomationLineItem:
1167 _drags->set (new LineDrag (this, item), event);
1177 if (event->type == GDK_BUTTON_PRESS) {
1178 _drags->set (new MouseZoomDrag (this, item), event);
1185 if (internal_editing() && item_type == NoteItem) {
1186 /* drag notes if we're in internal edit mode */
1187 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
1189 } else if (clicked_regionview) {
1191 _drags->set (new TimeFXDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1197 _drags->set (new ScrubDrag (this, item), event);
1198 scrub_reversals = 0;
1199 scrub_reverse_distance = 0;
1200 last_scrub_x = event->button.x;
1201 scrubbing_direction = 0;
1202 set_canvas_cursor (_cursors->transparent);
1214 Editor::button_press_handler_2 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1216 Editing::MouseMode const eff = effective_mouse_mode ();
1219 switch (item_type) {
1221 if (internal_editing ()) {
1222 /* no region drags in internal edit mode */
1226 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
1227 add_region_copy_drag (item, event, clicked_regionview);
1229 add_region_drag (item, event, clicked_regionview);
1231 _drags->start_grab (event);
1234 case ControlPointItem:
1235 _drags->set (new ControlPointDrag (this, item), event);
1243 switch (item_type) {
1244 case RegionViewNameHighlight:
1245 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1249 case LeftFrameHandle:
1250 case RightFrameHandle:
1251 if (!internal_editing ()) {
1252 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1257 case RegionViewName:
1258 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer()), event);
1272 /* relax till release */
1278 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1279 temporal_zoom_to_frame (false, canvas_event_frame (event));
1281 temporal_zoom_to_frame (true, canvas_event_frame(event));
1294 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1296 if (event->type == GDK_2BUTTON_PRESS) {
1297 _drags->mark_double_click ();
1301 if (event->type != GDK_BUTTON_PRESS) {
1302 if (event->type == GDK_2BUTTON_PRESS) {
1303 gdk_pointer_ungrab (GDK_CURRENT_TIME);
1304 return button_double_click_handler (item, event, item_type);
1309 Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->_track_canvas_viewport->get_window();
1311 if (canvas_window) {
1312 Glib::RefPtr<const Gdk::Window> pointer_window;
1315 Gdk::ModifierType mask;
1317 pointer_window = canvas_window->get_pointer (x, y, mask);
1319 if (pointer_window == _track_canvas->get_window()) {
1320 _track_canvas->window_to_canvas (x, y, wx, wy);
1324 pre_press_cursor = current_canvas_cursor;
1326 _track_canvas->grab_focus();
1328 if (_session && _session->actively_recording()) {
1332 if (internal_editing()) {
1333 bool leave_internal_edit_mode = false;
1335 switch (item_type) {
1340 if (!dynamic_cast<MidiRegionView*> (clicked_regionview) && !dynamic_cast<AutomationRegionView*> (clicked_regionview)) {
1341 leave_internal_edit_mode = true;
1345 case PlayheadCursorItem:
1347 case TempoMarkerItem:
1348 case MeterMarkerItem:
1352 case RangeMarkerBarItem:
1353 case CdMarkerBarItem:
1354 case TransportMarkerBarItem:
1356 /* button press on these events never does anything to
1357 change the editing mode.
1365 if (leave_internal_edit_mode) {
1366 ActionManager::do_action ("MouseMode", "toggle-internal-edit");
1370 button_selection (item, event, item_type);
1372 if (!_drags->active () &&
1373 (Keyboard::is_delete_event (&event->button) ||
1374 Keyboard::is_context_menu_event (&event->button) ||
1375 Keyboard::is_edit_event (&event->button))) {
1377 /* handled by button release */
1381 //not rolling, range mode click + join_play_range : locate the PH here
1382 if ( !_drags->active () && !_session->transport_rolling() && ( effective_mouse_mode() == MouseRange ) && Config->get_always_play_range() ) {
1383 framepos_t where = canvas_event_frame (event);
1385 _session->request_locate (where, false);
1388 switch (event->button.button) {
1390 return button_press_handler_1 (item, event, item_type);
1394 return button_press_handler_2 (item, event, item_type);
1401 return button_press_dispatch (&event->button);
1410 Editor::button_press_dispatch (GdkEventButton* ev)
1412 /* this function is intended only for buttons 4 and above.
1415 Gtkmm2ext::MouseButton b (ev->state, ev->button);
1416 return button_bindings->activate (b, Gtkmm2ext::Bindings::Press);
1420 Editor::button_release_dispatch (GdkEventButton* ev)
1422 /* this function is intended only for buttons 4 and above.
1425 Gtkmm2ext::MouseButton b (ev->state, ev->button);
1426 return button_bindings->activate (b, Gtkmm2ext::Bindings::Release);
1430 Editor::button_double_click_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type) {
1432 if (event->button.button != 1) {
1436 switch (item_type) {
1439 rv = clicked_regionview;
1440 rv->show_region_editor ();
1443 case PlayheadCursorItem:
1446 case RangeMarkerBarItem:
1447 case CdMarkerBarItem:
1449 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1452 rename_marker (marker);
1454 case TempoMarkerItem:
1455 edit_tempo_marker (item);
1457 case MeterMarkerItem:
1458 edit_meter_marker (item);
1463 case TransportMarkerBarItem:
1476 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1478 framepos_t where = canvas_event_frame (event);
1479 AutomationTimeAxisView* atv = 0;
1481 if (pre_press_cursor) {
1482 set_canvas_cursor (pre_press_cursor);
1483 pre_press_cursor = 0;
1486 /* no action if we're recording */
1488 if (_session && _session->actively_recording()) {
1492 /* see if we're finishing a drag */
1494 bool were_dragging = false;
1495 if (_drags->active ()) {
1496 bool const r = _drags->end_grab (event);
1498 /* grab dragged, so do nothing else */
1502 were_dragging = true;
1505 update_region_layering_order_editor ();
1507 /* edit events get handled here */
1509 if (!_drags->active () && Keyboard::is_edit_event (&event->button)) {
1510 switch (item_type) {
1512 show_region_properties ();
1515 case TempoMarkerItem:
1516 edit_tempo_marker (item);
1519 case MeterMarkerItem:
1520 edit_meter_marker (item);
1523 case RegionViewName:
1524 if (clicked_regionview->name_active()) {
1525 return mouse_rename_region (item, event);
1529 case ControlPointItem:
1530 edit_control_point (item);
1539 /* context menu events get handled here */
1540 if (Keyboard::is_context_menu_event (&event->button)) {
1542 context_click_event = *event;
1544 if (!_drags->active ()) {
1546 /* no matter which button pops up the context menu, tell the menu
1547 widget to use button 1 to drive menu selection.
1550 switch (item_type) {
1552 case FadeInHandleItem:
1554 case FadeOutHandleItem:
1555 popup_fade_context_menu (1, event->button.time, item, item_type);
1558 case StartCrossFadeItem:
1559 popup_xfade_in_context_menu (1, event->button.time, item, item_type);
1562 case EndCrossFadeItem:
1563 popup_xfade_out_context_menu (1, event->button.time, item, item_type);
1567 popup_track_context_menu (1, event->button.time, item_type, false);
1571 case RegionViewNameHighlight:
1572 case LeftFrameHandle:
1573 case RightFrameHandle:
1574 case RegionViewName:
1575 popup_track_context_menu (1, event->button.time, item_type, false);
1579 popup_track_context_menu (1, event->button.time, item_type, true);
1582 case AutomationTrackItem:
1583 popup_track_context_menu (1, event->button.time, item_type, false);
1587 case RangeMarkerBarItem:
1588 case TransportMarkerBarItem:
1589 case CdMarkerBarItem:
1593 popup_ruler_menu (where, item_type);
1597 marker_context_menu (&event->button, item);
1600 case TempoMarkerItem:
1601 tempo_or_meter_marker_context_menu (&event->button, item);
1604 case MeterMarkerItem:
1605 tempo_or_meter_marker_context_menu (&event->button, item);
1608 case CrossfadeViewItem:
1609 popup_track_context_menu (1, event->button.time, item_type, false);
1612 case ControlPointItem:
1613 popup_control_point_context_menu (item, event);
1624 /* delete events get handled here */
1626 Editing::MouseMode const eff = effective_mouse_mode ();
1628 if (!_drags->active () && Keyboard::is_delete_event (&event->button)) {
1630 switch (item_type) {
1631 case TempoMarkerItem:
1632 remove_tempo_marker (item);
1635 case MeterMarkerItem:
1636 remove_meter_marker (item);
1640 remove_marker (*item, event);
1644 if (eff == MouseObject) {
1645 remove_clicked_region ();
1649 case ControlPointItem:
1650 remove_control_point (item);
1654 remove_midi_note (item, event);
1663 switch (event->button.button) {
1666 switch (item_type) {
1667 /* see comments in button_press_handler */
1668 case PlayheadCursorItem:
1671 case AutomationLineItem:
1672 case StartSelectionTrimItem:
1673 case EndSelectionTrimItem:
1677 if (!_dragging_playhead) {
1678 snap_to_with_modifier (where, event, 0, true);
1679 mouse_add_new_marker (where);
1683 case CdMarkerBarItem:
1684 if (!_dragging_playhead) {
1685 // if we get here then a dragged range wasn't done
1686 snap_to_with_modifier (where, event, 0, true);
1687 mouse_add_new_marker (where, true);
1692 if (!_dragging_playhead) {
1693 snap_to_with_modifier (where, event);
1694 mouse_add_new_tempo_event (where);
1699 if (!_dragging_playhead) {
1700 mouse_add_new_meter_event (pixel_to_sample (event->button.x));
1711 switch (item_type) {
1712 case AutomationTrackItem:
1713 atv = dynamic_cast<AutomationTimeAxisView*>(clicked_axisview);
1715 atv->add_automation_event (event, where, event->button.y);
1725 switch (item_type) {
1728 /* check that we didn't drag before releasing, since
1729 its really annoying to create new control
1730 points when doing this.
1732 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (clicked_regionview);
1733 if (!were_dragging && arv) {
1734 arv->add_gain_point_event (item, event);
1740 case AutomationTrackItem:
1741 dynamic_cast<AutomationTimeAxisView*>(clicked_axisview)->
1742 add_automation_event (event, where, event->button.y);
1751 set_canvas_cursor (current_canvas_cursor);
1752 if (scrubbing_direction == 0) {
1753 /* no drag, just a click */
1754 switch (item_type) {
1756 play_selected_region ();
1762 /* make sure we stop */
1763 _session->request_transport_speed (0.0);
1772 /* do any (de)selection operations that should occur on button release */
1773 button_selection (item, event, item_type);
1782 switch (item_type) {
1784 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1786 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier|Keyboard::SecondaryModifier))) {
1789 // Button2 click is unused
1804 // x_style_paste (where, 1.0);
1825 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1832 switch (item_type) {
1833 case ControlPointItem:
1834 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1835 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1836 cp->set_visible (true);
1840 at_y = cp->get_y ();
1841 cp->i2w (at_x, at_y);
1845 fraction = 1.0 - (cp->get_y() / cp->line().height());
1847 if (is_drawable() && !_drags->active ()) {
1848 set_canvas_cursor (_cursors->fader);
1851 _verbose_cursor->set (cp->line().get_verbose_cursor_string (fraction), at_x, at_y);
1852 _verbose_cursor->show ();
1857 if (mouse_mode == MouseGain) {
1858 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1860 line->set_outline_color (ARDOUR_UI::config()->get_canvasvar_EnteredGainLine());
1862 if (is_drawable()) {
1863 set_canvas_cursor (_cursors->fader);
1868 case AutomationLineItem:
1869 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1870 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1872 line->set_outline_color (ARDOUR_UI::config()->get_canvasvar_EnteredAutomationLine());
1874 if (is_drawable()) {
1875 set_canvas_cursor (_cursors->fader);
1880 case RegionViewNameHighlight:
1881 if (is_drawable() && effective_mouse_mode() == MouseObject && entered_regionview) {
1882 set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1883 _over_region_trim_target = true;
1887 case LeftFrameHandle:
1888 case RightFrameHandle:
1889 if (is_drawable() && effective_mouse_mode() == MouseObject && !internal_editing() && entered_regionview) {
1890 set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1894 case StartSelectionTrimItem:
1895 if (is_drawable()) {
1896 set_canvas_cursor (_cursors->left_side_trim);
1899 case EndSelectionTrimItem:
1900 if (is_drawable()) {
1901 set_canvas_cursor (_cursors->right_side_trim);
1905 case PlayheadCursorItem:
1906 if (is_drawable()) {
1907 switch (_edit_point) {
1909 set_canvas_cursor (_cursors->grabber_edit_point);
1912 set_canvas_cursor (_cursors->grabber);
1918 case RegionViewName:
1920 /* when the name is not an active item, the entire name highlight is for trimming */
1922 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1923 if (mouse_mode == MouseObject && is_drawable()) {
1924 set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1925 _over_region_trim_target = true;
1931 case AutomationTrackItem:
1932 if (is_drawable()) {
1933 Gdk::Cursor *cursor;
1934 switch (mouse_mode) {
1936 cursor = _cursors->selector;
1939 cursor = _cursors->zoom_in;
1942 cursor = _cursors->cross_hair;
1946 set_canvas_cursor (cursor);
1948 AutomationTimeAxisView* atv;
1949 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1950 clear_entered_track = false;
1951 set_entered_track (atv);
1957 case RangeMarkerBarItem:
1958 case TransportMarkerBarItem:
1959 case CdMarkerBarItem:
1962 if (is_drawable()) {
1963 set_canvas_cursor (_cursors->timebar);
1968 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1971 entered_marker = marker;
1972 marker->set_color_rgba (ARDOUR_UI::config()->get_canvasvar_EnteredMarker());
1974 case MeterMarkerItem:
1975 case TempoMarkerItem:
1976 if (is_drawable()) {
1977 set_canvas_cursor (_cursors->timebar);
1981 case FadeInHandleItem:
1982 if (mouse_mode == MouseObject && !internal_editing()) {
1983 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
1985 RegionView* rv = static_cast<RegionView*>(item->get_data ("regionview"));
1986 rect->set_fill_color (rv->get_fill_color());
1987 set_canvas_cursor (_cursors->fade_in);
1992 case FadeOutHandleItem:
1993 if (mouse_mode == MouseObject && !internal_editing()) {
1994 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
1996 RegionView* rv = static_cast<RegionView*>(item->get_data ("regionview"));
1997 rect->set_fill_color (rv->get_fill_color ());
1998 set_canvas_cursor (_cursors->fade_out);
2002 case FeatureLineItem:
2004 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
2005 line->set_outline_color (0xFF0000FF);
2010 if ( get_smart_mode() ) {
2011 set_canvas_cursor ();
2019 /* second pass to handle entered track status in a comprehensible way.
2022 switch (item_type) {
2024 case AutomationLineItem:
2025 case ControlPointItem:
2026 /* these do not affect the current entered track state */
2027 clear_entered_track = false;
2030 case AutomationTrackItem:
2031 /* handled above already */
2035 set_entered_track (0);
2043 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent*, ItemType item_type)
2053 switch (item_type) {
2054 case ControlPointItem:
2055 cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
2056 if (cp->line().the_list()->interpolation() != AutomationList::Discrete) {
2057 if (cp->line().npoints() > 1 && !cp->get_selected()) {
2058 cp->set_visible (false);
2062 if (is_drawable()) {
2063 set_canvas_cursor (current_canvas_cursor);
2066 _verbose_cursor->hide ();
2069 case RegionViewNameHighlight:
2070 case LeftFrameHandle:
2071 case RightFrameHandle:
2072 case StartSelectionTrimItem:
2073 case EndSelectionTrimItem:
2074 case PlayheadCursorItem:
2076 _over_region_trim_target = false;
2078 if (is_drawable()) {
2079 set_canvas_cursor (current_canvas_cursor);
2084 case AutomationLineItem:
2085 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
2087 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
2089 line->set_outline_color (al->get_line_color());
2092 if (is_drawable()) {
2093 set_canvas_cursor (current_canvas_cursor);
2097 case RegionViewName:
2098 /* see enter_handler() for notes */
2099 _over_region_trim_target = false;
2101 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
2102 if (is_drawable() && mouse_mode == MouseObject) {
2103 set_canvas_cursor (current_canvas_cursor);
2108 case RangeMarkerBarItem:
2109 case TransportMarkerBarItem:
2110 case CdMarkerBarItem:
2114 if (is_drawable()) {
2115 set_canvas_cursor (current_canvas_cursor);
2120 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2124 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
2125 location_flags_changed (loc, this);
2128 case MeterMarkerItem:
2129 case TempoMarkerItem:
2131 if (is_drawable()) {
2132 set_canvas_cursor (current_canvas_cursor);
2137 case FadeInHandleItem:
2138 case FadeOutHandleItem:
2139 rv = static_cast<RegionView*>(item->get_data ("regionview"));
2141 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
2143 rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_InactiveFadeHandle());
2146 set_canvas_cursor (current_canvas_cursor);
2149 case AutomationTrackItem:
2150 if (is_drawable()) {
2151 set_canvas_cursor (current_canvas_cursor);
2152 clear_entered_track = true;
2153 Glib::signal_idle().connect (sigc::mem_fun(*this, &Editor::left_automation_track));
2156 case FeatureLineItem:
2158 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
2159 line->set_outline_color (ARDOUR_UI::config()->get_canvasvar_ZeroLine());
2171 Editor::left_automation_track ()
2173 if (clear_entered_track) {
2174 set_entered_track (0);
2175 clear_entered_track = false;
2181 Editor::scrub (framepos_t frame, double current_x)
2185 if (scrubbing_direction == 0) {
2187 _session->request_locate (frame, false);
2188 _session->request_transport_speed (0.1);
2189 scrubbing_direction = 1;
2193 if (last_scrub_x > current_x) {
2195 /* pointer moved to the left */
2197 if (scrubbing_direction > 0) {
2199 /* we reversed direction to go backwards */
2202 scrub_reverse_distance += (int) (last_scrub_x - current_x);
2206 /* still moving to the left (backwards) */
2208 scrub_reversals = 0;
2209 scrub_reverse_distance = 0;
2211 delta = 0.01 * (last_scrub_x - current_x);
2212 _session->request_transport_speed_nonzero (_session->transport_speed() - delta);
2216 /* pointer moved to the right */
2218 if (scrubbing_direction < 0) {
2219 /* we reversed direction to go forward */
2222 scrub_reverse_distance += (int) (current_x - last_scrub_x);
2225 /* still moving to the right */
2227 scrub_reversals = 0;
2228 scrub_reverse_distance = 0;
2230 delta = 0.01 * (current_x - last_scrub_x);
2231 _session->request_transport_speed_nonzero (_session->transport_speed() + delta);
2235 /* if there have been more than 2 opposite motion moves detected, or one that moves
2236 back more than 10 pixels, reverse direction
2239 if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
2241 if (scrubbing_direction > 0) {
2242 /* was forwards, go backwards */
2243 _session->request_transport_speed (-0.1);
2244 scrubbing_direction = -1;
2246 /* was backwards, go forwards */
2247 _session->request_transport_speed (0.1);
2248 scrubbing_direction = 1;
2251 scrub_reverse_distance = 0;
2252 scrub_reversals = 0;
2256 last_scrub_x = current_x;
2260 Editor::motion_handler (ArdourCanvas::Item* /*item*/, GdkEvent* event, bool from_autoscroll)
2262 _last_motion_y = event->motion.y;
2264 if (event->motion.is_hint) {
2267 /* We call this so that MOTION_NOTIFY events continue to be
2268 delivered to the canvas. We need to do this because we set
2269 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
2270 the density of the events, at the expense of a round-trip
2271 to the server. Given that this will mostly occur on cases
2272 where DISPLAY = :0.0, and given the cost of what the motion
2273 event might do, its a good tradeoff.
2276 _track_canvas->get_pointer (x, y);
2279 if (current_stepping_trackview) {
2280 /* don't keep the persistent stepped trackview if the mouse moves */
2281 current_stepping_trackview = 0;
2282 step_timeout.disconnect ();
2285 if (_session && _session->actively_recording()) {
2286 /* Sorry. no dragging stuff around while we record */
2290 JoinObjectRangeState const old = _join_object_range_state;
2291 update_join_object_range_location (event->motion.x, event->motion.y);
2293 if (!_internal_editing && _join_object_range_state != old) {
2294 set_canvas_cursor ();
2297 if (!_internal_editing && _over_region_trim_target) {
2298 set_canvas_cursor_for_region_view (event->motion.x, entered_regionview);
2301 bool handled = false;
2302 if (_drags->active ()) {
2303 handled = _drags->motion_handler (event, from_autoscroll);
2310 track_canvas_motion (event);
2315 Editor::can_remove_control_point (ArdourCanvas::Item* item)
2317 ControlPoint* control_point;
2319 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2320 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2324 AutomationLine& line = control_point->line ();
2325 if (dynamic_cast<AudioRegionGainLine*> (&line)) {
2326 /* we shouldn't remove the first or last gain point in region gain lines */
2327 if (line.is_last_point(*control_point) || line.is_first_point(*control_point)) {
2336 Editor::remove_control_point (ArdourCanvas::Item* item)
2338 if (!can_remove_control_point (item)) {
2342 ControlPoint* control_point;
2344 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2345 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2349 control_point->line().remove_point (*control_point);
2353 Editor::edit_control_point (ArdourCanvas::Item* item)
2355 ControlPoint* p = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"));
2358 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2362 ControlPointDialog d (p);
2365 if (d.run () != RESPONSE_ACCEPT) {
2369 p->line().modify_point_y (*p, d.get_y_fraction ());
2373 Editor::edit_notes (TimeAxisViewItem& tavi)
2375 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(&tavi);
2381 MidiRegionView::Selection const & s = mrv->selection();
2387 EditNoteDialog* d = new EditNoteDialog (&(*s.begin())->region_view(), s);
2391 d->signal_response().connect (sigc::bind (sigc::mem_fun (*this, &Editor::note_edit_done), d));
2395 Editor::note_edit_done (int r, EditNoteDialog* d)
2402 Editor::visible_order_range (int* low, int* high) const
2404 *low = TimeAxisView::max_order ();
2407 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
2409 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
2411 if (!rtv->hidden()) {
2413 if (*high < rtv->order()) {
2414 *high = rtv->order ();
2417 if (*low > rtv->order()) {
2418 *low = rtv->order ();
2425 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
2427 /* Either add to or set the set the region selection, unless
2428 this is an alignment click (control used)
2431 if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
2432 TimeAxisView* tv = &rv.get_time_axis_view();
2433 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
2435 if (rtv && rtv->is_track()) {
2436 speed = rtv->track()->speed();
2439 framepos_t where = get_preferred_edit_position();
2443 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
2445 align_region (rv.region(), SyncPoint, (framepos_t) (where * speed));
2447 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
2449 align_region (rv.region(), End, (framepos_t) (where * speed));
2453 align_region (rv.region(), Start, (framepos_t) (where * speed));
2460 Editor::collect_new_region_view (RegionView* rv)
2462 latest_regionviews.push_back (rv);
2466 Editor::collect_and_select_new_region_view (RegionView* rv)
2469 latest_regionviews.push_back (rv);
2473 Editor::cancel_selection ()
2475 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2476 (*i)->hide_selection ();
2479 selection->clear ();
2480 clicked_selection = 0;
2484 Editor::cancel_time_selection ()
2486 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2487 (*i)->hide_selection ();
2489 selection->time.clear ();
2490 clicked_selection = 0;
2494 Editor::point_trim (GdkEvent* event, framepos_t new_bound)
2496 RegionView* rv = clicked_regionview;
2498 /* Choose action dependant on which button was pressed */
2499 switch (event->button.button) {
2501 begin_reversible_command (_("start point trim"));
2503 if (selection->selected (rv)) {
2504 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
2505 i != selection->regions.by_layer().end(); ++i)
2507 if (!(*i)->region()->locked()) {
2508 (*i)->region()->clear_changes ();
2509 (*i)->region()->trim_front (new_bound);
2510 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2515 if (!rv->region()->locked()) {
2516 rv->region()->clear_changes ();
2517 rv->region()->trim_front (new_bound);
2518 _session->add_command(new StatefulDiffCommand (rv->region()));
2522 commit_reversible_command();
2526 begin_reversible_command (_("End point trim"));
2528 if (selection->selected (rv)) {
2530 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
2532 if (!(*i)->region()->locked()) {
2533 (*i)->region()->clear_changes();
2534 (*i)->region()->trim_end (new_bound);
2535 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2541 if (!rv->region()->locked()) {
2542 rv->region()->clear_changes ();
2543 rv->region()->trim_end (new_bound);
2544 _session->add_command (new StatefulDiffCommand (rv->region()));
2548 commit_reversible_command();
2557 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* /*event*/)
2562 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2563 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2567 Location* location = find_location_from_marker (marker, is_start);
2568 location->set_hidden (true, this);
2573 Editor::reposition_zoom_rect (framepos_t start, framepos_t end)
2575 double x1 = sample_to_pixel (start);
2576 double x2 = sample_to_pixel (end);
2577 double y2 = _full_canvas_height - 1.0;
2579 zoom_rect->set (ArdourCanvas::Rect (x1, 1.0, x2, y2));
2584 Editor::mouse_rename_region (ArdourCanvas::Item* /*item*/, GdkEvent* /*event*/)
2586 using namespace Gtkmm2ext;
2588 ArdourPrompter prompter (false);
2590 prompter.set_prompt (_("Name for region:"));
2591 prompter.set_initial_text (clicked_regionview->region()->name());
2592 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
2593 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
2594 prompter.show_all ();
2595 switch (prompter.run ()) {
2596 case Gtk::RESPONSE_ACCEPT:
2598 prompter.get_result(str);
2600 clicked_regionview->region()->set_name (str);
2609 Editor::mouse_brush_insert_region (RegionView* rv, framepos_t pos)
2611 /* no brushing without a useful snap setting */
2613 switch (_snap_mode) {
2615 return; /* can't work because it allows region to be placed anywhere */
2620 switch (_snap_type) {
2628 /* don't brush a copy over the original */
2630 if (pos == rv->region()->position()) {
2634 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
2636 if (rtv == 0 || !rtv->is_track()) {
2640 boost::shared_ptr<Playlist> playlist = rtv->playlist();
2641 double speed = rtv->track()->speed();
2643 playlist->clear_changes ();
2644 boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region(), true));
2645 playlist->add_region (new_region, (framepos_t) (pos * speed));
2646 _session->add_command (new StatefulDiffCommand (playlist));
2648 // playlist is frozen, so we have to update manually XXX this is disgusting
2650 playlist->RegionAdded (new_region); /* EMIT SIGNAL */
2654 Editor::track_height_step_timeout ()
2656 if (get_microseconds() - last_track_height_step_timestamp < 250000) {
2657 current_stepping_trackview = 0;
2664 Editor::add_region_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2666 assert (region_view);
2668 if (!region_view->region()->playlist()) {
2672 _region_motion_group->raise_to_top ();
2674 if (Config->get_edit_mode() == Splice) {
2675 _drags->add (new RegionSpliceDrag (this, item, region_view, selection->regions.by_layer()));
2677 _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), false, false));
2682 Editor::add_region_copy_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 _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), false, true));
2696 Editor::add_region_brush_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2698 assert (region_view);
2700 if (!region_view->region()->playlist()) {
2704 if (Config->get_edit_mode() == Splice) {
2708 _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), true, false));
2710 begin_reversible_command (Operations::drag_region_brush);
2713 /** Start a grab where a time range is selected, track(s) are selected, and the
2714 * user clicks and drags a region with a modifier in order to create a new region containing
2715 * the section of the clicked region that lies within the time range.
2718 Editor::start_selection_grab (ArdourCanvas::Item* /*item*/, GdkEvent* event)
2720 if (clicked_regionview == 0) {
2724 /* lets try to create new Region for the selection */
2726 vector<boost::shared_ptr<Region> > new_regions;
2727 create_region_from_selection (new_regions);
2729 if (new_regions.empty()) {
2733 /* XXX fix me one day to use all new regions */
2735 boost::shared_ptr<Region> region (new_regions.front());
2737 /* add it to the current stream/playlist.
2739 tricky: the streamview for the track will add a new regionview. we will
2740 catch the signal it sends when it creates the regionview to
2741 set the regionview we want to then drag.
2744 latest_regionviews.clear();
2745 sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
2747 /* A selection grab currently creates two undo/redo operations, one for
2748 creating the new region and another for moving it.
2751 begin_reversible_command (Operations::selection_grab);
2753 boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
2755 playlist->clear_changes ();
2756 clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
2757 _session->add_command(new StatefulDiffCommand (playlist));
2759 commit_reversible_command ();
2763 if (latest_regionviews.empty()) {
2764 /* something went wrong */
2768 /* we need to deselect all other regionviews, and select this one
2769 i'm ignoring undo stuff, because the region creation will take care of it
2771 selection->set (latest_regionviews);
2773 _drags->set (new RegionMoveDrag (this, latest_regionviews.front()->get_canvas_group(), latest_regionviews.front(), latest_regionviews, false, false), event);
2779 if (_drags->active ()) {
2782 selection->clear ();
2787 Editor::set_internal_edit (bool yn)
2789 if (_internal_editing == yn) {
2793 _internal_editing = yn;
2796 pre_internal_mouse_mode = mouse_mode;
2797 pre_internal_snap_type = _snap_type;
2798 pre_internal_snap_mode = _snap_mode;
2800 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2801 (*i)->enter_internal_edit_mode ();
2804 set_snap_to (internal_snap_type);
2805 set_snap_mode (internal_snap_mode);
2809 internal_snap_mode = _snap_mode;
2810 internal_snap_type = _snap_type;
2812 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2813 (*i)->leave_internal_edit_mode ();
2816 if (mouse_mode == MouseDraw && pre_internal_mouse_mode != MouseDraw) {
2817 /* we were drawing .. flip back to something sensible */
2818 set_mouse_mode (pre_internal_mouse_mode);
2821 set_snap_to (pre_internal_snap_type);
2822 set_snap_mode (pre_internal_snap_mode);
2825 set_canvas_cursor ();
2828 /** Update _join_object_range_state which indicate whether we are over the top or bottom half of a region view,
2829 * used by the `join object/range' tool mode.
2832 Editor::update_join_object_range_location (double /*x*/, double y)
2834 /* XXX: actually, this decides based on whether the mouse is in the top
2835 or bottom half of a the waveform part RouteTimeAxisView;
2837 Note that entered_{track,regionview} is not always setup (e.g. if
2838 the mouse is over a TimeSelection), and to get a Region
2839 that we're over requires searching the playlist.
2842 if ( !get_smart_mode() ) {
2843 _join_object_range_state = JOIN_OBJECT_RANGE_NONE;
2847 if (mouse_mode == MouseObject) {
2848 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2849 } else if (mouse_mode == MouseRange) {
2850 _join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
2853 /* XXX: maybe we should make entered_track work in all cases, rather than resorting to this */
2854 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
2858 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
2863 rtv->canvas_display()->canvas_to_item (cx, cy);
2865 double const c = cy / (rtv->view()->child_height() - TimeAxisViewItem::NAME_HIGHLIGHT_SIZE);
2867 _join_object_range_state = c <= 0.5 ? JOIN_OBJECT_RANGE_RANGE : JOIN_OBJECT_RANGE_OBJECT;
2873 Editor::effective_mouse_mode () const
2875 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
2877 } else if (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE) {
2885 Editor::remove_midi_note (ArdourCanvas::Item* item, GdkEvent *)
2887 NoteBase* e = reinterpret_cast<NoteBase*> (item->get_data ("notebase"));
2890 e->region_view().delete_note (e->note ());
2894 Editor::set_canvas_cursor_for_region_view (double x, RegionView* rv)
2896 /* XXX: this check should not be necessary */
2903 ArdourCanvas::Group* g = rv->get_canvas_group ();
2904 ArdourCanvas::Group* p = g->parent ();
2906 /* Compute x in region view parent coordinates */
2908 p->canvas_to_item (x, dy);
2910 boost::optional<ArdourCanvas::Rect> item_bbox = g->bounding_box ();
2912 ArdourCanvas::Rect parent_bbox = g->item_to_parent (item_bbox.get ());
2914 /* Halfway across the region */
2915 double const h = (parent_bbox.x0 + parent_bbox.x1) / 2;
2917 Trimmable::CanTrim ct = rv->region()->can_trim ();
2919 if (ct & Trimmable::FrontTrimEarlier) {
2920 set_canvas_cursor (_cursors->left_side_trim);
2922 set_canvas_cursor (_cursors->left_side_trim_right_only);
2925 if (ct & Trimmable::EndTrimLater) {
2926 set_canvas_cursor (_cursors->right_side_trim);
2928 set_canvas_cursor (_cursors->right_side_trim_left_only);
2933 /** Obtain the pointer position in canvas coordinates */
2935 Editor::get_pointer_position (double& x, double& y) const
2938 _track_canvas->get_pointer (px, py);
2939 _track_canvas->window_to_canvas (px, py, x, y);