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 + vertical_adjustment.get_value());
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_OBJECT)) {
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, item, 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->track_canvas_item (), false), event);
775 case RangeMarkerBarItem:
776 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
777 _drags->set (new CursorDrag (this, &playhead_cursor->track_canvas_item (), 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->track_canvas_item (), 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->track_canvas_item (), 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 + vertical_adjustment.get_value();
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 RegionSelection s = get_equivalent_regions (selection->regions, Properties::select.property_id);
886 _drags->set (new TrimDrag (this, item, clicked_regionview, s.by_layer()), event);
892 if (!internal_editing()) {
893 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::RangeSelectModifier)) {
894 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionExtend), event);
896 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
906 if (internal_editing()) {
907 /* trim notes if we're in internal edit mode and near the ends of the note */
908 NoteBase* cn = reinterpret_cast<NoteBase*>(item->get_data ("notebase"));
910 if (cn && cn->big_enough_to_trim() && cn->mouse_near_ends()) {
911 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
913 _drags->set (new NoteDrag (this, item), event);
919 if (internal_editing()) {
920 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
921 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
935 if (internal_editing()) {
936 NoteBase* cn = reinterpret_cast<NoteBase*> (item->get_data ("notebase"));
938 if (cn->mouse_near_ends()) {
939 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
941 _drags->set (new NoteDrag (this, item), event);
951 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)) &&
952 event->type == GDK_BUTTON_PRESS) {
954 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
956 } else if (event->type == GDK_BUTTON_PRESS) {
959 case FadeInHandleItem:
961 RegionSelection s = get_equivalent_regions (selection->regions, Properties::select.property_id);
962 _drags->set (new FadeInDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), s), event, _cursors->fade_in);
966 case FadeOutHandleItem:
968 RegionSelection s = get_equivalent_regions (selection->regions, Properties::select.property_id);
969 _drags->set (new FadeOutDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), s), event, _cursors->fade_out);
973 case StartCrossFadeItem:
974 case EndCrossFadeItem:
975 /* we might allow user to grab inside the fade to trim a region with preserve_fade_anchor. for not this is not fully implemented */
976 // if (!clicked_regionview->region()->locked()) {
977 // RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
978 // _drags->set (new TrimDrag (this, item, clicked_regionview, s.by_layer(), true), event);
983 case FeatureLineItem:
985 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier)) {
986 remove_transient(item);
990 _drags->set (new FeatureLineDrag (this, item), event);
996 if (dynamic_cast<AutomationRegionView*> (clicked_regionview)) {
997 /* click on an automation region view; do nothing here and let the ARV's signal handler
1003 if (internal_editing ()) {
1004 if (event->type == GDK_2BUTTON_PRESS && event->button.button == 1) {
1005 Glib::RefPtr<Action> act = ActionManager::get_action (X_("MouseMode"), X_("toggle-internal-edit"));
1011 /* click on a normal region view */
1012 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
1013 add_region_copy_drag (item, event, clicked_regionview);
1014 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
1015 add_region_brush_drag (item, event, clicked_regionview);
1017 add_region_drag (item, event, clicked_regionview);
1021 // if (!internal_editing() && (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE && !selection->regions.empty())) {
1022 // _drags->add (new SelectionDrag (this, clicked_axisview->get_selection_rect (clicked_selection)->rect, SelectionDrag::SelectionMove));
1025 _drags->start_grab (event);
1029 case RegionViewNameHighlight:
1030 case LeftFrameHandle:
1031 case RightFrameHandle:
1032 if (!clicked_regionview->region()->locked()) {
1033 RegionSelection s = get_equivalent_regions (selection->regions, Properties::select.property_id);
1034 _drags->set (new TrimDrag (this, item, clicked_regionview, s.by_layer()), event);
1039 case RegionViewName:
1041 /* rename happens on edit clicks */
1042 RegionSelection s = get_equivalent_regions (selection->regions, Properties::select.property_id);
1043 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, s.by_layer()), event);
1048 case ControlPointItem:
1049 _drags->set (new ControlPointDrag (this, item), event);
1053 case AutomationLineItem:
1054 _drags->set (new LineDrag (this, item), event);
1059 if (internal_editing()) {
1060 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
1061 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
1065 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
1069 case AutomationTrackItem:
1071 TimeAxisView* parent = clicked_axisview->get_parent ();
1072 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (clicked_axisview);
1074 if (parent && dynamic_cast<MidiTimeAxisView*> (parent) && atv->show_regions ()) {
1076 RouteTimeAxisView* p = dynamic_cast<RouteTimeAxisView*> (parent);
1078 boost::shared_ptr<Playlist> pl = p->track()->playlist ();
1079 if (pl->n_regions() == 0) {
1080 /* Parent has no regions; create one so that we have somewhere to put automation */
1081 _drags->set (new RegionCreateDrag (this, item, parent), event);
1083 /* See if there's a region before the click that we can extend, and extend it if so */
1084 framepos_t const t = canvas_event_frame (event);
1085 boost::shared_ptr<Region> prev = pl->find_next_region (t, End, -1);
1087 _drags->set (new RegionCreateDrag (this, item, parent), event);
1089 prev->set_length (t - prev->position ());
1093 /* rubberband drag to select automation points */
1094 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
1101 if ( get_smart_mode() ) {
1102 /* we're in "smart" joined mode, and we've clicked on a Selection */
1103 double const y = event->button.y + vertical_adjustment.get_value();
1104 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
1106 /* if we're over an automation track, start a drag of its data */
1107 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
1109 _drags->set (new AutomationRangeDrag (this, atv, selection->time), event, _cursors->up_down);
1112 /* if we're over a track and a region, and in the `object' part of a region,
1113 put a selection around the region and drag both
1115 /* RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1116 if (rtv && _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
1117 boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track> (rtv->route ());
1119 boost::shared_ptr<Playlist> pl = t->playlist ();
1122 boost::shared_ptr<Region> r = pl->top_region_at (canvas_event_frame (event));
1124 RegionView* rv = rtv->view()->find_view (r);
1125 clicked_selection = select_range (rv->region()->position(),
1126 rv->region()->last_frame()+1);
1127 _drags->add (new SelectionDrag (this, item, SelectionDrag::SelectionMove));
1128 list<RegionView*> rvs;
1130 _drags->add (new RegionMoveDrag (this, item, rv, rvs, false, false));
1131 _drags->start_grab (event);
1144 case ImageFrameHandleStartItem:
1145 imageframe_start_handle_op(item, event) ;
1148 case ImageFrameHandleEndItem:
1149 imageframe_end_handle_op(item, event) ;
1152 case MarkerViewHandleStartItem:
1153 markerview_item_start_handle_op(item, event) ;
1156 case MarkerViewHandleEndItem:
1157 markerview_item_end_handle_op(item, event) ;
1160 case MarkerViewItem:
1161 start_markerview_grab(item, event) ;
1163 case ImageFrameItem:
1164 start_imageframe_grab(item, event) ;
1180 switch (item_type) {
1182 _drags->set (new LineDrag (this, item), event);
1185 case ControlPointItem:
1186 _drags->set (new ControlPointDrag (this, item), event);
1192 AudioRegionView* arv = dynamic_cast<AudioRegionView *> (clicked_regionview);
1194 _drags->set (new AutomationRangeDrag (this, arv, selection->time), event, _cursors->up_down);
1195 _drags->start_grab (event);
1201 case AutomationLineItem:
1202 _drags->set (new LineDrag (this, item), event);
1212 if (event->type == GDK_BUTTON_PRESS) {
1213 _drags->set (new MouseZoomDrag (this, item), event);
1220 if (internal_editing() && item_type == NoteItem) {
1221 /* drag notes if we're in internal edit mode */
1222 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
1224 } else if (clicked_regionview) {
1226 _drags->set (new TimeFXDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1232 _drags->set (new ScrubDrag (this, item), event);
1233 scrub_reversals = 0;
1234 scrub_reverse_distance = 0;
1235 last_scrub_x = event->button.x;
1236 scrubbing_direction = 0;
1237 set_canvas_cursor (_cursors->transparent);
1249 Editor::button_press_handler_2 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1251 Editing::MouseMode const eff = effective_mouse_mode ();
1254 switch (item_type) {
1256 if (internal_editing ()) {
1257 /* no region drags in internal edit mode */
1261 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
1262 add_region_copy_drag (item, event, clicked_regionview);
1264 add_region_drag (item, event, clicked_regionview);
1266 _drags->start_grab (event);
1269 case ControlPointItem:
1270 _drags->set (new ControlPointDrag (this, item), event);
1278 switch (item_type) {
1279 case RegionViewNameHighlight:
1280 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1284 case LeftFrameHandle:
1285 case RightFrameHandle:
1286 if (!internal_editing ()) {
1287 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1292 case RegionViewName:
1293 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer()), event);
1307 /* relax till release */
1313 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1314 temporal_zoom_to_frame (false, canvas_event_frame (event));
1316 temporal_zoom_to_frame (true, canvas_event_frame(event));
1329 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1331 if (event->type != GDK_BUTTON_PRESS) {
1335 Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->_track_canvas_viewport->get_window();
1337 if (canvas_window) {
1338 Glib::RefPtr<const Gdk::Window> pointer_window;
1341 Gdk::ModifierType mask;
1343 pointer_window = canvas_window->get_pointer (x, y, mask);
1345 if (pointer_window == _track_canvas->get_window()) {
1346 _track_canvas_viewport->window_to_canvas (x, y, wx, wy);
1350 pre_press_cursor = current_canvas_cursor;
1352 _track_canvas->grab_focus();
1354 if (_session && _session->actively_recording()) {
1358 if (internal_editing()) {
1359 bool leave_internal_edit_mode = false;
1361 switch (item_type) {
1366 if (!dynamic_cast<MidiRegionView*> (clicked_regionview) && !dynamic_cast<AutomationRegionView*> (clicked_regionview)) {
1367 leave_internal_edit_mode = true;
1371 case PlayheadCursorItem:
1373 case TempoMarkerItem:
1374 case MeterMarkerItem:
1378 case RangeMarkerBarItem:
1379 case CdMarkerBarItem:
1380 case TransportMarkerBarItem:
1382 /* button press on these events never does anything to
1383 change the editing mode.
1391 if (leave_internal_edit_mode) {
1392 ActionManager::do_action ("MouseMode", "toggle-internal-edit");
1396 button_selection (item, event, item_type);
1398 if (!_drags->active () &&
1399 (Keyboard::is_delete_event (&event->button) ||
1400 Keyboard::is_context_menu_event (&event->button) ||
1401 Keyboard::is_edit_event (&event->button))) {
1403 /* handled by button release */
1407 //not rolling, range mode click + join_play_range : locate the PH here
1408 if ( !_drags->active () && !_session->transport_rolling() && ( effective_mouse_mode() == MouseRange ) && Config->get_always_play_range() ) {
1409 framepos_t where = canvas_event_frame (event, 0, 0);
1411 _session->request_locate (where, false);
1414 switch (event->button.button) {
1416 return button_press_handler_1 (item, event, item_type);
1420 return button_press_handler_2 (item, event, item_type);
1427 return button_press_dispatch (&event->button);
1436 Editor::button_press_dispatch (GdkEventButton* ev)
1438 /* this function is intended only for buttons 4 and above.
1441 Gtkmm2ext::MouseButton b (ev->state, ev->button);
1442 return button_bindings->activate (b, Gtkmm2ext::Bindings::Press);
1446 Editor::button_release_dispatch (GdkEventButton* ev)
1448 /* this function is intended only for buttons 4 and above.
1451 Gtkmm2ext::MouseButton b (ev->state, ev->button);
1452 return button_bindings->activate (b, Gtkmm2ext::Bindings::Release);
1456 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1458 framepos_t where = canvas_event_frame (event, 0, 0);
1459 AutomationTimeAxisView* atv = 0;
1461 if (pre_press_cursor) {
1462 set_canvas_cursor (pre_press_cursor);
1463 pre_press_cursor = 0;
1466 /* no action if we're recording */
1468 if (_session && _session->actively_recording()) {
1472 /* see if we're finishing a drag */
1474 bool were_dragging = false;
1475 if (_drags->active ()) {
1476 bool const r = _drags->end_grab (event);
1478 /* grab dragged, so do nothing else */
1482 were_dragging = true;
1485 update_region_layering_order_editor ();
1487 /* edit events get handled here */
1489 if (!_drags->active () && Keyboard::is_edit_event (&event->button)) {
1490 switch (item_type) {
1492 show_region_properties ();
1495 case TempoMarkerItem:
1496 edit_tempo_marker (item);
1499 case MeterMarkerItem:
1500 edit_meter_marker (item);
1503 case RegionViewName:
1504 if (clicked_regionview->name_active()) {
1505 return mouse_rename_region (item, event);
1509 case ControlPointItem:
1510 edit_control_point (item);
1515 NoteBase* e = reinterpret_cast<NoteBase*> (item->get_data ("notebase"));
1517 edit_notes (e->region_view().selection ());
1527 /* context menu events get handled here */
1528 if (Keyboard::is_context_menu_event (&event->button)) {
1530 context_click_event = *event;
1532 if (!_drags->active ()) {
1534 /* no matter which button pops up the context menu, tell the menu
1535 widget to use button 1 to drive menu selection.
1538 switch (item_type) {
1540 case FadeInHandleItem:
1542 case FadeOutHandleItem:
1543 popup_fade_context_menu (1, event->button.time, item, item_type);
1546 case StartCrossFadeItem:
1547 popup_xfade_in_context_menu (1, event->button.time, item, item_type);
1550 case EndCrossFadeItem:
1551 popup_xfade_out_context_menu (1, event->button.time, item, item_type);
1555 popup_track_context_menu (1, event->button.time, item_type, false);
1559 case RegionViewNameHighlight:
1560 case LeftFrameHandle:
1561 case RightFrameHandle:
1562 case RegionViewName:
1563 popup_track_context_menu (1, event->button.time, item_type, false);
1567 popup_track_context_menu (1, event->button.time, item_type, true);
1570 case AutomationTrackItem:
1571 popup_track_context_menu (1, event->button.time, item_type, false);
1575 case RangeMarkerBarItem:
1576 case TransportMarkerBarItem:
1577 case CdMarkerBarItem:
1581 popup_ruler_menu (where, item_type);
1585 marker_context_menu (&event->button, item);
1588 case TempoMarkerItem:
1589 tempo_or_meter_marker_context_menu (&event->button, item);
1592 case MeterMarkerItem:
1593 tempo_or_meter_marker_context_menu (&event->button, item);
1596 case CrossfadeViewItem:
1597 popup_track_context_menu (1, event->button.time, item_type, false);
1600 case ControlPointItem:
1601 popup_control_point_context_menu (item, event);
1605 case ImageFrameItem:
1606 popup_imageframe_edit_menu(1, event->button.time, item, true) ;
1608 case ImageFrameTimeAxisItem:
1609 popup_imageframe_edit_menu(1, event->button.time, item, false) ;
1611 case MarkerViewItem:
1612 popup_marker_time_axis_edit_menu(1, event->button.time, item, true) ;
1614 case MarkerTimeAxisItem:
1615 popup_marker_time_axis_edit_menu(1, event->button.time, item, false) ;
1627 /* delete events get handled here */
1629 Editing::MouseMode const eff = effective_mouse_mode ();
1631 if (!_drags->active () && Keyboard::is_delete_event (&event->button)) {
1633 switch (item_type) {
1634 case TempoMarkerItem:
1635 remove_tempo_marker (item);
1638 case MeterMarkerItem:
1639 remove_meter_marker (item);
1643 remove_marker (*item, event);
1647 if (eff == MouseObject) {
1648 remove_clicked_region ();
1652 case ControlPointItem:
1653 remove_control_point (item);
1657 remove_midi_note (item, event);
1666 switch (event->button.button) {
1669 switch (item_type) {
1670 /* see comments in button_press_handler */
1671 case PlayheadCursorItem:
1674 case AutomationLineItem:
1675 case StartSelectionTrimItem:
1676 case EndSelectionTrimItem:
1680 if (!_dragging_playhead) {
1681 snap_to_with_modifier (where, event, 0, true);
1682 mouse_add_new_marker (where);
1686 case CdMarkerBarItem:
1687 if (!_dragging_playhead) {
1688 // if we get here then a dragged range wasn't done
1689 snap_to_with_modifier (where, event, 0, true);
1690 mouse_add_new_marker (where, true);
1695 if (!_dragging_playhead) {
1696 snap_to_with_modifier (where, event);
1697 mouse_add_new_tempo_event (where);
1702 if (!_dragging_playhead) {
1703 mouse_add_new_meter_event (pixel_to_sample (event->button.x));
1714 switch (item_type) {
1715 case AutomationTrackItem:
1716 atv = dynamic_cast<AutomationTimeAxisView*>(clicked_axisview);
1718 atv->add_automation_event (event, where, event->button.y);
1728 switch (item_type) {
1731 /* check that we didn't drag before releasing, since
1732 its really annoying to create new control
1733 points when doing this.
1735 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (clicked_regionview);
1736 if (!were_dragging && arv) {
1737 arv->add_gain_point_event (item, event);
1743 case AutomationTrackItem:
1744 dynamic_cast<AutomationTimeAxisView*>(clicked_axisview)->
1745 add_automation_event (event, where, event->button.y);
1754 set_canvas_cursor (current_canvas_cursor);
1755 if (scrubbing_direction == 0) {
1756 /* no drag, just a click */
1757 switch (item_type) {
1759 play_selected_region ();
1765 /* make sure we stop */
1766 _session->request_transport_speed (0.0);
1775 /* do any (de)selection operations that should occur on button release */
1776 button_selection (item, event, item_type);
1785 switch (item_type) {
1787 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1789 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier|Keyboard::SecondaryModifier))) {
1792 // Button2 click is unused
1807 // x_style_paste (where, 1.0);
1828 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1835 switch (item_type) {
1836 case ControlPointItem:
1837 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1838 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1839 cp->set_visible (true);
1843 at_y = cp->get_y ();
1844 cp->i2w (at_x, at_y);
1848 fraction = 1.0 - (cp->get_y() / cp->line().height());
1850 if (is_drawable() && !_drags->active ()) {
1851 set_canvas_cursor (_cursors->fader);
1854 _verbose_cursor->set (cp->line().get_verbose_cursor_string (fraction), at_x, at_y);
1855 _verbose_cursor->show ();
1860 if (mouse_mode == MouseGain) {
1861 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1863 line->set_outline_color (ARDOUR_UI::config()->canvasvar_EnteredGainLine.get());
1865 if (is_drawable()) {
1866 set_canvas_cursor (_cursors->fader);
1871 case AutomationLineItem:
1872 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1873 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1875 line->set_outline_color (ARDOUR_UI::config()->canvasvar_EnteredAutomationLine.get());
1877 if (is_drawable()) {
1878 set_canvas_cursor (_cursors->fader);
1883 case RegionViewNameHighlight:
1884 if (is_drawable() && effective_mouse_mode() == MouseObject && entered_regionview) {
1885 set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1886 _over_region_trim_target = true;
1890 case LeftFrameHandle:
1891 case RightFrameHandle:
1892 if (is_drawable() && effective_mouse_mode() == MouseObject && !internal_editing() && entered_regionview) {
1893 set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1897 case StartSelectionTrimItem:
1899 case ImageFrameHandleStartItem:
1900 case MarkerViewHandleStartItem:
1902 if (is_drawable()) {
1903 set_canvas_cursor (_cursors->left_side_trim);
1906 case EndSelectionTrimItem:
1908 case ImageFrameHandleEndItem:
1909 case MarkerViewHandleEndItem:
1911 if (is_drawable()) {
1912 set_canvas_cursor (_cursors->right_side_trim);
1916 case PlayheadCursorItem:
1917 if (is_drawable()) {
1918 switch (_edit_point) {
1920 set_canvas_cursor (_cursors->grabber_edit_point);
1923 set_canvas_cursor (_cursors->grabber);
1929 case RegionViewName:
1931 /* when the name is not an active item, the entire name highlight is for trimming */
1933 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1934 if (mouse_mode == MouseObject && is_drawable()) {
1935 set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1936 _over_region_trim_target = true;
1942 case AutomationTrackItem:
1943 if (is_drawable()) {
1944 Gdk::Cursor *cursor;
1945 switch (mouse_mode) {
1947 cursor = _cursors->selector;
1950 cursor = _cursors->zoom_in;
1953 cursor = _cursors->cross_hair;
1957 set_canvas_cursor (cursor);
1959 AutomationTimeAxisView* atv;
1960 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1961 clear_entered_track = false;
1962 set_entered_track (atv);
1968 case RangeMarkerBarItem:
1969 case TransportMarkerBarItem:
1970 case CdMarkerBarItem:
1973 if (is_drawable()) {
1974 set_canvas_cursor (_cursors->timebar);
1979 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1982 entered_marker = marker;
1983 marker->set_color_rgba (ARDOUR_UI::config()->canvasvar_EnteredMarker.get());
1985 case MeterMarkerItem:
1986 case TempoMarkerItem:
1987 if (is_drawable()) {
1988 set_canvas_cursor (_cursors->timebar);
1992 case FadeInHandleItem:
1993 if (mouse_mode == MouseObject && !internal_editing()) {
1994 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
1996 rect->set_fill_color (0xBBBBBBAA);
1998 set_canvas_cursor (_cursors->fade_in);
2002 case FadeOutHandleItem:
2003 if (mouse_mode == MouseObject && !internal_editing()) {
2004 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
2006 rect->set_fill_color (0xBBBBBBAA);
2008 set_canvas_cursor (_cursors->fade_out);
2011 case FeatureLineItem:
2013 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
2014 line->set_outline_color (0xFF0000FF);
2019 if ( get_smart_mode() ) {
2020 set_canvas_cursor ();
2028 /* second pass to handle entered track status in a comprehensible way.
2031 switch (item_type) {
2033 case AutomationLineItem:
2034 case ControlPointItem:
2035 /* these do not affect the current entered track state */
2036 clear_entered_track = false;
2039 case AutomationTrackItem:
2040 /* handled above already */
2044 set_entered_track (0);
2052 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent*, ItemType item_type)
2062 switch (item_type) {
2063 case ControlPointItem:
2064 cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
2065 if (cp->line().the_list()->interpolation() != AutomationList::Discrete) {
2066 if (cp->line().npoints() > 1 && !cp->get_selected()) {
2067 cp->set_visible (false);
2071 if (is_drawable()) {
2072 set_canvas_cursor (current_canvas_cursor);
2075 _verbose_cursor->hide ();
2078 case RegionViewNameHighlight:
2079 case LeftFrameHandle:
2080 case RightFrameHandle:
2081 case StartSelectionTrimItem:
2082 case EndSelectionTrimItem:
2083 case PlayheadCursorItem:
2086 case ImageFrameHandleStartItem:
2087 case ImageFrameHandleEndItem:
2088 case MarkerViewHandleStartItem:
2089 case MarkerViewHandleEndItem:
2092 _over_region_trim_target = false;
2094 if (is_drawable()) {
2095 set_canvas_cursor (current_canvas_cursor);
2100 case AutomationLineItem:
2101 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
2103 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
2105 line->set_outline_color (al->get_line_color());
2108 if (is_drawable()) {
2109 set_canvas_cursor (current_canvas_cursor);
2113 case RegionViewName:
2114 /* see enter_handler() for notes */
2115 _over_region_trim_target = false;
2117 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
2118 if (is_drawable() && mouse_mode == MouseObject) {
2119 set_canvas_cursor (current_canvas_cursor);
2124 case RangeMarkerBarItem:
2125 case TransportMarkerBarItem:
2126 case CdMarkerBarItem:
2130 if (is_drawable()) {
2131 set_canvas_cursor (current_canvas_cursor);
2136 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2140 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
2141 location_flags_changed (loc, this);
2144 case MeterMarkerItem:
2145 case TempoMarkerItem:
2147 if (is_drawable()) {
2148 set_canvas_cursor (current_canvas_cursor);
2153 case FadeInHandleItem:
2154 case FadeOutHandleItem:
2155 rv = static_cast<RegionView*>(item->get_data ("regionview"));
2157 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
2159 rect->set_fill_color (rv->get_fill_color());
2162 set_canvas_cursor (current_canvas_cursor);
2165 case AutomationTrackItem:
2166 if (is_drawable()) {
2167 set_canvas_cursor (current_canvas_cursor);
2168 clear_entered_track = true;
2169 Glib::signal_idle().connect (sigc::mem_fun(*this, &Editor::left_automation_track));
2172 case FeatureLineItem:
2174 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
2175 line->set_outline_color (ARDOUR_UI::config()->canvasvar_ZeroLine.get());
2187 Editor::left_automation_track ()
2189 if (clear_entered_track) {
2190 set_entered_track (0);
2191 clear_entered_track = false;
2197 Editor::scrub (framepos_t frame, double current_x)
2201 if (scrubbing_direction == 0) {
2203 _session->request_locate (frame, false);
2204 _session->request_transport_speed (0.1);
2205 scrubbing_direction = 1;
2209 if (last_scrub_x > current_x) {
2211 /* pointer moved to the left */
2213 if (scrubbing_direction > 0) {
2215 /* we reversed direction to go backwards */
2218 scrub_reverse_distance += (int) (last_scrub_x - current_x);
2222 /* still moving to the left (backwards) */
2224 scrub_reversals = 0;
2225 scrub_reverse_distance = 0;
2227 delta = 0.01 * (last_scrub_x - current_x);
2228 _session->request_transport_speed_nonzero (_session->transport_speed() - delta);
2232 /* pointer moved to the right */
2234 if (scrubbing_direction < 0) {
2235 /* we reversed direction to go forward */
2238 scrub_reverse_distance += (int) (current_x - last_scrub_x);
2241 /* still moving to the right */
2243 scrub_reversals = 0;
2244 scrub_reverse_distance = 0;
2246 delta = 0.01 * (current_x - last_scrub_x);
2247 _session->request_transport_speed_nonzero (_session->transport_speed() + delta);
2251 /* if there have been more than 2 opposite motion moves detected, or one that moves
2252 back more than 10 pixels, reverse direction
2255 if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
2257 if (scrubbing_direction > 0) {
2258 /* was forwards, go backwards */
2259 _session->request_transport_speed (-0.1);
2260 scrubbing_direction = -1;
2262 /* was backwards, go forwards */
2263 _session->request_transport_speed (0.1);
2264 scrubbing_direction = 1;
2267 scrub_reverse_distance = 0;
2268 scrub_reversals = 0;
2272 last_scrub_x = current_x;
2276 Editor::motion_handler (ArdourCanvas::Item* /*item*/, GdkEvent* event, bool from_autoscroll)
2278 _last_motion_y = event->motion.y;
2280 if (event->motion.is_hint) {
2283 /* We call this so that MOTION_NOTIFY events continue to be
2284 delivered to the canvas. We need to do this because we set
2285 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
2286 the density of the events, at the expense of a round-trip
2287 to the server. Given that this will mostly occur on cases
2288 where DISPLAY = :0.0, and given the cost of what the motion
2289 event might do, its a good tradeoff.
2292 _track_canvas->get_pointer (x, y);
2295 if (current_stepping_trackview) {
2296 /* don't keep the persistent stepped trackview if the mouse moves */
2297 current_stepping_trackview = 0;
2298 step_timeout.disconnect ();
2301 if (_session && _session->actively_recording()) {
2302 /* Sorry. no dragging stuff around while we record */
2306 JoinObjectRangeState const old = _join_object_range_state;
2307 update_join_object_range_location (event->motion.x, event->motion.y);
2309 if (!_internal_editing && _join_object_range_state != old) {
2310 set_canvas_cursor ();
2313 if (!_internal_editing && _over_region_trim_target) {
2314 set_canvas_cursor_for_region_view (event->motion.x, entered_regionview);
2317 bool handled = false;
2318 if (_drags->active ()) {
2319 handled = _drags->motion_handler (event, from_autoscroll);
2326 track_canvas_motion (event);
2331 Editor::can_remove_control_point (ArdourCanvas::Item* item)
2333 ControlPoint* control_point;
2335 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2336 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2340 AutomationLine& line = control_point->line ();
2341 if (dynamic_cast<AudioRegionGainLine*> (&line)) {
2342 /* we shouldn't remove the first or last gain point in region gain lines */
2343 if (line.is_last_point(*control_point) || line.is_first_point(*control_point)) {
2352 Editor::remove_control_point (ArdourCanvas::Item* item)
2354 if (!can_remove_control_point (item)) {
2358 ControlPoint* control_point;
2360 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2361 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2365 control_point->line().remove_point (*control_point);
2369 Editor::edit_control_point (ArdourCanvas::Item* item)
2371 ControlPoint* p = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"));
2374 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2378 ControlPointDialog d (p);
2379 d.set_position (Gtk::WIN_POS_MOUSE);
2382 if (d.run () != RESPONSE_ACCEPT) {
2386 p->line().modify_point_y (*p, d.get_y_fraction ());
2390 Editor::edit_notes (MidiRegionView::Selection const & s)
2396 EditNoteDialog d (&(*s.begin())->region_view(), s);
2397 d.set_position (Gtk::WIN_POS_MOUSE);
2405 Editor::visible_order_range (int* low, int* high) const
2407 *low = TimeAxisView::max_order ();
2410 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
2412 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
2414 if (!rtv->hidden()) {
2416 if (*high < rtv->order()) {
2417 *high = rtv->order ();
2420 if (*low > rtv->order()) {
2421 *low = rtv->order ();
2428 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
2430 /* Either add to or set the set the region selection, unless
2431 this is an alignment click (control used)
2434 if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
2435 TimeAxisView* tv = &rv.get_time_axis_view();
2436 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
2438 if (rtv && rtv->is_track()) {
2439 speed = rtv->track()->speed();
2442 framepos_t where = get_preferred_edit_position();
2446 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
2448 align_region (rv.region(), SyncPoint, (framepos_t) (where * speed));
2450 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
2452 align_region (rv.region(), End, (framepos_t) (where * speed));
2456 align_region (rv.region(), Start, (framepos_t) (where * speed));
2463 Editor::collect_new_region_view (RegionView* rv)
2465 latest_regionviews.push_back (rv);
2469 Editor::collect_and_select_new_region_view (RegionView* rv)
2472 latest_regionviews.push_back (rv);
2476 Editor::cancel_selection ()
2478 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2479 (*i)->hide_selection ();
2482 selection->clear ();
2483 clicked_selection = 0;
2487 Editor::cancel_time_selection ()
2489 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2490 (*i)->hide_selection ();
2492 selection->time.clear ();
2493 clicked_selection = 0;
2497 Editor::point_trim (GdkEvent* event, framepos_t new_bound)
2499 RegionView* rv = clicked_regionview;
2501 /* Choose action dependant on which button was pressed */
2502 switch (event->button.button) {
2504 begin_reversible_command (_("start point trim"));
2506 if (selection->selected (rv)) {
2507 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
2508 i != selection->regions.by_layer().end(); ++i)
2510 if (!(*i)->region()->locked()) {
2511 (*i)->region()->clear_changes ();
2512 (*i)->region()->trim_front (new_bound);
2513 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2518 if (!rv->region()->locked()) {
2519 rv->region()->clear_changes ();
2520 rv->region()->trim_front (new_bound);
2521 _session->add_command(new StatefulDiffCommand (rv->region()));
2525 commit_reversible_command();
2529 begin_reversible_command (_("End point trim"));
2531 if (selection->selected (rv)) {
2533 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
2535 if (!(*i)->region()->locked()) {
2536 (*i)->region()->clear_changes();
2537 (*i)->region()->trim_end (new_bound);
2538 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2544 if (!rv->region()->locked()) {
2545 rv->region()->clear_changes ();
2546 rv->region()->trim_end (new_bound);
2547 _session->add_command (new StatefulDiffCommand (rv->region()));
2551 commit_reversible_command();
2560 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* /*event*/)
2565 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2566 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2570 Location* location = find_location_from_marker (marker, is_start);
2571 location->set_hidden (true, this);
2576 Editor::reposition_zoom_rect (framepos_t start, framepos_t end)
2578 double x1 = sample_to_pixel (start);
2579 double x2 = sample_to_pixel (end);
2580 double y2 = _full_canvas_height - 1.0;
2582 zoom_rect->set (ArdourCanvas::Rect (x1, 1.0, x2, y2));
2587 Editor::mouse_rename_region (ArdourCanvas::Item* /*item*/, GdkEvent* /*event*/)
2589 using namespace Gtkmm2ext;
2591 ArdourPrompter prompter (false);
2593 prompter.set_prompt (_("Name for region:"));
2594 prompter.set_initial_text (clicked_regionview->region()->name());
2595 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
2596 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
2597 prompter.show_all ();
2598 switch (prompter.run ()) {
2599 case Gtk::RESPONSE_ACCEPT:
2601 prompter.get_result(str);
2603 clicked_regionview->region()->set_name (str);
2612 Editor::mouse_brush_insert_region (RegionView* rv, framepos_t pos)
2614 /* no brushing without a useful snap setting */
2616 switch (_snap_mode) {
2618 return; /* can't work because it allows region to be placed anywhere */
2623 switch (_snap_type) {
2631 /* don't brush a copy over the original */
2633 if (pos == rv->region()->position()) {
2637 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
2639 if (rtv == 0 || !rtv->is_track()) {
2643 boost::shared_ptr<Playlist> playlist = rtv->playlist();
2644 double speed = rtv->track()->speed();
2646 playlist->clear_changes ();
2647 boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region(), true));
2648 playlist->add_region (new_region, (framepos_t) (pos * speed));
2649 _session->add_command (new StatefulDiffCommand (playlist));
2651 // playlist is frozen, so we have to update manually XXX this is disgusting
2653 playlist->RegionAdded (new_region); /* EMIT SIGNAL */
2657 Editor::track_height_step_timeout ()
2659 if (get_microseconds() - last_track_height_step_timestamp < 250000) {
2660 current_stepping_trackview = 0;
2667 Editor::add_region_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2669 assert (region_view);
2671 if (!region_view->region()->playlist()) {
2675 _region_motion_group->raise_to_top ();
2677 if (Config->get_edit_mode() == Splice) {
2678 _drags->add (new RegionSpliceDrag (this, item, region_view, selection->regions.by_layer()));
2680 RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::select.property_id);
2681 _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), false, false));
2684 /* sync the canvas to what we think is its current state */
2685 update_canvas_now();
2689 Editor::add_region_copy_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2691 assert (region_view);
2693 if (!region_view->region()->playlist()) {
2697 _region_motion_group->raise_to_top ();
2699 RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::select.property_id);
2700 _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), false, true));
2704 Editor::add_region_brush_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2706 assert (region_view);
2708 if (!region_view->region()->playlist()) {
2712 if (Config->get_edit_mode() == Splice) {
2716 RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::select.property_id);
2717 _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), true, false));
2719 begin_reversible_command (Operations::drag_region_brush);
2722 /** Start a grab where a time range is selected, track(s) are selected, and the
2723 * user clicks and drags a region with a modifier in order to create a new region containing
2724 * the section of the clicked region that lies within the time range.
2727 Editor::start_selection_grab (ArdourCanvas::Item* /*item*/, GdkEvent* event)
2729 if (clicked_regionview == 0) {
2733 /* lets try to create new Region for the selection */
2735 vector<boost::shared_ptr<Region> > new_regions;
2736 create_region_from_selection (new_regions);
2738 if (new_regions.empty()) {
2742 /* XXX fix me one day to use all new regions */
2744 boost::shared_ptr<Region> region (new_regions.front());
2746 /* add it to the current stream/playlist.
2748 tricky: the streamview for the track will add a new regionview. we will
2749 catch the signal it sends when it creates the regionview to
2750 set the regionview we want to then drag.
2753 latest_regionviews.clear();
2754 sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
2756 /* A selection grab currently creates two undo/redo operations, one for
2757 creating the new region and another for moving it.
2760 begin_reversible_command (Operations::selection_grab);
2762 boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
2764 playlist->clear_changes ();
2765 clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
2766 _session->add_command(new StatefulDiffCommand (playlist));
2768 commit_reversible_command ();
2772 if (latest_regionviews.empty()) {
2773 /* something went wrong */
2777 /* we need to deselect all other regionviews, and select this one
2778 i'm ignoring undo stuff, because the region creation will take care of it
2780 selection->set (latest_regionviews);
2782 _drags->set (new RegionMoveDrag (this, latest_regionviews.front()->get_canvas_group(), latest_regionviews.front(), latest_regionviews, false, false), event);
2788 if (_drags->active ()) {
2791 selection->clear ();
2796 Editor::set_internal_edit (bool yn)
2798 if (_internal_editing == yn) {
2802 _internal_editing = yn;
2805 pre_internal_mouse_mode = mouse_mode;
2806 pre_internal_snap_type = _snap_type;
2807 pre_internal_snap_mode = _snap_mode;
2809 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2810 (*i)->enter_internal_edit_mode ();
2813 set_snap_to (internal_snap_type);
2814 set_snap_mode (internal_snap_mode);
2818 internal_snap_mode = _snap_mode;
2819 internal_snap_type = _snap_type;
2821 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2822 (*i)->leave_internal_edit_mode ();
2825 if (mouse_mode == MouseDraw && pre_internal_mouse_mode != MouseDraw) {
2826 /* we were drawing .. flip back to something sensible */
2827 set_mouse_mode (pre_internal_mouse_mode);
2830 set_snap_to (pre_internal_snap_type);
2831 set_snap_mode (pre_internal_snap_mode);
2834 set_canvas_cursor ();
2837 /** Update _join_object_range_state which indicate whether we are over the top or bottom half of a region view,
2838 * used by the `join object/range' tool mode.
2841 Editor::update_join_object_range_location (double /*x*/, double y)
2843 /* XXX: actually, this decides based on whether the mouse is in the top
2844 or bottom half of a the waveform part RouteTimeAxisView;
2846 Note that entered_{track,regionview} is not always setup (e.g. if
2847 the mouse is over a TimeSelection), and to get a Region
2848 that we're over requires searching the playlist.
2851 if ( !get_smart_mode() ) {
2852 _join_object_range_state = JOIN_OBJECT_RANGE_NONE;
2856 if (mouse_mode == MouseObject) {
2857 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2858 } else if (mouse_mode == MouseRange) {
2859 _join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
2862 /* XXX: maybe we should make entered_track work in all cases, rather than resorting to this */
2863 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y + vertical_adjustment.get_value());
2867 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
2872 rtv->canvas_display()->canvas_to_item (cx, cy);
2874 double const c = cy / (rtv->view()->child_height() - TimeAxisViewItem::NAME_HIGHLIGHT_SIZE);
2876 _join_object_range_state = c <= 0.5 ? JOIN_OBJECT_RANGE_RANGE : JOIN_OBJECT_RANGE_OBJECT;
2882 Editor::effective_mouse_mode () const
2884 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
2886 } else if (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE) {
2894 Editor::remove_midi_note (ArdourCanvas::Item* item, GdkEvent *)
2896 NoteBase* e = reinterpret_cast<NoteBase*> (item->get_data ("notebase"));
2899 e->region_view().delete_note (e->note ());
2903 Editor::set_canvas_cursor_for_region_view (double x, RegionView* rv)
2905 /* XXX: this check should not be necessary */
2912 ArdourCanvas::Group* g = rv->get_canvas_group ();
2913 ArdourCanvas::Group* p = g->parent ();
2915 /* Compute x in region view parent coordinates */
2917 p->canvas_to_item (x, dy);
2919 boost::optional<ArdourCanvas::Rect> item_bbox = g->bounding_box ();
2921 ArdourCanvas::Rect parent_bbox = g->item_to_parent (item_bbox.get ());
2923 /* Halfway across the region */
2924 double const h = (parent_bbox.x0 + parent_bbox.x1) / 2;
2926 Trimmable::CanTrim ct = rv->region()->can_trim ();
2928 if (ct & Trimmable::FrontTrimEarlier) {
2929 set_canvas_cursor (_cursors->left_side_trim);
2931 set_canvas_cursor (_cursors->left_side_trim_right_only);
2934 if (ct & Trimmable::EndTrimLater) {
2935 set_canvas_cursor (_cursors->right_side_trim);
2937 set_canvas_cursor (_cursors->right_side_trim_left_only);
2942 /** Obtain the pointer position in canvas coordinates */
2944 Editor::get_pointer_position (double& x, double& y) const
2947 _track_canvas->get_pointer (px, py);
2948 _track_canvas_viewport->window_to_canvas (px, py, x, y);