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) {
106 Gdk::ModifierType mask;
107 Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->_track_canvas_viewport->get_bin_window();
108 Glib::RefPtr<const Gdk::Window> pointer_window;
110 if (!canvas_window) {
114 pointer_window = canvas_window->get_pointer (x, y, mask);
116 if (pointer_window == _track_canvas->get_window()) {
119 in_track_canvas = true;
121 in_track_canvas = false;
126 event.type = GDK_BUTTON_RELEASE;
130 where = event_frame (&event, 0, 0);
135 Editor::event_frame (GdkEvent const * event, double* pcx, double* pcy) const
149 /* The event coordinates will be canvas coordinates */
151 switch (event->type) {
152 case GDK_BUTTON_RELEASE:
153 case GDK_BUTTON_PRESS:
154 case GDK_2BUTTON_PRESS:
155 case GDK_3BUTTON_PRESS:
156 *pcx = event->button.x;
157 *pcy = event->button.y;
158 _trackview_group->canvas_to_item (*pcx, *pcy);
160 case GDK_MOTION_NOTIFY:
161 *pcx = event->motion.x;
162 *pcy = event->motion.y;
163 _trackview_group->canvas_to_item (*pcx, *pcy);
165 case GDK_ENTER_NOTIFY:
166 case GDK_LEAVE_NOTIFY:
167 *pcx = event->crossing.x;
168 *pcy = event->crossing.y;
170 // track_canvas->w2c(event->crossing.x, event->crossing.y, *pcx, *pcy);
173 case GDK_KEY_RELEASE:
174 // track_canvas->w2c(event->key.x, event->key.y, *pcx, *pcy);
177 warning << string_compose (_("Editor::event_frame() used on unhandled event type %1"), event->type) << endmsg;
181 /* note that pixel_to_frame() never returns less than zero, so even if the pixel
182 position is negative (as can be the case with motion events in particular),
183 the frame location is always positive.
186 return pixel_to_frame (*pcx);
190 Editor::which_grabber_cursor ()
192 Gdk::Cursor* c = _cursors->grabber;
194 if (_internal_editing) {
195 switch (mouse_mode) {
197 c = _cursors->midi_pencil;
201 c = _cursors->grabber_note;
205 c = _cursors->midi_resize;
209 c = _cursors->grabber_note;
218 switch (_edit_point) {
220 c = _cursors->grabber_edit_point;
223 boost::shared_ptr<Movable> m = _movable.lock();
224 if (m && m->locked()) {
225 c = _cursors->speaker;
235 Editor::set_current_trimmable (boost::shared_ptr<Trimmable> t)
237 boost::shared_ptr<Trimmable> st = _trimmable.lock();
239 if (!st || st == t) {
241 set_canvas_cursor ();
246 Editor::set_current_movable (boost::shared_ptr<Movable> m)
248 boost::shared_ptr<Movable> sm = _movable.lock();
250 if (!sm || sm != m) {
252 set_canvas_cursor ();
257 Editor::set_canvas_cursor ()
259 switch (mouse_mode) {
261 current_canvas_cursor = _cursors->selector;
262 if (_internal_editing) {
263 current_canvas_cursor = which_grabber_cursor();
268 current_canvas_cursor = which_grabber_cursor();
272 current_canvas_cursor = _cursors->midi_pencil;
276 current_canvas_cursor = _cursors->cross_hair;
280 if (Keyboard::the_keyboard().key_is_down (GDK_Control_L)) {
281 current_canvas_cursor = _cursors->zoom_out;
283 current_canvas_cursor = _cursors->zoom_in;
288 current_canvas_cursor = _cursors->time_fx; // just use playhead
292 current_canvas_cursor = _cursors->speaker;
296 if (!_internal_editing) {
297 switch (_join_object_range_state) {
298 case JOIN_OBJECT_RANGE_NONE:
300 case JOIN_OBJECT_RANGE_OBJECT:
301 current_canvas_cursor = which_grabber_cursor ();
303 case JOIN_OBJECT_RANGE_RANGE:
304 current_canvas_cursor = _cursors->selector;
309 /* up-down cursor as a cue that automation can be dragged up and down when in join object/range mode */
310 if (!_internal_editing && get_smart_mode() ) {
313 get_pointer_position (x, y);
315 if (x >= 0 && y >= 0) {
317 vector<ArdourCanvas::Item const *> items;
319 _track_canvas->root()->add_items_at_point (ArdourCanvas::Duple (x,y), items);
321 // first item will be the upper most
323 if (!items.empty()) {
324 const ArdourCanvas::Item* i = items.front();
326 if (i && i->parent() && i->parent()->get_data (X_("timeselection"))) {
327 pair<TimeAxisView*, int> tvp = trackview_by_y_position (_last_motion_y + vertical_adjustment.get_value());
328 if (dynamic_cast<AutomationTimeAxisView*> (tvp.first)) {
329 current_canvas_cursor = _cursors->up_down;
336 set_canvas_cursor (current_canvas_cursor, true);
340 Editor::mouse_mode_object_range_toggled()
342 MouseMode m = mouse_mode;
344 Glib::RefPtr<Action> act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-range"));
346 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
348 if (tact->get_active())
349 m = MouseObject; //Smart mode turned to ON, force editing to Object mode
351 set_mouse_mode(m, true); //call this so the button styles can get updated
355 Editor::set_mouse_mode (MouseMode m, bool force)
357 if (_drags->active ()) {
361 if (!force && m == mouse_mode) {
365 Glib::RefPtr<Action> act;
369 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-range"));
373 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-object"));
377 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-draw"));
381 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-gain"));
385 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-zoom"));
389 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-timefx"));
393 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-audition"));
399 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
402 /* go there and back to ensure that the toggled handler is called to set up mouse_mode */
403 tact->set_active (false);
404 tact->set_active (true);
406 //NOTE: this will result in a call to mouse_mode_toggled which does the heavy lifting
410 Editor::mouse_mode_toggled (MouseMode m)
412 Glib::RefPtr<Action> act;
413 Glib::RefPtr<ToggleAction> tact;
417 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-range"));
421 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-object"));
425 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-draw"));
429 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-gain"));
433 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-zoom"));
437 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-timefx"));
441 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-audition"));
447 tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
450 if (!tact->get_active()) {
451 /* this was just the notification that the old mode has been
452 * left. we'll get called again with the new mode active in a
460 act = ActionManager::get_action (X_("MouseMode"), X_("toggle-internal-edit"));
461 tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
462 tact->set_active (true);
468 if (_session && mouse_mode == MouseAudition) {
469 /* stop transport and reset default speed to avoid oddness with
471 _session->request_transport_speed (0.0, true);
478 //TODO: set button styles for smart buttons
480 if ( smart_mode_action->get_active() ) {
481 if( mouse_mode == MouseObject ) { //smart active and object active
482 smart_mode_button.set_active(1);
483 smart_mode_button.set_name("smart mode button");
484 mouse_move_button.set_name("smart mode button");
485 } else { //smart active but object inactive
486 smart_mode_button.set_active(0);
487 smart_mode_button.set_name("smart mode button");
488 mouse_move_button.set_name("mouse mode button");
491 smart_mode_button.set_active(0);
492 smart_mode_button.set_name("mouse mode button");
493 mouse_move_button.set_name("mouse mode button");
497 set_canvas_cursor ();
498 set_gain_envelope_visibility ();
500 MouseModeChanged (); /* EMIT SIGNAL */
504 Editor::step_mouse_mode (bool next)
506 switch (current_mouse_mode()) {
509 if (Profile->get_sae()) {
510 set_mouse_mode (MouseZoom);
512 set_mouse_mode (MouseRange);
515 set_mouse_mode (MouseTimeFX);
520 if (next) set_mouse_mode (MouseDraw);
521 else set_mouse_mode (MouseObject);
525 if (next) set_mouse_mode (MouseZoom);
526 else set_mouse_mode (MouseRange);
531 if (Profile->get_sae()) {
532 set_mouse_mode (MouseTimeFX);
534 set_mouse_mode (MouseGain);
537 if (Profile->get_sae()) {
538 set_mouse_mode (MouseObject);
540 set_mouse_mode (MouseDraw);
546 if (next) set_mouse_mode (MouseTimeFX);
547 else set_mouse_mode (MouseZoom);
552 set_mouse_mode (MouseAudition);
554 if (Profile->get_sae()) {
555 set_mouse_mode (MouseZoom);
557 set_mouse_mode (MouseGain);
563 if (next) set_mouse_mode (MouseObject);
564 else set_mouse_mode (MouseTimeFX);
570 Editor::toggle_internal_editing_from_double_click (GdkEvent* event)
572 if (_drags->active()) {
573 _drags->end_grab (event);
576 ActionManager::do_action ("MouseMode", "toggle-internal-edit");
578 /* prevent reversion of edit cursor on button release */
580 pre_press_cursor = 0;
586 Editor::button_selection (ArdourCanvas::Item* /*item*/, GdkEvent* event, ItemType item_type)
588 /* in object/audition/timefx/gain-automation mode,
589 any button press sets the selection if the object
590 can be selected. this is a bit of hack, because
591 we want to avoid this if the mouse operation is a
594 note: not dbl-click or triple-click
596 Also note that there is no region selection in internal edit mode, otherwise
597 for operations operating on the selection (e.g. cut) it is not obvious whether
598 to cut notes or regions.
601 if (((mouse_mode != MouseObject) &&
602 (mouse_mode != MouseAudition || item_type != RegionItem) &&
603 (mouse_mode != MouseTimeFX || item_type != RegionItem) &&
604 (mouse_mode != MouseGain) &&
605 (mouse_mode != MouseDraw)) ||
606 ((event->type != GDK_BUTTON_PRESS && event->type != GDK_BUTTON_RELEASE) || event->button.button > 3) ||
607 (internal_editing() && mouse_mode != MouseTimeFX)) {
612 if (event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) {
614 if ((event->button.state & Keyboard::RelevantModifierKeyMask) && event->button.button != 1) {
616 /* almost no selection action on modified button-2 or button-3 events */
618 if (item_type != RegionItem && event->button.button != 2) {
624 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
625 bool press = (event->type == GDK_BUTTON_PRESS);
629 if (!get_smart_mode() || (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT)) {
631 if (mouse_mode != MouseRange) {
632 set_selected_regionview_from_click (press, op);
634 /* don't change the selection unless the
635 clicked track is not currently selected. if
636 so, "collapse" the selection to just this
639 if (!selection->selected (clicked_axisview)) {
640 set_selected_track_as_side_effect (Selection::Set);
644 if (mouse_mode != MouseRange) {
645 set_selected_regionview_from_click (press, op);
651 case RegionViewNameHighlight:
653 case LeftFrameHandle:
654 case RightFrameHandle:
655 if ( mouse_mode != MouseRange ) {
656 set_selected_regionview_from_click (press, op);
657 } else if (event->type == GDK_BUTTON_PRESS) {
658 set_selected_track_as_side_effect (op);
662 case FadeInHandleItem:
664 case FadeOutHandleItem:
666 case StartCrossFadeItem:
667 case EndCrossFadeItem:
668 if ( mouse_mode != MouseRange ) {
669 set_selected_regionview_from_click (press, op);
670 } else if (event->type == GDK_BUTTON_PRESS) {
671 set_selected_track_as_side_effect (op);
675 case ControlPointItem:
676 set_selected_track_as_side_effect (op);
677 if ( mouse_mode != MouseRange ) {
678 set_selected_control_point_from_click (press, op);
683 /* for context click, select track */
684 if (event->button.button == 3) {
685 selection->clear_tracks ();
686 set_selected_track_as_side_effect (op);
690 case AutomationTrackItem:
691 set_selected_track_as_side_effect (op);
700 Editor::button_press_handler_1 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
702 /* single mouse clicks on any of these item types operate
703 independent of mouse mode, mostly because they are
704 not on the main track canvas or because we want
709 case PlayheadCursorItem:
710 _drags->set (new CursorDrag (this, item, true), event);
714 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
715 hide_marker (item, event);
717 _drags->set (new MarkerDrag (this, item), event);
721 case TempoMarkerItem:
723 TempoMarker* m = reinterpret_cast<TempoMarker*> (item->get_data ("marker"));
725 if (m->tempo().movable ()) {
727 new TempoMarkerDrag (
730 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
740 case MeterMarkerItem:
742 MeterMarker* m = reinterpret_cast<MeterMarker*> (item->get_data ("marker"));
744 if (m->meter().movable ()) {
746 new MeterMarkerDrag (
749 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
759 #ifdef WITH_VIDEOTIMELINE
761 _drags->set (new VideoTimeLineDrag (this, item), event);
769 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
770 _drags->set (new CursorDrag (this, &playhead_cursor->track_canvas_item (), false), event);
776 case RangeMarkerBarItem:
777 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
778 _drags->set (new CursorDrag (this, &playhead_cursor->track_canvas_item (), false), event);
780 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateRangeMarker), event);
785 case CdMarkerBarItem:
786 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
787 _drags->set (new CursorDrag (this, &playhead_cursor->track_canvas_item (), false), event);
789 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateCDMarker), event);
794 case TransportMarkerBarItem:
795 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
796 _drags->set (new CursorDrag (this, &playhead_cursor->track_canvas_item (), false), event);
798 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateTransportMarker), event);
807 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
808 /* special case: allow trim of range selections in joined object mode;
809 in theory eff should equal MouseRange in this case, but it doesn't
810 because entering the range selection canvas item results in entered_regionview
811 being set to 0, so update_join_object_range_location acts as if we aren't
814 if (item_type == StartSelectionTrimItem) {
815 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
816 } else if (item_type == EndSelectionTrimItem) {
817 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
821 Editing::MouseMode eff = effective_mouse_mode ();
823 /* special case: allow drag of region fade in/out in object mode with join object/range enabled */
824 if (item_type == FadeInHandleItem || item_type == FadeOutHandleItem) {
828 /* there is no Range mode when in internal edit mode */
829 if (eff == MouseRange && internal_editing()) {
836 case StartSelectionTrimItem:
837 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
840 case EndSelectionTrimItem:
841 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
845 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
846 start_selection_grab (item, event);
848 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
849 /* grab selection for moving */
850 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionMove), event);
852 double const y = event->button.y + vertical_adjustment.get_value();
853 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
855 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
856 if ( get_smart_mode() && atv) {
857 /* smart "join" mode: drag automation */
858 _drags->set (new AutomationRangeDrag (this, atv, selection->time), event, _cursors->up_down);
860 /* this was debated, but decided the more common action was to
861 make a new selection */
862 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
869 if (internal_editing()) {
870 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
871 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
875 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::RangeSelectModifier)) {
876 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionExtend), event);
878 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
884 case RegionViewNameHighlight:
885 if (!clicked_regionview->region()->locked()) {
886 RegionSelection s = get_equivalent_regions (selection->regions, Properties::select.property_id);
887 _drags->set (new TrimDrag (this, item, clicked_regionview, s.by_layer()), event);
893 if (!internal_editing()) {
894 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::RangeSelectModifier)) {
895 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionExtend), event);
897 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
907 if (internal_editing()) {
908 /* trim notes if we're in internal edit mode and near the ends of the note */
909 NoteBase* cn = reinterpret_cast<NoteBase*>(item->get_data ("notebase"));
911 if (cn && cn->big_enough_to_trim() && cn->mouse_near_ends()) {
912 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
914 _drags->set (new NoteDrag (this, item), event);
920 if (internal_editing()) {
921 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
922 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
936 if (internal_editing()) {
937 NoteBase* cn = reinterpret_cast<NoteBase*> (item->get_data ("notebase"));
939 if (cn->mouse_near_ends()) {
940 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
942 _drags->set (new NoteDrag (this, item), event);
952 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)) &&
953 event->type == GDK_BUTTON_PRESS) {
955 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
957 } else if (event->type == GDK_BUTTON_PRESS) {
960 case FadeInHandleItem:
962 RegionSelection s = get_equivalent_regions (selection->regions, Properties::select.property_id);
963 _drags->set (new FadeInDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), s), event, _cursors->fade_in);
967 case FadeOutHandleItem:
969 RegionSelection s = get_equivalent_regions (selection->regions, Properties::select.property_id);
970 _drags->set (new FadeOutDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), s), event, _cursors->fade_out);
974 case StartCrossFadeItem:
975 case EndCrossFadeItem:
976 /* we might allow user to grab inside the fade to trim a region with preserve_fade_anchor. for not this is not fully implemented */
977 // if (!clicked_regionview->region()->locked()) {
978 // RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
979 // _drags->set (new TrimDrag (this, item, clicked_regionview, s.by_layer(), true), event);
984 case FeatureLineItem:
986 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier)) {
987 remove_transient(item);
991 _drags->set (new FeatureLineDrag (this, item), event);
997 if (dynamic_cast<AutomationRegionView*> (clicked_regionview)) {
998 /* click on an automation region view; do nothing here and let the ARV's signal handler
1004 if (internal_editing ()) {
1005 if (event->type == GDK_2BUTTON_PRESS && event->button.button == 1) {
1006 Glib::RefPtr<Action> act = ActionManager::get_action (X_("MouseMode"), X_("toggle-internal-edit"));
1012 /* click on a normal region view */
1013 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
1014 add_region_copy_drag (item, event, clicked_regionview);
1015 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
1016 add_region_brush_drag (item, event, clicked_regionview);
1018 add_region_drag (item, event, clicked_regionview);
1022 // if (!internal_editing() && (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE && !selection->regions.empty())) {
1023 // _drags->add (new SelectionDrag (this, clicked_axisview->get_selection_rect (clicked_selection)->rect, SelectionDrag::SelectionMove));
1026 _drags->start_grab (event);
1030 case RegionViewNameHighlight:
1031 case LeftFrameHandle:
1032 case RightFrameHandle:
1033 if (!clicked_regionview->region()->locked()) {
1034 RegionSelection s = get_equivalent_regions (selection->regions, Properties::select.property_id);
1035 _drags->set (new TrimDrag (this, item, clicked_regionview, s.by_layer()), event);
1040 case RegionViewName:
1042 /* rename happens on edit clicks */
1043 RegionSelection s = get_equivalent_regions (selection->regions, Properties::select.property_id);
1044 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, s.by_layer()), event);
1049 case ControlPointItem:
1050 _drags->set (new ControlPointDrag (this, item), event);
1054 case AutomationLineItem:
1055 _drags->set (new LineDrag (this, item), event);
1060 if (internal_editing()) {
1061 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
1062 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
1066 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
1070 case AutomationTrackItem:
1072 TimeAxisView* parent = clicked_axisview->get_parent ();
1073 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (clicked_axisview);
1075 if (parent && dynamic_cast<MidiTimeAxisView*> (parent) && atv->show_regions ()) {
1077 RouteTimeAxisView* p = dynamic_cast<RouteTimeAxisView*> (parent);
1079 boost::shared_ptr<Playlist> pl = p->track()->playlist ();
1080 if (pl->n_regions() == 0) {
1081 /* Parent has no regions; create one so that we have somewhere to put automation */
1082 _drags->set (new RegionCreateDrag (this, item, parent), event);
1084 /* See if there's a region before the click that we can extend, and extend it if so */
1085 framepos_t const t = event_frame (event);
1086 boost::shared_ptr<Region> prev = pl->find_next_region (t, End, -1);
1088 _drags->set (new RegionCreateDrag (this, item, parent), event);
1090 prev->set_length (t - prev->position ());
1094 /* rubberband drag to select automation points */
1095 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
1102 if ( get_smart_mode() ) {
1103 /* we're in "smart" joined mode, and we've clicked on a Selection */
1104 double const y = event->button.y + vertical_adjustment.get_value();
1105 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
1107 /* if we're over an automation track, start a drag of its data */
1108 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
1110 _drags->set (new AutomationRangeDrag (this, atv, selection->time), event, _cursors->up_down);
1113 /* if we're over a track and a region, and in the `object' part of a region,
1114 put a selection around the region and drag both
1116 /* RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1117 if (rtv && _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
1118 boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track> (rtv->route ());
1120 boost::shared_ptr<Playlist> pl = t->playlist ();
1123 boost::shared_ptr<Region> r = pl->top_region_at (event_frame (event));
1125 RegionView* rv = rtv->view()->find_view (r);
1126 clicked_selection = select_range (rv->region()->position(),
1127 rv->region()->last_frame()+1);
1128 _drags->add (new SelectionDrag (this, item, SelectionDrag::SelectionMove));
1129 list<RegionView*> rvs;
1131 _drags->add (new RegionMoveDrag (this, item, rv, rvs, false, false));
1132 _drags->start_grab (event);
1145 case ImageFrameHandleStartItem:
1146 imageframe_start_handle_op(item, event) ;
1149 case ImageFrameHandleEndItem:
1150 imageframe_end_handle_op(item, event) ;
1153 case MarkerViewHandleStartItem:
1154 markerview_item_start_handle_op(item, event) ;
1157 case MarkerViewHandleEndItem:
1158 markerview_item_end_handle_op(item, event) ;
1161 case MarkerViewItem:
1162 start_markerview_grab(item, event) ;
1164 case ImageFrameItem:
1165 start_imageframe_grab(item, event) ;
1181 switch (item_type) {
1183 _drags->set (new LineDrag (this, item), event);
1186 case ControlPointItem:
1187 _drags->set (new ControlPointDrag (this, item), event);
1193 AudioRegionView* arv = dynamic_cast<AudioRegionView *> (clicked_regionview);
1195 _drags->set (new AutomationRangeDrag (this, arv, selection->time), event, _cursors->up_down);
1196 _drags->start_grab (event);
1202 case AutomationLineItem:
1203 _drags->set (new LineDrag (this, item), event);
1213 if (event->type == GDK_BUTTON_PRESS) {
1214 _drags->set (new MouseZoomDrag (this, item), event);
1221 if (internal_editing() && item_type == NoteItem) {
1222 /* drag notes if we're in internal edit mode */
1223 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
1225 } else if (clicked_regionview) {
1227 _drags->set (new TimeFXDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1233 _drags->set (new ScrubDrag (this, item), event);
1234 scrub_reversals = 0;
1235 scrub_reverse_distance = 0;
1236 last_scrub_x = event->button.x;
1237 scrubbing_direction = 0;
1238 set_canvas_cursor (_cursors->transparent);
1250 Editor::button_press_handler_2 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1252 Editing::MouseMode const eff = effective_mouse_mode ();
1255 switch (item_type) {
1257 if (internal_editing ()) {
1258 /* no region drags in internal edit mode */
1262 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
1263 add_region_copy_drag (item, event, clicked_regionview);
1265 add_region_drag (item, event, clicked_regionview);
1267 _drags->start_grab (event);
1270 case ControlPointItem:
1271 _drags->set (new ControlPointDrag (this, item), event);
1279 switch (item_type) {
1280 case RegionViewNameHighlight:
1281 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1285 case LeftFrameHandle:
1286 case RightFrameHandle:
1287 if (!internal_editing ()) {
1288 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1293 case RegionViewName:
1294 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer()), event);
1308 /* relax till release */
1314 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1315 temporal_zoom_to_frame (false, event_frame (event));
1317 temporal_zoom_to_frame (true, event_frame(event));
1330 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1332 if (event->type != GDK_BUTTON_PRESS) {
1336 Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->_track_canvas_viewport->get_window();
1338 if (canvas_window) {
1339 Glib::RefPtr<const Gdk::Window> pointer_window;
1342 Gdk::ModifierType mask;
1344 pointer_window = canvas_window->get_pointer (x, y, mask);
1346 if (pointer_window == _track_canvas->get_window()) {
1347 _track_canvas_viewport->window_to_canvas (x, y, wx, wy);
1351 pre_press_cursor = current_canvas_cursor;
1353 _track_canvas->grab_focus();
1355 if (_session && _session->actively_recording()) {
1359 if (internal_editing()) {
1360 bool leave_internal_edit_mode = false;
1362 switch (item_type) {
1367 if (!dynamic_cast<MidiRegionView*> (clicked_regionview) && !dynamic_cast<AutomationRegionView*> (clicked_regionview)) {
1368 leave_internal_edit_mode = true;
1372 case PlayheadCursorItem:
1374 case TempoMarkerItem:
1375 case MeterMarkerItem:
1379 case RangeMarkerBarItem:
1380 case CdMarkerBarItem:
1381 case TransportMarkerBarItem:
1383 /* button press on these events never does anything to
1384 change the editing mode.
1392 if (leave_internal_edit_mode) {
1393 ActionManager::do_action ("MouseMode", "toggle-internal-edit");
1397 button_selection (item, event, item_type);
1399 if (!_drags->active () &&
1400 (Keyboard::is_delete_event (&event->button) ||
1401 Keyboard::is_context_menu_event (&event->button) ||
1402 Keyboard::is_edit_event (&event->button))) {
1404 /* handled by button release */
1408 //not rolling, range mode click + join_play_range : locate the PH here
1409 if ( !_drags->active () && !_session->transport_rolling() && ( effective_mouse_mode() == MouseRange ) && Config->get_always_play_range() ) {
1410 framepos_t where = event_frame (event, 0, 0);
1412 _session->request_locate (where, false);
1415 switch (event->button.button) {
1417 return button_press_handler_1 (item, event, item_type);
1421 return button_press_handler_2 (item, event, item_type);
1428 return button_press_dispatch (&event->button);
1437 Editor::button_press_dispatch (GdkEventButton* ev)
1439 /* this function is intended only for buttons 4 and above.
1442 Gtkmm2ext::MouseButton b (ev->state, ev->button);
1443 return button_bindings->activate (b, Gtkmm2ext::Bindings::Press);
1447 Editor::button_release_dispatch (GdkEventButton* ev)
1449 /* this function is intended only for buttons 4 and above.
1452 Gtkmm2ext::MouseButton b (ev->state, ev->button);
1453 return button_bindings->activate (b, Gtkmm2ext::Bindings::Release);
1457 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1459 framepos_t where = event_frame (event, 0, 0);
1460 AutomationTimeAxisView* atv = 0;
1462 if (pre_press_cursor) {
1463 set_canvas_cursor (pre_press_cursor);
1464 pre_press_cursor = 0;
1467 /* no action if we're recording */
1469 if (_session && _session->actively_recording()) {
1473 /* see if we're finishing a drag */
1475 bool were_dragging = false;
1476 if (_drags->active ()) {
1477 bool const r = _drags->end_grab (event);
1479 /* grab dragged, so do nothing else */
1483 were_dragging = true;
1486 update_region_layering_order_editor ();
1488 /* edit events get handled here */
1490 if (!_drags->active () && Keyboard::is_edit_event (&event->button)) {
1491 switch (item_type) {
1493 show_region_properties ();
1496 case TempoMarkerItem:
1497 edit_tempo_marker (item);
1500 case MeterMarkerItem:
1501 edit_meter_marker (item);
1504 case RegionViewName:
1505 if (clicked_regionview->name_active()) {
1506 return mouse_rename_region (item, event);
1510 case ControlPointItem:
1511 edit_control_point (item);
1516 NoteBase* e = reinterpret_cast<NoteBase*> (item->get_data ("notebase"));
1518 edit_notes (e->region_view().selection ());
1528 /* context menu events get handled here */
1529 if (Keyboard::is_context_menu_event (&event->button)) {
1531 context_click_event = *event;
1533 if (!_drags->active ()) {
1535 /* no matter which button pops up the context menu, tell the menu
1536 widget to use button 1 to drive menu selection.
1539 switch (item_type) {
1541 case FadeInHandleItem:
1543 case FadeOutHandleItem:
1544 popup_fade_context_menu (1, event->button.time, item, item_type);
1547 case StartCrossFadeItem:
1548 popup_xfade_in_context_menu (1, event->button.time, item, item_type);
1551 case EndCrossFadeItem:
1552 popup_xfade_out_context_menu (1, event->button.time, item, item_type);
1556 popup_track_context_menu (1, event->button.time, item_type, false);
1560 case RegionViewNameHighlight:
1561 case LeftFrameHandle:
1562 case RightFrameHandle:
1563 case RegionViewName:
1564 popup_track_context_menu (1, event->button.time, item_type, false);
1568 popup_track_context_menu (1, event->button.time, item_type, true);
1571 case AutomationTrackItem:
1572 popup_track_context_menu (1, event->button.time, item_type, false);
1576 case RangeMarkerBarItem:
1577 case TransportMarkerBarItem:
1578 case CdMarkerBarItem:
1581 #ifdef WITH_VIDEOTIMELINE
1584 popup_ruler_menu (where, item_type);
1588 marker_context_menu (&event->button, item);
1591 case TempoMarkerItem:
1592 tempo_or_meter_marker_context_menu (&event->button, item);
1595 case MeterMarkerItem:
1596 tempo_or_meter_marker_context_menu (&event->button, item);
1599 case CrossfadeViewItem:
1600 popup_track_context_menu (1, event->button.time, item_type, false);
1603 case ControlPointItem:
1604 popup_control_point_context_menu (item, event);
1608 case ImageFrameItem:
1609 popup_imageframe_edit_menu(1, event->button.time, item, true) ;
1611 case ImageFrameTimeAxisItem:
1612 popup_imageframe_edit_menu(1, event->button.time, item, false) ;
1614 case MarkerViewItem:
1615 popup_marker_time_axis_edit_menu(1, event->button.time, item, true) ;
1617 case MarkerTimeAxisItem:
1618 popup_marker_time_axis_edit_menu(1, event->button.time, item, false) ;
1630 /* delete events get handled here */
1632 Editing::MouseMode const eff = effective_mouse_mode ();
1634 if (!_drags->active () && Keyboard::is_delete_event (&event->button)) {
1636 switch (item_type) {
1637 case TempoMarkerItem:
1638 remove_tempo_marker (item);
1641 case MeterMarkerItem:
1642 remove_meter_marker (item);
1646 remove_marker (*item, event);
1650 if (eff == MouseObject) {
1651 remove_clicked_region ();
1655 case ControlPointItem:
1656 remove_control_point (item);
1660 remove_midi_note (item, event);
1669 switch (event->button.button) {
1672 switch (item_type) {
1673 /* see comments in button_press_handler */
1674 case PlayheadCursorItem:
1677 case AutomationLineItem:
1678 case StartSelectionTrimItem:
1679 case EndSelectionTrimItem:
1683 if (!_dragging_playhead) {
1684 snap_to_with_modifier (where, event, 0, true);
1685 mouse_add_new_marker (where);
1689 case CdMarkerBarItem:
1690 if (!_dragging_playhead) {
1691 // if we get here then a dragged range wasn't done
1692 snap_to_with_modifier (where, event, 0, true);
1693 mouse_add_new_marker (where, true);
1698 if (!_dragging_playhead) {
1699 snap_to_with_modifier (where, event);
1700 mouse_add_new_tempo_event (where);
1705 if (!_dragging_playhead) {
1706 mouse_add_new_meter_event (pixel_to_frame (event->button.x));
1717 switch (item_type) {
1718 case AutomationTrackItem:
1719 atv = dynamic_cast<AutomationTimeAxisView*>(clicked_axisview);
1721 atv->add_automation_event (event, where, event->button.y);
1731 switch (item_type) {
1734 /* check that we didn't drag before releasing, since
1735 its really annoying to create new control
1736 points when doing this.
1738 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (clicked_regionview);
1739 if (!were_dragging && arv) {
1740 arv->add_gain_point_event (item, event);
1746 case AutomationTrackItem:
1747 dynamic_cast<AutomationTimeAxisView*>(clicked_axisview)->
1748 add_automation_event (event, where, event->button.y);
1757 set_canvas_cursor (current_canvas_cursor);
1758 if (scrubbing_direction == 0) {
1759 /* no drag, just a click */
1760 switch (item_type) {
1762 play_selected_region ();
1768 /* make sure we stop */
1769 _session->request_transport_speed (0.0);
1778 /* do any (de)selection operations that should occur on button release */
1779 button_selection (item, event, item_type);
1788 switch (item_type) {
1790 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1792 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier|Keyboard::SecondaryModifier))) {
1795 // Button2 click is unused
1810 // x_style_paste (where, 1.0);
1831 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1838 switch (item_type) {
1839 case ControlPointItem:
1840 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1841 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1842 cp->set_visible (true);
1846 at_y = cp->get_y ();
1847 cp->i2w (at_x, at_y);
1851 fraction = 1.0 - (cp->get_y() / cp->line().height());
1853 if (is_drawable() && !_drags->active ()) {
1854 set_canvas_cursor (_cursors->fader);
1857 _verbose_cursor->set (cp->line().get_verbose_cursor_string (fraction), at_x, at_y);
1858 _verbose_cursor->show ();
1863 if (mouse_mode == MouseGain) {
1864 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1866 line->set_outline_color (ARDOUR_UI::config()->canvasvar_EnteredGainLine.get());
1868 if (is_drawable()) {
1869 set_canvas_cursor (_cursors->fader);
1874 case AutomationLineItem:
1875 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1876 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1878 line->set_outline_color (ARDOUR_UI::config()->canvasvar_EnteredAutomationLine.get());
1880 if (is_drawable()) {
1881 set_canvas_cursor (_cursors->fader);
1886 case RegionViewNameHighlight:
1887 if (is_drawable() && effective_mouse_mode() == MouseObject && entered_regionview) {
1888 set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1889 _over_region_trim_target = true;
1893 case LeftFrameHandle:
1894 case RightFrameHandle:
1895 if (is_drawable() && effective_mouse_mode() == MouseObject && !internal_editing() && entered_regionview) {
1896 set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1900 case StartSelectionTrimItem:
1902 case ImageFrameHandleStartItem:
1903 case MarkerViewHandleStartItem:
1905 if (is_drawable()) {
1906 set_canvas_cursor (_cursors->left_side_trim);
1909 case EndSelectionTrimItem:
1911 case ImageFrameHandleEndItem:
1912 case MarkerViewHandleEndItem:
1914 if (is_drawable()) {
1915 set_canvas_cursor (_cursors->right_side_trim);
1919 case PlayheadCursorItem:
1920 if (is_drawable()) {
1921 switch (_edit_point) {
1923 set_canvas_cursor (_cursors->grabber_edit_point);
1926 set_canvas_cursor (_cursors->grabber);
1932 case RegionViewName:
1934 /* when the name is not an active item, the entire name highlight is for trimming */
1936 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1937 if (mouse_mode == MouseObject && is_drawable()) {
1938 set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1939 _over_region_trim_target = true;
1945 case AutomationTrackItem:
1946 if (is_drawable()) {
1947 Gdk::Cursor *cursor;
1948 switch (mouse_mode) {
1950 cursor = _cursors->selector;
1953 cursor = _cursors->zoom_in;
1956 cursor = _cursors->cross_hair;
1960 set_canvas_cursor (cursor);
1962 AutomationTimeAxisView* atv;
1963 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1964 clear_entered_track = false;
1965 set_entered_track (atv);
1971 case RangeMarkerBarItem:
1972 case TransportMarkerBarItem:
1973 case CdMarkerBarItem:
1976 if (is_drawable()) {
1977 set_canvas_cursor (_cursors->timebar);
1982 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1985 entered_marker = marker;
1986 marker->set_color_rgba (ARDOUR_UI::config()->canvasvar_EnteredMarker.get());
1988 case MeterMarkerItem:
1989 case TempoMarkerItem:
1990 if (is_drawable()) {
1991 set_canvas_cursor (_cursors->timebar);
1995 case FadeInHandleItem:
1996 if (mouse_mode == MouseObject && !internal_editing()) {
1997 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
1999 rect->set_fill_color (0xBBBBBBAA);
2001 set_canvas_cursor (_cursors->fade_in);
2005 case FadeOutHandleItem:
2006 if (mouse_mode == MouseObject && !internal_editing()) {
2007 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
2009 rect->set_fill_color (0xBBBBBBAA);
2011 set_canvas_cursor (_cursors->fade_out);
2014 case FeatureLineItem:
2016 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
2017 line->set_outline_color (0xFF0000FF);
2022 if ( get_smart_mode() ) {
2023 set_canvas_cursor ();
2031 /* second pass to handle entered track status in a comprehensible way.
2034 switch (item_type) {
2036 case AutomationLineItem:
2037 case ControlPointItem:
2038 /* these do not affect the current entered track state */
2039 clear_entered_track = false;
2042 case AutomationTrackItem:
2043 /* handled above already */
2047 set_entered_track (0);
2055 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent*, ItemType item_type)
2065 switch (item_type) {
2066 case ControlPointItem:
2067 cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
2068 if (cp->line().the_list()->interpolation() != AutomationList::Discrete) {
2069 if (cp->line().npoints() > 1 && !cp->get_selected()) {
2070 cp->set_visible (false);
2074 if (is_drawable()) {
2075 set_canvas_cursor (current_canvas_cursor);
2078 _verbose_cursor->hide ();
2081 case RegionViewNameHighlight:
2082 case LeftFrameHandle:
2083 case RightFrameHandle:
2084 case StartSelectionTrimItem:
2085 case EndSelectionTrimItem:
2086 case PlayheadCursorItem:
2089 case ImageFrameHandleStartItem:
2090 case ImageFrameHandleEndItem:
2091 case MarkerViewHandleStartItem:
2092 case MarkerViewHandleEndItem:
2095 _over_region_trim_target = false;
2097 if (is_drawable()) {
2098 set_canvas_cursor (current_canvas_cursor);
2103 case AutomationLineItem:
2104 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
2106 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
2108 line->set_outline_color (al->get_line_color());
2111 if (is_drawable()) {
2112 set_canvas_cursor (current_canvas_cursor);
2116 case RegionViewName:
2117 /* see enter_handler() for notes */
2118 _over_region_trim_target = false;
2120 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
2121 if (is_drawable() && mouse_mode == MouseObject) {
2122 set_canvas_cursor (current_canvas_cursor);
2127 case RangeMarkerBarItem:
2128 case TransportMarkerBarItem:
2129 case CdMarkerBarItem:
2133 if (is_drawable()) {
2134 set_canvas_cursor (current_canvas_cursor);
2139 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2143 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
2144 location_flags_changed (loc, this);
2147 case MeterMarkerItem:
2148 case TempoMarkerItem:
2150 if (is_drawable()) {
2151 set_canvas_cursor (current_canvas_cursor);
2156 case FadeInHandleItem:
2157 case FadeOutHandleItem:
2158 rv = static_cast<RegionView*>(item->get_data ("regionview"));
2160 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
2162 rect->set_fill_color (rv->get_fill_color());
2165 set_canvas_cursor (current_canvas_cursor);
2168 case AutomationTrackItem:
2169 if (is_drawable()) {
2170 set_canvas_cursor (current_canvas_cursor);
2171 clear_entered_track = true;
2172 Glib::signal_idle().connect (sigc::mem_fun(*this, &Editor::left_automation_track));
2175 case FeatureLineItem:
2177 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
2178 line->set_outline_color (ARDOUR_UI::config()->canvasvar_ZeroLine.get());
2190 Editor::left_automation_track ()
2192 if (clear_entered_track) {
2193 set_entered_track (0);
2194 clear_entered_track = false;
2200 Editor::scrub (framepos_t frame, double current_x)
2204 if (scrubbing_direction == 0) {
2206 _session->request_locate (frame, false);
2207 _session->request_transport_speed (0.1);
2208 scrubbing_direction = 1;
2212 if (last_scrub_x > current_x) {
2214 /* pointer moved to the left */
2216 if (scrubbing_direction > 0) {
2218 /* we reversed direction to go backwards */
2221 scrub_reverse_distance += (int) (last_scrub_x - current_x);
2225 /* still moving to the left (backwards) */
2227 scrub_reversals = 0;
2228 scrub_reverse_distance = 0;
2230 delta = 0.01 * (last_scrub_x - current_x);
2231 _session->request_transport_speed_nonzero (_session->transport_speed() - delta);
2235 /* pointer moved to the right */
2237 if (scrubbing_direction < 0) {
2238 /* we reversed direction to go forward */
2241 scrub_reverse_distance += (int) (current_x - last_scrub_x);
2244 /* still moving to the right */
2246 scrub_reversals = 0;
2247 scrub_reverse_distance = 0;
2249 delta = 0.01 * (current_x - last_scrub_x);
2250 _session->request_transport_speed_nonzero (_session->transport_speed() + delta);
2254 /* if there have been more than 2 opposite motion moves detected, or one that moves
2255 back more than 10 pixels, reverse direction
2258 if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
2260 if (scrubbing_direction > 0) {
2261 /* was forwards, go backwards */
2262 _session->request_transport_speed (-0.1);
2263 scrubbing_direction = -1;
2265 /* was backwards, go forwards */
2266 _session->request_transport_speed (0.1);
2267 scrubbing_direction = 1;
2270 scrub_reverse_distance = 0;
2271 scrub_reversals = 0;
2275 last_scrub_x = current_x;
2279 Editor::motion_handler (ArdourCanvas::Item* /*item*/, GdkEvent* event, bool from_autoscroll)
2281 _last_motion_y = event->motion.y;
2283 if (event->motion.is_hint) {
2286 /* We call this so that MOTION_NOTIFY events continue to be
2287 delivered to the canvas. We need to do this because we set
2288 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
2289 the density of the events, at the expense of a round-trip
2290 to the server. Given that this will mostly occur on cases
2291 where DISPLAY = :0.0, and given the cost of what the motion
2292 event might do, its a good tradeoff.
2295 _track_canvas->get_pointer (x, y);
2298 if (current_stepping_trackview) {
2299 /* don't keep the persistent stepped trackview if the mouse moves */
2300 current_stepping_trackview = 0;
2301 step_timeout.disconnect ();
2304 if (_session && _session->actively_recording()) {
2305 /* Sorry. no dragging stuff around while we record */
2309 JoinObjectRangeState const old = _join_object_range_state;
2310 update_join_object_range_location (event->motion.x, event->motion.y);
2312 if (!_internal_editing && _join_object_range_state != old) {
2313 set_canvas_cursor ();
2316 if (!_internal_editing && _over_region_trim_target) {
2317 set_canvas_cursor_for_region_view (event->motion.x, entered_regionview);
2320 bool handled = false;
2321 if (_drags->active ()) {
2322 handled = _drags->motion_handler (event, from_autoscroll);
2329 track_canvas_motion (event);
2334 Editor::can_remove_control_point (ArdourCanvas::Item* item)
2336 ControlPoint* control_point;
2338 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2339 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2343 AutomationLine& line = control_point->line ();
2344 if (dynamic_cast<AudioRegionGainLine*> (&line)) {
2345 /* we shouldn't remove the first or last gain point in region gain lines */
2346 if (line.is_last_point(*control_point) || line.is_first_point(*control_point)) {
2355 Editor::remove_control_point (ArdourCanvas::Item* item)
2357 if (!can_remove_control_point (item)) {
2361 ControlPoint* control_point;
2363 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2364 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2368 control_point->line().remove_point (*control_point);
2372 Editor::edit_control_point (ArdourCanvas::Item* item)
2374 ControlPoint* p = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"));
2377 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2381 ControlPointDialog d (p);
2382 d.set_position (Gtk::WIN_POS_MOUSE);
2385 if (d.run () != RESPONSE_ACCEPT) {
2389 p->line().modify_point_y (*p, d.get_y_fraction ());
2393 Editor::edit_notes (MidiRegionView::Selection const & s)
2399 EditNoteDialog d (&(*s.begin())->region_view(), s);
2400 d.set_position (Gtk::WIN_POS_MOUSE);
2408 Editor::visible_order_range (int* low, int* high) const
2410 *low = TimeAxisView::max_order ();
2413 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
2415 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
2417 if (!rtv->hidden()) {
2419 if (*high < rtv->order()) {
2420 *high = rtv->order ();
2423 if (*low > rtv->order()) {
2424 *low = rtv->order ();
2431 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
2433 /* Either add to or set the set the region selection, unless
2434 this is an alignment click (control used)
2437 if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
2438 TimeAxisView* tv = &rv.get_time_axis_view();
2439 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
2441 if (rtv && rtv->is_track()) {
2442 speed = rtv->track()->speed();
2445 framepos_t where = get_preferred_edit_position();
2449 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
2451 align_region (rv.region(), SyncPoint, (framepos_t) (where * speed));
2453 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
2455 align_region (rv.region(), End, (framepos_t) (where * speed));
2459 align_region (rv.region(), Start, (framepos_t) (where * speed));
2466 Editor::collect_new_region_view (RegionView* rv)
2468 latest_regionviews.push_back (rv);
2472 Editor::collect_and_select_new_region_view (RegionView* rv)
2475 latest_regionviews.push_back (rv);
2479 Editor::cancel_selection ()
2481 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2482 (*i)->hide_selection ();
2485 selection->clear ();
2486 clicked_selection = 0;
2490 Editor::cancel_time_selection ()
2492 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2493 (*i)->hide_selection ();
2495 selection->time.clear ();
2496 clicked_selection = 0;
2500 Editor::point_trim (GdkEvent* event, framepos_t new_bound)
2502 RegionView* rv = clicked_regionview;
2504 /* Choose action dependant on which button was pressed */
2505 switch (event->button.button) {
2507 begin_reversible_command (_("start point trim"));
2509 if (selection->selected (rv)) {
2510 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
2511 i != selection->regions.by_layer().end(); ++i)
2513 if (!(*i)->region()->locked()) {
2514 (*i)->region()->clear_changes ();
2515 (*i)->region()->trim_front (new_bound);
2516 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2521 if (!rv->region()->locked()) {
2522 rv->region()->clear_changes ();
2523 rv->region()->trim_front (new_bound);
2524 _session->add_command(new StatefulDiffCommand (rv->region()));
2528 commit_reversible_command();
2532 begin_reversible_command (_("End point trim"));
2534 if (selection->selected (rv)) {
2536 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
2538 if (!(*i)->region()->locked()) {
2539 (*i)->region()->clear_changes();
2540 (*i)->region()->trim_end (new_bound);
2541 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2547 if (!rv->region()->locked()) {
2548 rv->region()->clear_changes ();
2549 rv->region()->trim_end (new_bound);
2550 _session->add_command (new StatefulDiffCommand (rv->region()));
2554 commit_reversible_command();
2563 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* /*event*/)
2568 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2569 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2573 Location* location = find_location_from_marker (marker, is_start);
2574 location->set_hidden (true, this);
2579 Editor::reposition_zoom_rect (framepos_t start, framepos_t end)
2581 double x1 = frame_to_pixel (start);
2582 double x2 = frame_to_pixel (end);
2583 double y2 = _full_canvas_height - 1.0;
2585 zoom_rect->set (ArdourCanvas::Rect (x1, 1.0, x2, y2));
2590 Editor::mouse_rename_region (ArdourCanvas::Item* /*item*/, GdkEvent* /*event*/)
2592 using namespace Gtkmm2ext;
2594 ArdourPrompter prompter (false);
2596 prompter.set_prompt (_("Name for region:"));
2597 prompter.set_initial_text (clicked_regionview->region()->name());
2598 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
2599 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
2600 prompter.show_all ();
2601 switch (prompter.run ()) {
2602 case Gtk::RESPONSE_ACCEPT:
2604 prompter.get_result(str);
2606 clicked_regionview->region()->set_name (str);
2615 Editor::mouse_brush_insert_region (RegionView* rv, framepos_t pos)
2617 /* no brushing without a useful snap setting */
2619 switch (_snap_mode) {
2621 return; /* can't work because it allows region to be placed anywhere */
2626 switch (_snap_type) {
2634 /* don't brush a copy over the original */
2636 if (pos == rv->region()->position()) {
2640 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
2642 if (rtv == 0 || !rtv->is_track()) {
2646 boost::shared_ptr<Playlist> playlist = rtv->playlist();
2647 double speed = rtv->track()->speed();
2649 playlist->clear_changes ();
2650 boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region(), true));
2651 playlist->add_region (new_region, (framepos_t) (pos * speed));
2652 _session->add_command (new StatefulDiffCommand (playlist));
2654 // playlist is frozen, so we have to update manually XXX this is disgusting
2656 playlist->RegionAdded (new_region); /* EMIT SIGNAL */
2660 Editor::track_height_step_timeout ()
2662 if (get_microseconds() - last_track_height_step_timestamp < 250000) {
2663 current_stepping_trackview = 0;
2670 Editor::add_region_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2672 assert (region_view);
2674 if (!region_view->region()->playlist()) {
2678 _region_motion_group->raise_to_top ();
2680 if (Config->get_edit_mode() == Splice) {
2681 _drags->add (new RegionSpliceDrag (this, item, region_view, selection->regions.by_layer()));
2683 RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::select.property_id);
2684 _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), false, false));
2687 /* sync the canvas to what we think is its current state */
2688 update_canvas_now();
2692 Editor::add_region_copy_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2694 assert (region_view);
2696 if (!region_view->region()->playlist()) {
2700 _region_motion_group->raise_to_top ();
2702 RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::select.property_id);
2703 _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), false, true));
2707 Editor::add_region_brush_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2709 assert (region_view);
2711 if (!region_view->region()->playlist()) {
2715 if (Config->get_edit_mode() == Splice) {
2719 RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::select.property_id);
2720 _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), true, false));
2722 begin_reversible_command (Operations::drag_region_brush);
2725 /** Start a grab where a time range is selected, track(s) are selected, and the
2726 * user clicks and drags a region with a modifier in order to create a new region containing
2727 * the section of the clicked region that lies within the time range.
2730 Editor::start_selection_grab (ArdourCanvas::Item* /*item*/, GdkEvent* event)
2732 if (clicked_regionview == 0) {
2736 /* lets try to create new Region for the selection */
2738 vector<boost::shared_ptr<Region> > new_regions;
2739 create_region_from_selection (new_regions);
2741 if (new_regions.empty()) {
2745 /* XXX fix me one day to use all new regions */
2747 boost::shared_ptr<Region> region (new_regions.front());
2749 /* add it to the current stream/playlist.
2751 tricky: the streamview for the track will add a new regionview. we will
2752 catch the signal it sends when it creates the regionview to
2753 set the regionview we want to then drag.
2756 latest_regionviews.clear();
2757 sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
2759 /* A selection grab currently creates two undo/redo operations, one for
2760 creating the new region and another for moving it.
2763 begin_reversible_command (Operations::selection_grab);
2765 boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
2767 playlist->clear_changes ();
2768 clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
2769 _session->add_command(new StatefulDiffCommand (playlist));
2771 commit_reversible_command ();
2775 if (latest_regionviews.empty()) {
2776 /* something went wrong */
2780 /* we need to deselect all other regionviews, and select this one
2781 i'm ignoring undo stuff, because the region creation will take care of it
2783 selection->set (latest_regionviews);
2785 _drags->set (new RegionMoveDrag (this, latest_regionviews.front()->get_canvas_group(), latest_regionviews.front(), latest_regionviews, false, false), event);
2791 if (_drags->active ()) {
2794 selection->clear ();
2799 Editor::set_internal_edit (bool yn)
2801 if (_internal_editing == yn) {
2805 _internal_editing = yn;
2808 pre_internal_mouse_mode = mouse_mode;
2809 pre_internal_snap_type = _snap_type;
2810 pre_internal_snap_mode = _snap_mode;
2812 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2813 (*i)->enter_internal_edit_mode ();
2816 set_snap_to (internal_snap_type);
2817 set_snap_mode (internal_snap_mode);
2821 internal_snap_mode = _snap_mode;
2822 internal_snap_type = _snap_type;
2824 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2825 (*i)->leave_internal_edit_mode ();
2828 if (mouse_mode == MouseDraw && pre_internal_mouse_mode != MouseDraw) {
2829 /* we were drawing .. flip back to something sensible */
2830 set_mouse_mode (pre_internal_mouse_mode);
2833 set_snap_to (pre_internal_snap_type);
2834 set_snap_mode (pre_internal_snap_mode);
2837 set_canvas_cursor ();
2840 /** Update _join_object_range_state which indicate whether we are over the top or bottom half of a region view,
2841 * used by the `join object/range' tool mode.
2844 Editor::update_join_object_range_location (double /*x*/, double y)
2846 /* XXX: actually, this decides based on whether the mouse is in the top
2847 or bottom half of a the waveform part RouteTimeAxisView;
2849 Note that entered_{track,regionview} is not always setup (e.g. if
2850 the mouse is over a TimeSelection), and to get a Region
2851 that we're over requires searching the playlist.
2854 if ( !get_smart_mode() ) {
2855 _join_object_range_state = JOIN_OBJECT_RANGE_NONE;
2859 if (mouse_mode == MouseObject) {
2860 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2861 } else if (mouse_mode == MouseRange) {
2862 _join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
2865 /* XXX: maybe we should make entered_track work in all cases, rather than resorting to this */
2866 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y + vertical_adjustment.get_value());
2870 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
2875 rtv->canvas_display()->canvas_to_item (cx, cy);
2877 double const c = cy / (rtv->view()->child_height() - TimeAxisViewItem::NAME_HIGHLIGHT_SIZE);
2879 _join_object_range_state = c <= 0.5 ? JOIN_OBJECT_RANGE_RANGE : JOIN_OBJECT_RANGE_OBJECT;
2885 Editor::effective_mouse_mode () const
2887 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
2889 } else if (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE) {
2897 Editor::remove_midi_note (ArdourCanvas::Item* item, GdkEvent *)
2899 NoteBase* e = reinterpret_cast<NoteBase*> (item->get_data ("notebase"));
2902 e->region_view().delete_note (e->note ());
2906 Editor::set_canvas_cursor_for_region_view (double x, RegionView* rv)
2908 /* XXX: this check should not be necessary */
2915 ArdourCanvas::Group* g = rv->get_canvas_group ();
2916 ArdourCanvas::Group* p = g->parent ();
2918 /* Compute x in region view parent coordinates */
2920 p->canvas_to_item (x, dy);
2922 boost::optional<ArdourCanvas::Rect> item_bbox = g->bounding_box ();
2924 ArdourCanvas::Rect parent_bbox = g->item_to_parent (item_bbox.get ());
2926 /* Halfway across the region */
2927 double const h = (parent_bbox.x0 + parent_bbox.x1) / 2;
2929 Trimmable::CanTrim ct = rv->region()->can_trim ();
2931 if (ct & Trimmable::FrontTrimEarlier) {
2932 set_canvas_cursor (_cursors->left_side_trim);
2934 set_canvas_cursor (_cursors->left_side_trim_right_only);
2937 if (ct & Trimmable::EndTrimLater) {
2938 set_canvas_cursor (_cursors->right_side_trim);
2940 set_canvas_cursor (_cursors->right_side_trim_left_only);
2945 /** Obtain the pointer position in canvas coordinates */
2947 Editor::get_pointer_position (double& x, double& y) const
2950 _track_canvas->get_pointer (px, py);
2951 _track_canvas_viewport->window_to_canvas (px, py, x, y);