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 rect->set_fill_color (0xBBBBBBAA);
1987 set_canvas_cursor (_cursors->fade_in);
1991 case FadeOutHandleItem:
1992 if (mouse_mode == MouseObject && !internal_editing()) {
1993 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
1995 rect->set_fill_color (0xBBBBBBAA);
1997 set_canvas_cursor (_cursors->fade_out);
2000 case FeatureLineItem:
2002 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
2003 line->set_outline_color (0xFF0000FF);
2008 if ( get_smart_mode() ) {
2009 set_canvas_cursor ();
2017 /* second pass to handle entered track status in a comprehensible way.
2020 switch (item_type) {
2022 case AutomationLineItem:
2023 case ControlPointItem:
2024 /* these do not affect the current entered track state */
2025 clear_entered_track = false;
2028 case AutomationTrackItem:
2029 /* handled above already */
2033 set_entered_track (0);
2041 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent*, ItemType item_type)
2051 switch (item_type) {
2052 case ControlPointItem:
2053 cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
2054 if (cp->line().the_list()->interpolation() != AutomationList::Discrete) {
2055 if (cp->line().npoints() > 1 && !cp->get_selected()) {
2056 cp->set_visible (false);
2060 if (is_drawable()) {
2061 set_canvas_cursor (current_canvas_cursor);
2064 _verbose_cursor->hide ();
2067 case RegionViewNameHighlight:
2068 case LeftFrameHandle:
2069 case RightFrameHandle:
2070 case StartSelectionTrimItem:
2071 case EndSelectionTrimItem:
2072 case PlayheadCursorItem:
2074 _over_region_trim_target = false;
2076 if (is_drawable()) {
2077 set_canvas_cursor (current_canvas_cursor);
2082 case AutomationLineItem:
2083 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
2085 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
2087 line->set_outline_color (al->get_line_color());
2090 if (is_drawable()) {
2091 set_canvas_cursor (current_canvas_cursor);
2095 case RegionViewName:
2096 /* see enter_handler() for notes */
2097 _over_region_trim_target = false;
2099 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
2100 if (is_drawable() && mouse_mode == MouseObject) {
2101 set_canvas_cursor (current_canvas_cursor);
2106 case RangeMarkerBarItem:
2107 case TransportMarkerBarItem:
2108 case CdMarkerBarItem:
2112 if (is_drawable()) {
2113 set_canvas_cursor (current_canvas_cursor);
2118 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2122 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
2123 location_flags_changed (loc, this);
2126 case MeterMarkerItem:
2127 case TempoMarkerItem:
2129 if (is_drawable()) {
2130 set_canvas_cursor (current_canvas_cursor);
2135 case FadeInHandleItem:
2136 case FadeOutHandleItem:
2137 rv = static_cast<RegionView*>(item->get_data ("regionview"));
2139 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
2141 rect->set_fill_color (rv->get_fill_color());
2144 set_canvas_cursor (current_canvas_cursor);
2147 case AutomationTrackItem:
2148 if (is_drawable()) {
2149 set_canvas_cursor (current_canvas_cursor);
2150 clear_entered_track = true;
2151 Glib::signal_idle().connect (sigc::mem_fun(*this, &Editor::left_automation_track));
2154 case FeatureLineItem:
2156 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
2157 line->set_outline_color (ARDOUR_UI::config()->get_canvasvar_ZeroLine());
2169 Editor::left_automation_track ()
2171 if (clear_entered_track) {
2172 set_entered_track (0);
2173 clear_entered_track = false;
2179 Editor::scrub (framepos_t frame, double current_x)
2183 if (scrubbing_direction == 0) {
2185 _session->request_locate (frame, false);
2186 _session->request_transport_speed (0.1);
2187 scrubbing_direction = 1;
2191 if (last_scrub_x > current_x) {
2193 /* pointer moved to the left */
2195 if (scrubbing_direction > 0) {
2197 /* we reversed direction to go backwards */
2200 scrub_reverse_distance += (int) (last_scrub_x - current_x);
2204 /* still moving to the left (backwards) */
2206 scrub_reversals = 0;
2207 scrub_reverse_distance = 0;
2209 delta = 0.01 * (last_scrub_x - current_x);
2210 _session->request_transport_speed_nonzero (_session->transport_speed() - delta);
2214 /* pointer moved to the right */
2216 if (scrubbing_direction < 0) {
2217 /* we reversed direction to go forward */
2220 scrub_reverse_distance += (int) (current_x - last_scrub_x);
2223 /* still moving to the right */
2225 scrub_reversals = 0;
2226 scrub_reverse_distance = 0;
2228 delta = 0.01 * (current_x - last_scrub_x);
2229 _session->request_transport_speed_nonzero (_session->transport_speed() + delta);
2233 /* if there have been more than 2 opposite motion moves detected, or one that moves
2234 back more than 10 pixels, reverse direction
2237 if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
2239 if (scrubbing_direction > 0) {
2240 /* was forwards, go backwards */
2241 _session->request_transport_speed (-0.1);
2242 scrubbing_direction = -1;
2244 /* was backwards, go forwards */
2245 _session->request_transport_speed (0.1);
2246 scrubbing_direction = 1;
2249 scrub_reverse_distance = 0;
2250 scrub_reversals = 0;
2254 last_scrub_x = current_x;
2258 Editor::motion_handler (ArdourCanvas::Item* /*item*/, GdkEvent* event, bool from_autoscroll)
2260 _last_motion_y = event->motion.y;
2262 if (event->motion.is_hint) {
2265 /* We call this so that MOTION_NOTIFY events continue to be
2266 delivered to the canvas. We need to do this because we set
2267 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
2268 the density of the events, at the expense of a round-trip
2269 to the server. Given that this will mostly occur on cases
2270 where DISPLAY = :0.0, and given the cost of what the motion
2271 event might do, its a good tradeoff.
2274 _track_canvas->get_pointer (x, y);
2277 if (current_stepping_trackview) {
2278 /* don't keep the persistent stepped trackview if the mouse moves */
2279 current_stepping_trackview = 0;
2280 step_timeout.disconnect ();
2283 if (_session && _session->actively_recording()) {
2284 /* Sorry. no dragging stuff around while we record */
2288 JoinObjectRangeState const old = _join_object_range_state;
2289 update_join_object_range_location (event->motion.x, event->motion.y);
2291 if (!_internal_editing && _join_object_range_state != old) {
2292 set_canvas_cursor ();
2295 if (!_internal_editing && _over_region_trim_target) {
2296 set_canvas_cursor_for_region_view (event->motion.x, entered_regionview);
2299 bool handled = false;
2300 if (_drags->active ()) {
2301 handled = _drags->motion_handler (event, from_autoscroll);
2308 track_canvas_motion (event);
2313 Editor::can_remove_control_point (ArdourCanvas::Item* item)
2315 ControlPoint* control_point;
2317 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2318 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2322 AutomationLine& line = control_point->line ();
2323 if (dynamic_cast<AudioRegionGainLine*> (&line)) {
2324 /* we shouldn't remove the first or last gain point in region gain lines */
2325 if (line.is_last_point(*control_point) || line.is_first_point(*control_point)) {
2334 Editor::remove_control_point (ArdourCanvas::Item* item)
2336 if (!can_remove_control_point (item)) {
2340 ControlPoint* control_point;
2342 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2343 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2347 control_point->line().remove_point (*control_point);
2351 Editor::edit_control_point (ArdourCanvas::Item* item)
2353 ControlPoint* p = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"));
2356 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2360 ControlPointDialog d (p);
2363 if (d.run () != RESPONSE_ACCEPT) {
2367 p->line().modify_point_y (*p, d.get_y_fraction ());
2371 Editor::edit_notes (TimeAxisViewItem& tavi)
2373 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(&tavi);
2379 MidiRegionView::Selection const & s = mrv->selection();
2385 EditNoteDialog* d = new EditNoteDialog (&(*s.begin())->region_view(), s);
2389 d->signal_response().connect (sigc::bind (sigc::mem_fun (*this, &Editor::note_edit_done), d));
2393 Editor::note_edit_done (int r, EditNoteDialog* d)
2400 Editor::visible_order_range (int* low, int* high) const
2402 *low = TimeAxisView::max_order ();
2405 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
2407 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
2409 if (!rtv->hidden()) {
2411 if (*high < rtv->order()) {
2412 *high = rtv->order ();
2415 if (*low > rtv->order()) {
2416 *low = rtv->order ();
2423 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
2425 /* Either add to or set the set the region selection, unless
2426 this is an alignment click (control used)
2429 if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
2430 TimeAxisView* tv = &rv.get_time_axis_view();
2431 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
2433 if (rtv && rtv->is_track()) {
2434 speed = rtv->track()->speed();
2437 framepos_t where = get_preferred_edit_position();
2441 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
2443 align_region (rv.region(), SyncPoint, (framepos_t) (where * speed));
2445 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
2447 align_region (rv.region(), End, (framepos_t) (where * speed));
2451 align_region (rv.region(), Start, (framepos_t) (where * speed));
2458 Editor::collect_new_region_view (RegionView* rv)
2460 latest_regionviews.push_back (rv);
2464 Editor::collect_and_select_new_region_view (RegionView* rv)
2467 latest_regionviews.push_back (rv);
2471 Editor::cancel_selection ()
2473 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2474 (*i)->hide_selection ();
2477 selection->clear ();
2478 clicked_selection = 0;
2482 Editor::cancel_time_selection ()
2484 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2485 (*i)->hide_selection ();
2487 selection->time.clear ();
2488 clicked_selection = 0;
2492 Editor::point_trim (GdkEvent* event, framepos_t new_bound)
2494 RegionView* rv = clicked_regionview;
2496 /* Choose action dependant on which button was pressed */
2497 switch (event->button.button) {
2499 begin_reversible_command (_("start point trim"));
2501 if (selection->selected (rv)) {
2502 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
2503 i != selection->regions.by_layer().end(); ++i)
2505 if (!(*i)->region()->locked()) {
2506 (*i)->region()->clear_changes ();
2507 (*i)->region()->trim_front (new_bound);
2508 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2513 if (!rv->region()->locked()) {
2514 rv->region()->clear_changes ();
2515 rv->region()->trim_front (new_bound);
2516 _session->add_command(new StatefulDiffCommand (rv->region()));
2520 commit_reversible_command();
2524 begin_reversible_command (_("End point trim"));
2526 if (selection->selected (rv)) {
2528 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
2530 if (!(*i)->region()->locked()) {
2531 (*i)->region()->clear_changes();
2532 (*i)->region()->trim_end (new_bound);
2533 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2539 if (!rv->region()->locked()) {
2540 rv->region()->clear_changes ();
2541 rv->region()->trim_end (new_bound);
2542 _session->add_command (new StatefulDiffCommand (rv->region()));
2546 commit_reversible_command();
2555 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* /*event*/)
2560 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2561 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2565 Location* location = find_location_from_marker (marker, is_start);
2566 location->set_hidden (true, this);
2571 Editor::reposition_zoom_rect (framepos_t start, framepos_t end)
2573 double x1 = sample_to_pixel (start);
2574 double x2 = sample_to_pixel (end);
2575 double y2 = _full_canvas_height - 1.0;
2577 zoom_rect->set (ArdourCanvas::Rect (x1, 1.0, x2, y2));
2582 Editor::mouse_rename_region (ArdourCanvas::Item* /*item*/, GdkEvent* /*event*/)
2584 using namespace Gtkmm2ext;
2586 ArdourPrompter prompter (false);
2588 prompter.set_prompt (_("Name for region:"));
2589 prompter.set_initial_text (clicked_regionview->region()->name());
2590 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
2591 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
2592 prompter.show_all ();
2593 switch (prompter.run ()) {
2594 case Gtk::RESPONSE_ACCEPT:
2596 prompter.get_result(str);
2598 clicked_regionview->region()->set_name (str);
2607 Editor::mouse_brush_insert_region (RegionView* rv, framepos_t pos)
2609 /* no brushing without a useful snap setting */
2611 switch (_snap_mode) {
2613 return; /* can't work because it allows region to be placed anywhere */
2618 switch (_snap_type) {
2626 /* don't brush a copy over the original */
2628 if (pos == rv->region()->position()) {
2632 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
2634 if (rtv == 0 || !rtv->is_track()) {
2638 boost::shared_ptr<Playlist> playlist = rtv->playlist();
2639 double speed = rtv->track()->speed();
2641 playlist->clear_changes ();
2642 boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region(), true));
2643 playlist->add_region (new_region, (framepos_t) (pos * speed));
2644 _session->add_command (new StatefulDiffCommand (playlist));
2646 // playlist is frozen, so we have to update manually XXX this is disgusting
2648 playlist->RegionAdded (new_region); /* EMIT SIGNAL */
2652 Editor::track_height_step_timeout ()
2654 if (get_microseconds() - last_track_height_step_timestamp < 250000) {
2655 current_stepping_trackview = 0;
2662 Editor::add_region_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2664 assert (region_view);
2666 if (!region_view->region()->playlist()) {
2670 _region_motion_group->raise_to_top ();
2672 if (Config->get_edit_mode() == Splice) {
2673 _drags->add (new RegionSpliceDrag (this, item, region_view, selection->regions.by_layer()));
2675 _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), false, false));
2680 Editor::add_region_copy_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2682 assert (region_view);
2684 if (!region_view->region()->playlist()) {
2688 _region_motion_group->raise_to_top ();
2690 _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), false, true));
2694 Editor::add_region_brush_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2696 assert (region_view);
2698 if (!region_view->region()->playlist()) {
2702 if (Config->get_edit_mode() == Splice) {
2706 _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), true, false));
2708 begin_reversible_command (Operations::drag_region_brush);
2711 /** Start a grab where a time range is selected, track(s) are selected, and the
2712 * user clicks and drags a region with a modifier in order to create a new region containing
2713 * the section of the clicked region that lies within the time range.
2716 Editor::start_selection_grab (ArdourCanvas::Item* /*item*/, GdkEvent* event)
2718 if (clicked_regionview == 0) {
2722 /* lets try to create new Region for the selection */
2724 vector<boost::shared_ptr<Region> > new_regions;
2725 create_region_from_selection (new_regions);
2727 if (new_regions.empty()) {
2731 /* XXX fix me one day to use all new regions */
2733 boost::shared_ptr<Region> region (new_regions.front());
2735 /* add it to the current stream/playlist.
2737 tricky: the streamview for the track will add a new regionview. we will
2738 catch the signal it sends when it creates the regionview to
2739 set the regionview we want to then drag.
2742 latest_regionviews.clear();
2743 sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
2745 /* A selection grab currently creates two undo/redo operations, one for
2746 creating the new region and another for moving it.
2749 begin_reversible_command (Operations::selection_grab);
2751 boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
2753 playlist->clear_changes ();
2754 clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
2755 _session->add_command(new StatefulDiffCommand (playlist));
2757 commit_reversible_command ();
2761 if (latest_regionviews.empty()) {
2762 /* something went wrong */
2766 /* we need to deselect all other regionviews, and select this one
2767 i'm ignoring undo stuff, because the region creation will take care of it
2769 selection->set (latest_regionviews);
2771 _drags->set (new RegionMoveDrag (this, latest_regionviews.front()->get_canvas_group(), latest_regionviews.front(), latest_regionviews, false, false), event);
2777 if (_drags->active ()) {
2780 selection->clear ();
2785 Editor::set_internal_edit (bool yn)
2787 if (_internal_editing == yn) {
2791 _internal_editing = yn;
2794 pre_internal_mouse_mode = mouse_mode;
2795 pre_internal_snap_type = _snap_type;
2796 pre_internal_snap_mode = _snap_mode;
2798 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2799 (*i)->enter_internal_edit_mode ();
2802 set_snap_to (internal_snap_type);
2803 set_snap_mode (internal_snap_mode);
2807 internal_snap_mode = _snap_mode;
2808 internal_snap_type = _snap_type;
2810 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2811 (*i)->leave_internal_edit_mode ();
2814 if (mouse_mode == MouseDraw && pre_internal_mouse_mode != MouseDraw) {
2815 /* we were drawing .. flip back to something sensible */
2816 set_mouse_mode (pre_internal_mouse_mode);
2819 set_snap_to (pre_internal_snap_type);
2820 set_snap_mode (pre_internal_snap_mode);
2823 set_canvas_cursor ();
2826 /** Update _join_object_range_state which indicate whether we are over the top or bottom half of a region view,
2827 * used by the `join object/range' tool mode.
2830 Editor::update_join_object_range_location (double /*x*/, double y)
2832 /* XXX: actually, this decides based on whether the mouse is in the top
2833 or bottom half of a the waveform part RouteTimeAxisView;
2835 Note that entered_{track,regionview} is not always setup (e.g. if
2836 the mouse is over a TimeSelection), and to get a Region
2837 that we're over requires searching the playlist.
2840 if ( !get_smart_mode() ) {
2841 _join_object_range_state = JOIN_OBJECT_RANGE_NONE;
2845 if (mouse_mode == MouseObject) {
2846 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2847 } else if (mouse_mode == MouseRange) {
2848 _join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
2851 /* XXX: maybe we should make entered_track work in all cases, rather than resorting to this */
2852 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
2856 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
2861 rtv->canvas_display()->canvas_to_item (cx, cy);
2863 double const c = cy / (rtv->view()->child_height() - TimeAxisViewItem::NAME_HIGHLIGHT_SIZE);
2865 _join_object_range_state = c <= 0.5 ? JOIN_OBJECT_RANGE_RANGE : JOIN_OBJECT_RANGE_OBJECT;
2871 Editor::effective_mouse_mode () const
2873 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
2875 } else if (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE) {
2883 Editor::remove_midi_note (ArdourCanvas::Item* item, GdkEvent *)
2885 NoteBase* e = reinterpret_cast<NoteBase*> (item->get_data ("notebase"));
2888 e->region_view().delete_note (e->note ());
2892 Editor::set_canvas_cursor_for_region_view (double x, RegionView* rv)
2894 /* XXX: this check should not be necessary */
2901 ArdourCanvas::Group* g = rv->get_canvas_group ();
2902 ArdourCanvas::Group* p = g->parent ();
2904 /* Compute x in region view parent coordinates */
2906 p->canvas_to_item (x, dy);
2908 boost::optional<ArdourCanvas::Rect> item_bbox = g->bounding_box ();
2910 ArdourCanvas::Rect parent_bbox = g->item_to_parent (item_bbox.get ());
2912 /* Halfway across the region */
2913 double const h = (parent_bbox.x0 + parent_bbox.x1) / 2;
2915 Trimmable::CanTrim ct = rv->region()->can_trim ();
2917 if (ct & Trimmable::FrontTrimEarlier) {
2918 set_canvas_cursor (_cursors->left_side_trim);
2920 set_canvas_cursor (_cursors->left_side_trim_right_only);
2923 if (ct & Trimmable::EndTrimLater) {
2924 set_canvas_cursor (_cursors->right_side_trim);
2926 set_canvas_cursor (_cursors->right_side_trim_left_only);
2931 /** Obtain the pointer position in canvas coordinates */
2933 Editor::get_pointer_position (double& x, double& y) const
2936 _track_canvas->get_pointer (px, py);
2937 _track_canvas->window_to_canvas (px, py, x, y);