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.
28 #include "pbd/error.h"
29 #include "pbd/enumwriter.h"
30 #include "pbd/memento_command.h"
31 #include "pbd/basename.h"
32 #include "pbd/stateful_diff_command.h"
34 #include "gtkmm2ext/bindings.h"
35 #include "gtkmm2ext/utils.h"
36 #include "gtkmm2ext/tearoff.h"
38 #include "ardour_ui.h"
40 #include "canvas-note.h"
42 #include "time_axis_view.h"
43 #include "audio_time_axis.h"
44 #include "audio_region_view.h"
45 #include "midi_region_view.h"
47 #include "streamview.h"
48 #include "region_gain_line.h"
49 #include "automation_time_axis.h"
50 #include "control_point.h"
53 #include "selection.h"
56 #include "rgb_macros.h"
57 #include "control_point_dialog.h"
58 #include "editor_drag.h"
59 #include "automation_region_view.h"
60 #include "edit_note_dialog.h"
61 #include "mouse_cursors.h"
62 #include "editor_cursors.h"
63 #include "verbose_cursor.h"
65 #include "ardour/types.h"
66 #include "ardour/profile.h"
67 #include "ardour/route.h"
68 #include "ardour/audio_track.h"
69 #include "ardour/playlist.h"
70 #include "ardour/audioplaylist.h"
71 #include "ardour/audioregion.h"
72 #include "ardour/midi_region.h"
73 #include "ardour/dB.h"
74 #include "ardour/utils.h"
75 #include "ardour/region_factory.h"
76 #include "ardour/source_factory.h"
77 #include "ardour/session.h"
78 #include "ardour/operations.h"
85 using namespace ARDOUR;
88 using namespace Editing;
89 using Gtkmm2ext::Keyboard;
92 Editor::mouse_frame (framepos_t& where, bool& in_track_canvas) const
94 /* gdk_window_get_pointer() has X11's XQueryPointer semantics in that it only
95 pays attentions to subwindows. this means that menu windows are ignored, and
96 if the pointer is in a menu, the return window from the call will be the
97 the regular subwindow *under* the menu.
99 this matters quite a lot if the pointer is moving around in a menu that overlaps
100 the track canvas because we will believe that we are within the track canvas
101 when we are not. therefore, we track enter/leave events for the track canvas
102 and allow that to override the result of gdk_window_get_pointer().
105 if (!within_track_canvas) {
111 Gdk::ModifierType mask;
112 Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->track_canvas->get_window();
113 Glib::RefPtr<const Gdk::Window> pointer_window;
115 if (!canvas_window) {
119 pointer_window = canvas_window->get_pointer (x, y, mask);
121 if (pointer_window == track_canvas->get_bin_window()) {
124 in_track_canvas = true;
127 in_track_canvas = false;
132 event.type = GDK_BUTTON_RELEASE;
136 where = event_frame (&event, 0, 0);
141 Editor::event_frame (GdkEvent const * event, double* pcx, double* pcy) const
155 switch (event->type) {
156 case GDK_BUTTON_RELEASE:
157 case GDK_BUTTON_PRESS:
158 case GDK_2BUTTON_PRESS:
159 case GDK_3BUTTON_PRESS:
160 *pcx = event->button.x;
161 *pcy = event->button.y;
162 _trackview_group->w2i(*pcx, *pcy);
164 case GDK_MOTION_NOTIFY:
165 *pcx = event->motion.x;
166 *pcy = event->motion.y;
167 _trackview_group->w2i(*pcx, *pcy);
169 case GDK_ENTER_NOTIFY:
170 case GDK_LEAVE_NOTIFY:
171 track_canvas->w2c(event->crossing.x, event->crossing.y, *pcx, *pcy);
174 case GDK_KEY_RELEASE:
175 // track_canvas->w2c(event->key.x, event->key.y, *pcx, *pcy);
178 warning << string_compose (_("Editor::event_frame() used on unhandled event type %1"), event->type) << endmsg;
182 /* note that pixel_to_frame() 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_frame (*pcx);
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;
215 switch (_edit_point) {
217 c = _cursors->grabber_edit_point;
220 boost::shared_ptr<Movable> m = _movable.lock();
221 if (m && m->locked()) {
222 c = _cursors->speaker;
232 Editor::set_current_trimmable (boost::shared_ptr<Trimmable> t)
234 boost::shared_ptr<Trimmable> st = _trimmable.lock();
236 if (!st || st == t) {
238 set_canvas_cursor ();
243 Editor::set_current_movable (boost::shared_ptr<Movable> m)
245 boost::shared_ptr<Movable> sm = _movable.lock();
247 if (!sm || sm != m) {
249 set_canvas_cursor ();
254 Editor::set_canvas_cursor ()
256 if (_internal_editing) {
258 switch (mouse_mode) {
260 current_canvas_cursor = _cursors->midi_pencil;
264 current_canvas_cursor = which_grabber_cursor();
268 current_canvas_cursor = _cursors->midi_resize;
277 switch (mouse_mode) {
279 current_canvas_cursor = _cursors->selector;
283 current_canvas_cursor = which_grabber_cursor();
287 /* shouldn't be possible, but just cover it anyway ... */
288 current_canvas_cursor = _cursors->midi_pencil;
292 current_canvas_cursor = _cursors->cross_hair;
296 if (Keyboard::the_keyboard().key_is_down (GDK_Control_L)) {
297 current_canvas_cursor = _cursors->zoom_out;
299 current_canvas_cursor = _cursors->zoom_in;
304 current_canvas_cursor = _cursors->time_fx; // just use playhead
308 current_canvas_cursor = _cursors->speaker;
313 switch (_join_object_range_state) {
314 case JOIN_OBJECT_RANGE_NONE:
316 case JOIN_OBJECT_RANGE_OBJECT:
317 current_canvas_cursor = which_grabber_cursor ();
319 case JOIN_OBJECT_RANGE_RANGE:
320 current_canvas_cursor = _cursors->selector;
324 /* up-down cursor as a cue that automation can be dragged up and down when in join object/range mode */
325 if (smart_mode_action->get_active()) {
327 get_pointer_position (x, y);
328 ArdourCanvas::Item* i = track_canvas->get_item_at (x, y);
329 if (i && i->property_parent() && (*i->property_parent()).get_data (X_("timeselection"))) {
330 pair<TimeAxisView*, int> tvp = trackview_by_y_position (_last_motion_y + vertical_adjustment.get_value() - canvas_timebars_vsize);
331 if (dynamic_cast<AutomationTimeAxisView*> (tvp.first)) {
332 current_canvas_cursor = _cursors->up_down;
337 set_canvas_cursor (current_canvas_cursor, true);
341 Editor::set_mouse_mode (MouseMode m, bool force)
343 if (_drags->active ()) {
347 if (!force && m == mouse_mode) {
351 Glib::RefPtr<Action> act;
355 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-range"));
359 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-object"));
363 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-draw"));
367 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-gain"));
371 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-zoom"));
375 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-timefx"));
379 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-audition"));
385 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
388 /* go there and back to ensure that the toggled handler is called to set up mouse_mode */
389 tact->set_active (false);
390 tact->set_active (true);
392 MouseModeChanged (); /* EMIT SIGNAL */
396 Editor::mouse_mode_toggled (MouseMode m)
398 Glib::RefPtr<Action> act;
399 Glib::RefPtr<ToggleAction> tact;
403 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-range"));
407 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-object"));
411 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-draw"));
415 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-gain"));
419 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-zoom"));
423 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-timefx"));
427 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-audition"));
433 tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
436 if (!tact->get_active()) {
437 /* this was just the notification that the old mode has been
438 * left. we'll get called again with the new mode active in a
446 act = ActionManager::get_action (X_("MouseMode"), X_("toggle-internal-edit"));
447 tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
448 tact->set_active (true);
458 if (!internal_editing()) {
459 if (mouse_mode != MouseRange && mouse_mode != MouseGain && _join_object_range_state == JOIN_OBJECT_RANGE_NONE) {
461 /* in all modes except range, gain and joined object/range, hide the range selection,
462 show the object (region) selection.
465 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
466 (*i)->hide_selection ();
472 in range or object/range mode, show the range selection.
475 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
476 (*i)->show_selection (selection->time);
481 set_canvas_cursor ();
483 MouseModeChanged (); /* EMIT SIGNAL */
487 Editor::step_mouse_mode (bool next)
489 switch (current_mouse_mode()) {
492 if (Profile->get_sae()) {
493 set_mouse_mode (MouseZoom);
495 set_mouse_mode (MouseRange);
498 set_mouse_mode (MouseTimeFX);
503 if (next) set_mouse_mode (MouseDraw);
504 else set_mouse_mode (MouseObject);
508 if (next) set_mouse_mode (MouseZoom);
509 else set_mouse_mode (MouseRange);
514 if (Profile->get_sae()) {
515 set_mouse_mode (MouseTimeFX);
517 set_mouse_mode (MouseGain);
520 if (Profile->get_sae()) {
521 set_mouse_mode (MouseObject);
523 set_mouse_mode (MouseDraw);
529 if (next) set_mouse_mode (MouseTimeFX);
530 else set_mouse_mode (MouseZoom);
535 set_mouse_mode (MouseAudition);
537 if (Profile->get_sae()) {
538 set_mouse_mode (MouseZoom);
540 set_mouse_mode (MouseGain);
546 if (next) set_mouse_mode (MouseObject);
547 else set_mouse_mode (MouseTimeFX);
553 Editor::toggle_internal_editing_from_double_click (GdkEvent* event)
555 if (_drags->active()) {
556 _drags->end_grab (event);
559 ActionManager::do_action ("MouseMode", "toggle-internal-edit");
561 /* prevent reversion of edit cursor on button release */
563 pre_press_cursor = 0;
569 Editor::button_selection (ArdourCanvas::Item* /*item*/, GdkEvent* event, ItemType item_type)
571 /* in object/audition/timefx/gain-automation mode,
572 any button press sets the selection if the object
573 can be selected. this is a bit of hack, because
574 we want to avoid this if the mouse operation is a
577 note: not dbl-click or triple-click
579 Also note that there is no region selection in internal edit mode, otherwise
580 for operations operating on the selection (e.g. cut) it is not obvious whether
581 to cut notes or regions.
584 if (((mouse_mode != MouseObject) &&
585 (_join_object_range_state != JOIN_OBJECT_RANGE_OBJECT) &&
586 (mouse_mode != MouseAudition || item_type != RegionItem) &&
587 (mouse_mode != MouseTimeFX || item_type != RegionItem) &&
588 (mouse_mode != MouseGain) &&
589 (mouse_mode != MouseRange) &&
590 (mouse_mode != MouseDraw)) ||
591 ((event->type != GDK_BUTTON_PRESS && event->type != GDK_BUTTON_RELEASE) || event->button.button > 3) ||
592 internal_editing()) {
597 if (event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) {
599 if ((event->button.state & Keyboard::RelevantModifierKeyMask) && event->button.button != 1) {
601 /* almost no selection action on modified button-2 or button-3 events */
603 if (item_type != RegionItem && event->button.button != 2) {
609 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
610 bool press = (event->type == GDK_BUTTON_PRESS);
614 if (!doing_range_stuff()) {
615 set_selected_regionview_from_click (press, op);
619 if (doing_range_stuff()) {
620 /* don't change the selection unless the
621 clicked track is not currently selected. if
622 so, "collapse" the selection to just this
625 if (!selection->selected (clicked_axisview)) {
626 set_selected_track_as_side_effect (Selection::Set);
632 case RegionViewNameHighlight:
634 case LeftFrameHandle:
635 case RightFrameHandle:
636 if (doing_object_stuff() || (mouse_mode != MouseRange && mouse_mode != MouseObject)) {
637 set_selected_regionview_from_click (press, op);
638 } else if (event->type == GDK_BUTTON_PRESS) {
639 set_selected_track_as_side_effect (op);
643 case FadeInHandleItem:
645 case FadeOutHandleItem:
647 case StartCrossFadeItem:
648 case EndCrossFadeItem:
649 if (doing_object_stuff() || (mouse_mode != MouseRange && mouse_mode != MouseObject)) {
650 set_selected_regionview_from_click (press, op);
651 } else if (event->type == GDK_BUTTON_PRESS) {
652 set_selected_track_as_side_effect (op);
656 case ControlPointItem:
657 set_selected_track_as_side_effect (op);
658 if (doing_object_stuff() || (mouse_mode != MouseRange && mouse_mode != MouseObject)) {
659 set_selected_control_point_from_click (press, op);
664 /* for context click, select track */
665 if (event->button.button == 3) {
666 selection->clear_tracks ();
667 set_selected_track_as_side_effect (op);
671 case AutomationTrackItem:
672 set_selected_track_as_side_effect (op);
681 Editor::button_press_handler_1 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
683 /* single mouse clicks on any of these item types operate
684 independent of mouse mode, mostly because they are
685 not on the main track canvas or because we want
690 case PlayheadCursorItem:
691 _drags->set (new CursorDrag (this, item, true), event);
695 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
696 hide_marker (item, event);
698 _drags->set (new MarkerDrag (this, item), event);
702 case TempoMarkerItem:
704 TempoMarker* m = reinterpret_cast<TempoMarker*> (item->get_data ("marker"));
706 if (m->tempo().movable ()) {
708 new TempoMarkerDrag (
711 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
721 case MeterMarkerItem:
723 MeterMarker* m = reinterpret_cast<MeterMarker*> (item->get_data ("marker"));
725 if (m->meter().movable ()) {
727 new MeterMarkerDrag (
730 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
743 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
744 _drags->set (new CursorDrag (this, &playhead_cursor->canvas_item, false), event);
750 case RangeMarkerBarItem:
751 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
752 _drags->set (new CursorDrag (this, &playhead_cursor->canvas_item, false), event);
754 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateRangeMarker), event);
759 case CdMarkerBarItem:
760 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
761 _drags->set (new CursorDrag (this, &playhead_cursor->canvas_item, false), event);
763 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateCDMarker), event);
768 case TransportMarkerBarItem:
769 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
770 _drags->set (new CursorDrag (this, &playhead_cursor->canvas_item, false), event);
772 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateTransportMarker), event);
781 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
782 /* special case: allow trim of range selections in joined object mode;
783 in theory eff should equal MouseRange in this case, but it doesn't
784 because entering the range selection canvas item results in entered_regionview
785 being set to 0, so update_join_object_range_location acts as if we aren't
788 if (item_type == StartSelectionTrimItem) {
789 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
790 } else if (item_type == EndSelectionTrimItem) {
791 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
795 Editing::MouseMode eff = effective_mouse_mode ();
797 /* special case: allow drag of region fade in/out in object mode with join object/range enabled */
798 if (item_type == FadeInHandleItem || item_type == FadeOutHandleItem) {
805 case StartSelectionTrimItem:
806 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
809 case EndSelectionTrimItem:
810 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
814 if (Keyboard::modifier_state_contains
815 (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier))) {
816 // contains and not equals because I can't use alt as a modifier alone.
817 start_selection_grab (item, event);
818 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
819 /* grab selection for moving */
820 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionMove), event);
822 double const y = event->button.y + vertical_adjustment.get_value() - canvas_timebars_vsize;
823 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
825 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
826 if (smart_mode_action->get_active() && atv) {
827 /* smart "join" mode: drag automation */
828 _drags->set (new AutomationRangeDrag (this, atv, selection->time), event, _cursors->up_down);
830 /* this was debated, but decided the more common action was to
831 make a new selection */
832 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
839 if (internal_editing()) {
840 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
841 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
845 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
850 case RegionViewNameHighlight:
851 if (!clicked_regionview->region()->locked()) {
852 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
853 _drags->set (new TrimDrag (this, item, clicked_regionview, s.by_layer()), event);
858 case LeftFrameHandle:
859 case RightFrameHandle:
860 if (!internal_editing() && doing_object_stuff() && !clicked_regionview->region()->locked()) {
861 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
862 _drags->set (new TrimDrag (this, item, clicked_regionview, s.by_layer()), event);
868 if (!internal_editing()) {
869 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
878 if (internal_editing()) {
879 /* trim notes if we're in internal edit mode and near the ends of the note */
880 ArdourCanvas::CanvasNote* cn = dynamic_cast<ArdourCanvas::CanvasNote*> (item);
881 if (cn && cn->big_enough_to_trim() && cn->mouse_near_ends()) {
882 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
884 _drags->set (new NoteDrag (this, item), event);
898 if (internal_editing()) {
899 ArdourCanvas::CanvasNoteEvent* cn = dynamic_cast<ArdourCanvas::CanvasNoteEvent*> (item);
900 if (cn->mouse_near_ends()) {
901 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
903 _drags->set (new NoteDrag (this, item), event);
913 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)) &&
914 event->type == GDK_BUTTON_PRESS) {
916 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
918 } else if (event->type == GDK_BUTTON_PRESS) {
921 case FadeInHandleItem:
923 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
924 _drags->set (new FadeInDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), s), event, _cursors->fade_in);
928 case FadeOutHandleItem:
930 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
931 _drags->set (new FadeOutDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), s), event, _cursors->fade_out);
935 case StartCrossFadeItem:
936 _drags->set (new CrossfadeEdgeDrag (this, reinterpret_cast<AudioRegionView*>(item->get_data("regionview")), item, true), event, 0);
939 case EndCrossFadeItem:
940 _drags->set (new CrossfadeEdgeDrag (this, reinterpret_cast<AudioRegionView*>(item->get_data("regionview")), item, false), event, 0);
943 case FeatureLineItem:
945 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier)) {
946 remove_transient(item);
950 _drags->set (new FeatureLineDrag (this, item), event);
956 if (dynamic_cast<AutomationRegionView*> (clicked_regionview)) {
957 /* click on an automation region view; do nothing here and let the ARV's signal handler
963 if (internal_editing ()) {
964 if (event->type == GDK_2BUTTON_PRESS && event->button.button == 1) {
965 Glib::RefPtr<Action> act = ActionManager::get_action (X_("MouseMode"), X_("toggle-internal-edit"));
971 /* click on a normal region view */
972 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
973 add_region_copy_drag (item, event, clicked_regionview);
974 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
975 add_region_brush_drag (item, event, clicked_regionview);
977 add_region_drag (item, event, clicked_regionview);
981 if (!internal_editing() && (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE && !selection->regions.empty())) {
982 _drags->add (new SelectionDrag (this, clicked_axisview->get_selection_rect (clicked_selection)->rect, SelectionDrag::SelectionMove));
985 _drags->start_grab (event);
988 case RegionViewNameHighlight:
989 case LeftFrameHandle:
990 case RightFrameHandle:
991 if (!clicked_regionview->region()->locked()) {
992 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
993 _drags->set (new TrimDrag (this, item, clicked_regionview, s.by_layer()), event);
1000 /* rename happens on edit clicks */
1001 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
1002 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, s.by_layer()), event);
1007 case ControlPointItem:
1008 _drags->set (new ControlPointDrag (this, item), event);
1012 case AutomationLineItem:
1013 _drags->set (new LineDrag (this, item), event);
1018 if (internal_editing()) {
1019 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
1020 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
1024 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
1028 case AutomationTrackItem:
1030 TimeAxisView* parent = clicked_axisview->get_parent ();
1031 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (clicked_axisview);
1033 if (parent && dynamic_cast<MidiTimeAxisView*> (parent) && atv->show_regions ()) {
1034 /* create a MIDI region so that we have somewhere to put automation */
1035 _drags->set (new RegionCreateDrag (this, item, parent), event);
1037 /* rubberband drag to select automation points */
1038 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
1045 if (smart_mode_action->get_active()) {
1046 /* we're in "smart" joined mode, and we've clicked on a Selection */
1047 double const y = event->button.y + vertical_adjustment.get_value() - canvas_timebars_vsize;
1048 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
1050 /* if we're over an automation track, start a drag of its data */
1051 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
1053 _drags->set (new AutomationRangeDrag (this, atv, selection->time), event, _cursors->up_down);
1056 /* if we're over a track and a region, and in the `object' part of a region,
1057 put a selection around the region and drag both
1059 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1060 if (rtv && _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
1061 boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track> (rtv->route ());
1063 boost::shared_ptr<Playlist> pl = t->playlist ();
1066 boost::shared_ptr<Region> r = pl->top_region_at (event_frame (event));
1068 RegionView* rv = rtv->view()->find_view (r);
1069 clicked_selection = select_range (rv->region()->position(),
1070 rv->region()->last_frame()+1);
1071 _drags->add (new SelectionDrag (this, item, SelectionDrag::SelectionMove));
1072 list<RegionView*> rvs;
1074 _drags->add (new RegionMoveDrag (this, item, rv, rvs, false, false));
1075 _drags->start_grab (event);
1086 case ImageFrameHandleStartItem:
1087 imageframe_start_handle_op(item, event) ;
1090 case ImageFrameHandleEndItem:
1091 imageframe_end_handle_op(item, event) ;
1094 case MarkerViewHandleStartItem:
1095 markerview_item_start_handle_op(item, event) ;
1098 case MarkerViewHandleEndItem:
1099 markerview_item_end_handle_op(item, event) ;
1102 case MarkerViewItem:
1103 start_markerview_grab(item, event) ;
1105 case ImageFrameItem:
1106 start_imageframe_grab(item, event) ;
1122 switch (item_type) {
1124 _drags->set (new LineDrag (this, item), event);
1127 case ControlPointItem:
1128 _drags->set (new ControlPointDrag (this, item), event);
1134 AudioRegionView* arv = dynamic_cast<AudioRegionView *> (clicked_regionview);
1136 _drags->set (new AutomationRangeDrag (this, arv, selection->time), event, _cursors->up_down);
1137 _drags->start_grab (event);
1143 case AutomationLineItem:
1144 _drags->set (new LineDrag (this, item), event);
1154 if (event->type == GDK_BUTTON_PRESS) {
1155 _drags->set (new MouseZoomDrag (this, item), event);
1162 if (internal_editing() && item_type == NoteItem) {
1163 /* drag notes if we're in internal edit mode */
1164 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
1166 } else if ((!internal_editing() || dynamic_cast<AudioRegionView*> (clicked_regionview)) && clicked_regionview) {
1167 /* do time-FX if we're not in internal edit mode, or we are but we clicked on an audio region */
1168 _drags->set (new TimeFXDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1174 _drags->set (new ScrubDrag (this, item), event);
1175 scrub_reversals = 0;
1176 scrub_reverse_distance = 0;
1177 last_scrub_x = event->button.x;
1178 scrubbing_direction = 0;
1179 set_canvas_cursor (_cursors->transparent);
1191 Editor::button_press_handler_2 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1193 Editing::MouseMode const eff = effective_mouse_mode ();
1196 switch (item_type) {
1198 if (internal_editing ()) {
1199 /* no region drags in internal edit mode */
1203 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
1204 add_region_copy_drag (item, event, clicked_regionview);
1206 add_region_drag (item, event, clicked_regionview);
1208 _drags->start_grab (event);
1211 case ControlPointItem:
1212 _drags->set (new ControlPointDrag (this, item), event);
1220 switch (item_type) {
1221 case RegionViewNameHighlight:
1222 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1226 case LeftFrameHandle:
1227 case RightFrameHandle:
1228 if (!internal_editing ()) {
1229 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1234 case RegionViewName:
1235 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer()), event);
1249 /* relax till release */
1255 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1256 temporal_zoom_to_frame (false, event_frame (event));
1258 temporal_zoom_to_frame (true, event_frame(event));
1271 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1273 if (event->type != GDK_BUTTON_PRESS) {
1277 Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->track_canvas->get_window();
1279 if (canvas_window) {
1280 Glib::RefPtr<const Gdk::Window> pointer_window;
1283 Gdk::ModifierType mask;
1285 pointer_window = canvas_window->get_pointer (x, y, mask);
1287 if (pointer_window == track_canvas->get_bin_window()) {
1288 track_canvas->window_to_world (x, y, wx, wy);
1292 pre_press_cursor = current_canvas_cursor;
1294 track_canvas->grab_focus();
1296 if (_session && _session->actively_recording()) {
1302 if (internal_editing()) {
1303 bool leave_internal_edit_mode = false;
1305 switch (item_type) {
1310 if (!dynamic_cast<MidiRegionView*> (clicked_regionview) && !dynamic_cast<AutomationRegionView*> (clicked_regionview)) {
1311 leave_internal_edit_mode = true;
1315 case PlayheadCursorItem:
1317 case TempoMarkerItem:
1318 case MeterMarkerItem:
1322 case RangeMarkerBarItem:
1323 case CdMarkerBarItem:
1324 case TransportMarkerBarItem:
1325 /* button press on these events never does anything to
1326 change the editing mode.
1331 leave_internal_edit_mode = true;
1338 if (leave_internal_edit_mode) {
1339 ActionManager::do_action ("MouseMode", "toggle-internal-edit");
1343 button_selection (item, event, item_type);
1345 if (!_drags->active () &&
1346 (Keyboard::is_delete_event (&event->button) ||
1347 Keyboard::is_context_menu_event (&event->button) ||
1348 Keyboard::is_edit_event (&event->button))) {
1350 /* handled by button release */
1354 switch (event->button.button) {
1356 return button_press_handler_1 (item, event, item_type);
1360 return button_press_handler_2 (item, event, item_type);
1367 return button_press_dispatch (&event->button);
1376 Editor::button_press_dispatch (GdkEventButton* ev)
1378 /* this function is intended only for buttons 4 and above.
1381 Gtkmm2ext::MouseButton b (ev->state, ev->button);
1382 return button_bindings->activate (b, Gtkmm2ext::Bindings::Press);
1386 Editor::button_release_dispatch (GdkEventButton* ev)
1388 /* this function is intended only for buttons 4 and above.
1391 Gtkmm2ext::MouseButton b (ev->state, ev->button);
1392 return button_bindings->activate (b, Gtkmm2ext::Bindings::Release);
1396 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1398 framepos_t where = event_frame (event, 0, 0);
1399 AutomationTimeAxisView* atv = 0;
1401 if (pre_press_cursor) {
1402 set_canvas_cursor (pre_press_cursor);
1403 pre_press_cursor = 0;
1406 /* no action if we're recording */
1408 if (_session && _session->actively_recording()) {
1412 /* see if we're finishing a drag */
1414 bool were_dragging = false;
1415 if (_drags->active ()) {
1416 bool const r = _drags->end_grab (event);
1418 /* grab dragged, so do nothing else */
1422 were_dragging = true;
1425 update_region_layering_order_editor ();
1427 /* edit events get handled here */
1429 if (!_drags->active () && Keyboard::is_edit_event (&event->button)) {
1430 switch (item_type) {
1432 show_region_properties ();
1435 case TempoMarkerItem:
1436 edit_tempo_marker (item);
1439 case MeterMarkerItem:
1440 edit_meter_marker (item);
1443 case RegionViewName:
1444 if (clicked_regionview->name_active()) {
1445 return mouse_rename_region (item, event);
1449 case ControlPointItem:
1450 edit_control_point (item);
1463 /* context menu events get handled here */
1465 if (Keyboard::is_context_menu_event (&event->button)) {
1467 context_click_event = *event;
1469 if (!_drags->active ()) {
1471 /* no matter which button pops up the context menu, tell the menu
1472 widget to use button 1 to drive menu selection.
1475 switch (item_type) {
1477 case FadeInHandleItem:
1479 case FadeOutHandleItem:
1480 popup_fade_context_menu (1, event->button.time, item, item_type);
1483 case StartCrossFadeItem:
1484 popup_xfade_in_context_menu (1, event->button.time, item, item_type);
1487 case EndCrossFadeItem:
1488 popup_xfade_out_context_menu (1, event->button.time, item, item_type);
1492 popup_track_context_menu (1, event->button.time, item_type, false);
1496 case RegionViewNameHighlight:
1497 case LeftFrameHandle:
1498 case RightFrameHandle:
1499 case RegionViewName:
1500 popup_track_context_menu (1, event->button.time, item_type, false);
1504 popup_track_context_menu (1, event->button.time, item_type, true);
1507 case AutomationTrackItem:
1508 popup_track_context_menu (1, event->button.time, item_type, false);
1512 case RangeMarkerBarItem:
1513 case TransportMarkerBarItem:
1514 case CdMarkerBarItem:
1517 popup_ruler_menu (where, item_type);
1521 marker_context_menu (&event->button, item);
1524 case TempoMarkerItem:
1525 tempo_or_meter_marker_context_menu (&event->button, item);
1528 case MeterMarkerItem:
1529 tempo_or_meter_marker_context_menu (&event->button, item);
1532 case CrossfadeViewItem:
1533 popup_track_context_menu (1, event->button.time, item_type, false);
1536 case ControlPointItem:
1537 popup_control_point_context_menu (item, event);
1541 case ImageFrameItem:
1542 popup_imageframe_edit_menu(1, event->button.time, item, true) ;
1544 case ImageFrameTimeAxisItem:
1545 popup_imageframe_edit_menu(1, event->button.time, item, false) ;
1547 case MarkerViewItem:
1548 popup_marker_time_axis_edit_menu(1, event->button.time, item, true) ;
1550 case MarkerTimeAxisItem:
1551 popup_marker_time_axis_edit_menu(1, event->button.time, item, false) ;
1563 /* delete events get handled here */
1565 Editing::MouseMode const eff = effective_mouse_mode ();
1567 if (!_drags->active () && Keyboard::is_delete_event (&event->button)) {
1569 switch (item_type) {
1570 case TempoMarkerItem:
1571 remove_tempo_marker (item);
1574 case MeterMarkerItem:
1575 remove_meter_marker (item);
1579 remove_marker (*item, event);
1583 if (eff == MouseObject) {
1584 remove_clicked_region ();
1588 case ControlPointItem:
1589 remove_control_point (item);
1593 remove_midi_note (item, event);
1602 switch (event->button.button) {
1605 switch (item_type) {
1606 /* see comments in button_press_handler */
1607 case PlayheadCursorItem:
1610 case AutomationLineItem:
1611 case StartSelectionTrimItem:
1612 case EndSelectionTrimItem:
1616 if (!_dragging_playhead) {
1617 snap_to_with_modifier (where, event, 0, true);
1618 mouse_add_new_marker (where);
1622 case CdMarkerBarItem:
1623 if (!_dragging_playhead) {
1624 // if we get here then a dragged range wasn't done
1625 snap_to_with_modifier (where, event, 0, true);
1626 mouse_add_new_marker (where, true);
1631 if (!_dragging_playhead) {
1632 snap_to_with_modifier (where, event);
1633 mouse_add_new_tempo_event (where);
1638 if (!_dragging_playhead) {
1639 mouse_add_new_meter_event (pixel_to_frame (event->button.x));
1650 switch (item_type) {
1651 case AutomationTrackItem:
1652 atv = dynamic_cast<AutomationTimeAxisView*>(clicked_axisview);
1654 atv->add_automation_event (event, where, event->button.y);
1664 switch (item_type) {
1667 /* check that we didn't drag before releasing, since
1668 its really annoying to create new control
1669 points when doing this.
1671 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (clicked_regionview);
1672 if (!were_dragging && arv) {
1673 arv->add_gain_point_event (item, event);
1679 case AutomationTrackItem:
1680 dynamic_cast<AutomationTimeAxisView*>(clicked_axisview)->
1681 add_automation_event (event, where, event->button.y);
1690 set_canvas_cursor (current_canvas_cursor);
1691 if (scrubbing_direction == 0) {
1692 /* no drag, just a click */
1693 switch (item_type) {
1695 play_selected_region ();
1701 /* make sure we stop */
1702 _session->request_transport_speed (0.0);
1711 /* do any (de)selection operations that should occur on button release */
1712 button_selection (item, event, item_type);
1721 switch (item_type) {
1723 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1725 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier|Keyboard::SecondaryModifier))) {
1728 // Button2 click is unused
1743 // x_style_paste (where, 1.0);
1764 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1771 switch (item_type) {
1772 case ControlPointItem:
1773 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1774 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1775 cp->set_visible (true);
1779 at_y = cp->get_y ();
1780 cp->i2w (at_x, at_y);
1784 fraction = 1.0 - (cp->get_y() / cp->line().height());
1786 if (is_drawable() && !_drags->active ()) {
1787 set_canvas_cursor (_cursors->fader);
1790 _verbose_cursor->set (cp->line().get_verbose_cursor_string (fraction), at_x, at_y);
1791 _verbose_cursor->show ();
1796 if (mouse_mode == MouseGain) {
1797 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1799 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredGainLine.get();
1800 if (is_drawable()) {
1801 set_canvas_cursor (_cursors->fader);
1806 case AutomationLineItem:
1807 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1808 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1810 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredAutomationLine.get();
1812 if (is_drawable()) {
1813 set_canvas_cursor (_cursors->fader);
1818 case RegionViewNameHighlight:
1819 if (is_drawable() && doing_object_stuff() && entered_regionview) {
1820 set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1821 _over_region_trim_target = true;
1825 case LeftFrameHandle:
1826 case RightFrameHandle:
1827 if (is_drawable() && doing_object_stuff() && !internal_editing() && entered_regionview) {
1828 set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1832 case StartSelectionTrimItem:
1834 case ImageFrameHandleStartItem:
1835 case MarkerViewHandleStartItem:
1837 if (is_drawable()) {
1838 set_canvas_cursor (_cursors->left_side_trim);
1841 case EndSelectionTrimItem:
1843 case ImageFrameHandleEndItem:
1844 case MarkerViewHandleEndItem:
1846 if (is_drawable()) {
1847 set_canvas_cursor (_cursors->right_side_trim);
1851 case PlayheadCursorItem:
1852 if (is_drawable()) {
1853 switch (_edit_point) {
1855 set_canvas_cursor (_cursors->grabber_edit_point);
1858 set_canvas_cursor (_cursors->grabber);
1864 case RegionViewName:
1866 /* when the name is not an active item, the entire name highlight is for trimming */
1868 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1869 if (mouse_mode == MouseObject && is_drawable()) {
1870 set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1871 _over_region_trim_target = true;
1877 case AutomationTrackItem:
1878 if (is_drawable()) {
1879 Gdk::Cursor *cursor;
1880 switch (mouse_mode) {
1882 cursor = _cursors->selector;
1885 cursor = _cursors->zoom_in;
1888 cursor = _cursors->cross_hair;
1892 set_canvas_cursor (cursor);
1894 AutomationTimeAxisView* atv;
1895 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1896 clear_entered_track = false;
1897 set_entered_track (atv);
1903 case RangeMarkerBarItem:
1904 case TransportMarkerBarItem:
1905 case CdMarkerBarItem:
1908 if (is_drawable()) {
1909 set_canvas_cursor (_cursors->timebar);
1914 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1917 entered_marker = marker;
1918 marker->set_color_rgba (ARDOUR_UI::config()->canvasvar_EnteredMarker.get());
1920 case MeterMarkerItem:
1921 case TempoMarkerItem:
1922 if (is_drawable()) {
1923 set_canvas_cursor (_cursors->timebar);
1927 case FadeInHandleItem:
1928 if (mouse_mode == MouseObject && !internal_editing()) {
1929 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1931 rect->property_fill_color_rgba() = 0xBBBBBBAA;
1933 set_canvas_cursor (_cursors->fade_in);
1937 case FadeOutHandleItem:
1938 if (mouse_mode == MouseObject && !internal_editing()) {
1939 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1941 rect->property_fill_color_rgba() = 0xBBBBBBAA;
1943 set_canvas_cursor (_cursors->fade_out);
1946 case FeatureLineItem:
1948 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1949 line->property_fill_color_rgba() = 0xFF0000FF;
1953 if (smart_mode_action->get_active()) {
1954 set_canvas_cursor ();
1962 /* second pass to handle entered track status in a comprehensible way.
1965 switch (item_type) {
1967 case AutomationLineItem:
1968 case ControlPointItem:
1969 /* these do not affect the current entered track state */
1970 clear_entered_track = false;
1973 case AutomationTrackItem:
1974 /* handled above already */
1978 set_entered_track (0);
1986 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent*, ItemType item_type)
1996 switch (item_type) {
1997 case ControlPointItem:
1998 cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
1999 if (cp->line().the_list()->interpolation() != AutomationList::Discrete) {
2000 if (cp->line().npoints() > 1 && !cp->get_selected()) {
2001 cp->set_visible (false);
2005 if (is_drawable()) {
2006 set_canvas_cursor (current_canvas_cursor);
2009 _verbose_cursor->hide ();
2012 case RegionViewNameHighlight:
2013 case LeftFrameHandle:
2014 case RightFrameHandle:
2015 case StartSelectionTrimItem:
2016 case EndSelectionTrimItem:
2017 case PlayheadCursorItem:
2020 case ImageFrameHandleStartItem:
2021 case ImageFrameHandleEndItem:
2022 case MarkerViewHandleStartItem:
2023 case MarkerViewHandleEndItem:
2026 _over_region_trim_target = false;
2028 if (is_drawable()) {
2029 set_canvas_cursor (current_canvas_cursor);
2034 case AutomationLineItem:
2035 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
2037 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
2039 line->property_fill_color_rgba() = al->get_line_color();
2041 if (is_drawable()) {
2042 set_canvas_cursor (current_canvas_cursor);
2046 case RegionViewName:
2047 /* see enter_handler() for notes */
2048 _over_region_trim_target = false;
2050 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
2051 if (is_drawable() && mouse_mode == MouseObject) {
2052 set_canvas_cursor (current_canvas_cursor);
2057 case RangeMarkerBarItem:
2058 case TransportMarkerBarItem:
2059 case CdMarkerBarItem:
2063 if (is_drawable()) {
2064 set_canvas_cursor (current_canvas_cursor);
2069 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2073 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
2074 location_flags_changed (loc, this);
2077 case MeterMarkerItem:
2078 case TempoMarkerItem:
2080 if (is_drawable()) {
2081 set_canvas_cursor (current_canvas_cursor);
2086 case FadeInHandleItem:
2087 case FadeOutHandleItem:
2088 rv = static_cast<RegionView*>(item->get_data ("regionview"));
2090 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
2092 rect->property_fill_color_rgba() = rv->get_fill_color();
2093 rect->property_outline_pixels() = 0;
2096 set_canvas_cursor (current_canvas_cursor);
2099 case AutomationTrackItem:
2100 if (is_drawable()) {
2101 set_canvas_cursor (current_canvas_cursor);
2102 clear_entered_track = true;
2103 Glib::signal_idle().connect (sigc::mem_fun(*this, &Editor::left_automation_track));
2106 case FeatureLineItem:
2108 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
2109 line->property_fill_color_rgba() = (guint) ARDOUR_UI::config()->canvasvar_ZeroLine.get();;
2121 Editor::left_automation_track ()
2123 if (clear_entered_track) {
2124 set_entered_track (0);
2125 clear_entered_track = false;
2131 Editor::scrub (framepos_t frame, double current_x)
2135 if (scrubbing_direction == 0) {
2137 _session->request_locate (frame, false);
2138 _session->request_transport_speed (0.1);
2139 scrubbing_direction = 1;
2143 if (last_scrub_x > current_x) {
2145 /* pointer moved to the left */
2147 if (scrubbing_direction > 0) {
2149 /* we reversed direction to go backwards */
2152 scrub_reverse_distance += (int) (last_scrub_x - current_x);
2156 /* still moving to the left (backwards) */
2158 scrub_reversals = 0;
2159 scrub_reverse_distance = 0;
2161 delta = 0.01 * (last_scrub_x - current_x);
2162 _session->request_transport_speed_nonzero (_session->transport_speed() - delta);
2166 /* pointer moved to the right */
2168 if (scrubbing_direction < 0) {
2169 /* we reversed direction to go forward */
2172 scrub_reverse_distance += (int) (current_x - last_scrub_x);
2175 /* still moving to the right */
2177 scrub_reversals = 0;
2178 scrub_reverse_distance = 0;
2180 delta = 0.01 * (current_x - last_scrub_x);
2181 _session->request_transport_speed_nonzero (_session->transport_speed() + delta);
2185 /* if there have been more than 2 opposite motion moves detected, or one that moves
2186 back more than 10 pixels, reverse direction
2189 if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
2191 if (scrubbing_direction > 0) {
2192 /* was forwards, go backwards */
2193 _session->request_transport_speed (-0.1);
2194 scrubbing_direction = -1;
2196 /* was backwards, go forwards */
2197 _session->request_transport_speed (0.1);
2198 scrubbing_direction = 1;
2201 scrub_reverse_distance = 0;
2202 scrub_reversals = 0;
2206 last_scrub_x = current_x;
2210 Editor::motion_handler (ArdourCanvas::Item* /*item*/, GdkEvent* event, bool from_autoscroll)
2212 _last_motion_y = event->motion.y;
2214 if (event->motion.is_hint) {
2217 /* We call this so that MOTION_NOTIFY events continue to be
2218 delivered to the canvas. We need to do this because we set
2219 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
2220 the density of the events, at the expense of a round-trip
2221 to the server. Given that this will mostly occur on cases
2222 where DISPLAY = :0.0, and given the cost of what the motion
2223 event might do, its a good tradeoff.
2226 track_canvas->get_pointer (x, y);
2229 if (current_stepping_trackview) {
2230 /* don't keep the persistent stepped trackview if the mouse moves */
2231 current_stepping_trackview = 0;
2232 step_timeout.disconnect ();
2235 if (_session && _session->actively_recording()) {
2236 /* Sorry. no dragging stuff around while we record */
2240 JoinObjectRangeState const old = _join_object_range_state;
2241 update_join_object_range_location (event->motion.x, event->motion.y);
2242 if (_join_object_range_state != old) {
2243 set_canvas_cursor ();
2246 if (_over_region_trim_target) {
2247 set_canvas_cursor_for_region_view (event->motion.x, entered_regionview);
2250 bool handled = false;
2251 if (_drags->active ()) {
2252 handled = _drags->motion_handler (event, from_autoscroll);
2259 track_canvas_motion (event);
2264 Editor::can_remove_control_point (ArdourCanvas::Item* item)
2266 ControlPoint* control_point;
2268 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2269 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2273 AutomationLine& line = control_point->line ();
2274 if (dynamic_cast<AudioRegionGainLine*> (&line)) {
2275 /* we shouldn't remove the first or last gain point in region gain lines */
2276 if (line.is_last_point(*control_point) || line.is_first_point(*control_point)) {
2285 Editor::remove_control_point (ArdourCanvas::Item* item)
2287 if (!can_remove_control_point (item)) {
2291 ControlPoint* control_point;
2293 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2294 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2298 control_point->line().remove_point (*control_point);
2302 Editor::edit_control_point (ArdourCanvas::Item* item)
2304 ControlPoint* p = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"));
2307 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2311 ControlPointDialog d (p);
2312 d.set_position (Gtk::WIN_POS_MOUSE);
2315 if (d.run () != RESPONSE_ACCEPT) {
2319 p->line().modify_point_y (*p, d.get_y_fraction ());
2323 Editor::edit_note (ArdourCanvas::Item* item)
2325 ArdourCanvas::CanvasNoteEvent* e = dynamic_cast<ArdourCanvas::CanvasNoteEvent*> (item);
2328 EditNoteDialog d (&e->region_view(), e);
2329 d.set_position (Gtk::WIN_POS_MOUSE);
2337 Editor::visible_order_range (int* low, int* high) const
2339 *low = TimeAxisView::max_order ();
2342 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
2344 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
2346 if (!rtv->hidden()) {
2348 if (*high < rtv->order()) {
2349 *high = rtv->order ();
2352 if (*low > rtv->order()) {
2353 *low = rtv->order ();
2360 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
2362 /* Either add to or set the set the region selection, unless
2363 this is an alignment click (control used)
2366 if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
2367 TimeAxisView* tv = &rv.get_time_axis_view();
2368 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
2370 if (rtv && rtv->is_track()) {
2371 speed = rtv->track()->speed();
2374 framepos_t where = get_preferred_edit_position();
2378 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
2380 align_region (rv.region(), SyncPoint, (framepos_t) (where * speed));
2382 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
2384 align_region (rv.region(), End, (framepos_t) (where * speed));
2388 align_region (rv.region(), Start, (framepos_t) (where * speed));
2395 Editor::collect_new_region_view (RegionView* rv)
2397 latest_regionviews.push_back (rv);
2401 Editor::collect_and_select_new_region_view (RegionView* rv)
2404 latest_regionviews.push_back (rv);
2408 Editor::cancel_selection ()
2410 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2411 (*i)->hide_selection ();
2414 selection->clear ();
2415 clicked_selection = 0;
2420 Editor::point_trim (GdkEvent* event, framepos_t new_bound)
2422 RegionView* rv = clicked_regionview;
2424 /* Choose action dependant on which button was pressed */
2425 switch (event->button.button) {
2427 begin_reversible_command (_("start point trim"));
2429 if (selection->selected (rv)) {
2430 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
2431 i != selection->regions.by_layer().end(); ++i)
2434 cerr << "region view contains null region" << endl;
2437 if (!(*i)->region()->locked()) {
2438 (*i)->region()->clear_changes ();
2439 (*i)->region()->trim_front (new_bound);
2440 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2445 if (!rv->region()->locked()) {
2446 rv->region()->clear_changes ();
2447 rv->region()->trim_front (new_bound);
2448 _session->add_command(new StatefulDiffCommand (rv->region()));
2452 commit_reversible_command();
2456 begin_reversible_command (_("End point trim"));
2458 if (selection->selected (rv)) {
2460 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
2462 if (!(*i)->region()->locked()) {
2463 (*i)->region()->clear_changes();
2464 (*i)->region()->trim_end (new_bound);
2465 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2471 if (!rv->region()->locked()) {
2472 rv->region()->clear_changes ();
2473 rv->region()->trim_end (new_bound);
2474 _session->add_command (new StatefulDiffCommand (rv->region()));
2478 commit_reversible_command();
2487 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* /*event*/)
2492 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2493 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2497 Location* location = find_location_from_marker (marker, is_start);
2498 location->set_hidden (true, this);
2503 Editor::reposition_zoom_rect (framepos_t start, framepos_t end)
2505 double x1 = frame_to_pixel (start);
2506 double x2 = frame_to_pixel (end);
2507 double y2 = full_canvas_height - 1.0;
2509 zoom_rect->property_x1() = x1;
2510 zoom_rect->property_y1() = 1.0;
2511 zoom_rect->property_x2() = x2;
2512 zoom_rect->property_y2() = y2;
2517 Editor::mouse_rename_region (ArdourCanvas::Item* /*item*/, GdkEvent* /*event*/)
2519 using namespace Gtkmm2ext;
2521 ArdourPrompter prompter (false);
2523 prompter.set_prompt (_("Name for region:"));
2524 prompter.set_initial_text (clicked_regionview->region()->name());
2525 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
2526 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
2527 prompter.show_all ();
2528 switch (prompter.run ()) {
2529 case Gtk::RESPONSE_ACCEPT:
2531 prompter.get_result(str);
2533 clicked_regionview->region()->set_name (str);
2542 Editor::mouse_brush_insert_region (RegionView* rv, framepos_t pos)
2544 /* no brushing without a useful snap setting */
2546 switch (_snap_mode) {
2548 return; /* can't work because it allows region to be placed anywhere */
2553 switch (_snap_type) {
2561 /* don't brush a copy over the original */
2563 if (pos == rv->region()->position()) {
2567 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
2569 if (rtv == 0 || !rtv->is_track()) {
2573 boost::shared_ptr<Playlist> playlist = rtv->playlist();
2574 double speed = rtv->track()->speed();
2576 playlist->clear_changes ();
2577 boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region(), true));
2578 playlist->add_region (new_region, (framepos_t) (pos * speed));
2579 _session->add_command (new StatefulDiffCommand (playlist));
2581 // playlist is frozen, so we have to update manually XXX this is disgusting
2583 playlist->RegionAdded (new_region); /* EMIT SIGNAL */
2587 Editor::track_height_step_timeout ()
2589 if (get_microseconds() - last_track_height_step_timestamp < 250000) {
2590 current_stepping_trackview = 0;
2597 Editor::add_region_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2599 assert (region_view);
2601 if (!region_view->region()->playlist()) {
2605 _region_motion_group->raise_to_top ();
2607 if (Config->get_edit_mode() == Splice) {
2608 _drags->add (new RegionSpliceDrag (this, item, region_view, selection->regions.by_layer()));
2610 RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id);
2611 _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), false, false));
2614 /* sync the canvas to what we think is its current state */
2615 update_canvas_now();
2619 Editor::add_region_copy_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2621 assert (region_view);
2623 if (!region_view->region()->playlist()) {
2627 _region_motion_group->raise_to_top ();
2629 RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id);
2630 _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), false, true));
2634 Editor::add_region_brush_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2636 assert (region_view);
2638 if (!region_view->region()->playlist()) {
2642 if (Config->get_edit_mode() == Splice) {
2646 RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id);
2647 _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), true, false));
2649 begin_reversible_command (Operations::drag_region_brush);
2652 /** Start a grab where a time range is selected, track(s) are selected, and the
2653 * user clicks and drags a region with a modifier in order to create a new region containing
2654 * the section of the clicked region that lies within the time range.
2657 Editor::start_selection_grab (ArdourCanvas::Item* /*item*/, GdkEvent* event)
2659 if (clicked_regionview == 0) {
2663 /* lets try to create new Region for the selection */
2665 vector<boost::shared_ptr<Region> > new_regions;
2666 create_region_from_selection (new_regions);
2668 if (new_regions.empty()) {
2672 /* XXX fix me one day to use all new regions */
2674 boost::shared_ptr<Region> region (new_regions.front());
2676 /* add it to the current stream/playlist.
2678 tricky: the streamview for the track will add a new regionview. we will
2679 catch the signal it sends when it creates the regionview to
2680 set the regionview we want to then drag.
2683 latest_regionviews.clear();
2684 sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
2686 /* A selection grab currently creates two undo/redo operations, one for
2687 creating the new region and another for moving it.
2690 begin_reversible_command (Operations::selection_grab);
2692 boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
2694 playlist->clear_changes ();
2695 clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
2696 _session->add_command(new StatefulDiffCommand (playlist));
2698 commit_reversible_command ();
2702 if (latest_regionviews.empty()) {
2703 /* something went wrong */
2707 /* we need to deselect all other regionviews, and select this one
2708 i'm ignoring undo stuff, because the region creation will take care of it
2710 selection->set (latest_regionviews);
2712 _drags->set (new RegionMoveDrag (this, latest_regionviews.front()->get_canvas_group(), latest_regionviews.front(), latest_regionviews, false, false), event);
2718 if (_drags->active ()) {
2721 selection->clear ();
2726 Editor::set_internal_edit (bool yn)
2728 if (_internal_editing == yn) {
2732 _internal_editing = yn;
2735 pre_internal_mouse_mode = mouse_mode;
2736 pre_internal_snap_type = _snap_type;
2737 pre_internal_snap_mode = _snap_mode;
2739 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2740 (*i)->enter_internal_edit_mode ();
2743 set_snap_to (internal_snap_type);
2744 set_snap_mode (internal_snap_mode);
2748 internal_snap_mode = _snap_mode;
2749 internal_snap_type = _snap_type;
2751 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2752 (*i)->leave_internal_edit_mode ();
2755 if (mouse_mode == MouseDraw && pre_internal_mouse_mode != MouseDraw) {
2756 /* we were drawing .. flip back to something sensible */
2757 set_mouse_mode (pre_internal_mouse_mode);
2760 set_snap_to (pre_internal_snap_type);
2761 set_snap_mode (pre_internal_snap_mode);
2764 set_canvas_cursor ();
2767 /** Update _join_object_range_state which indicate whether we are over the top or bottom half of a region view,
2768 * used by the `join object/range' tool mode.
2771 Editor::update_join_object_range_location (double /*x*/, double y)
2773 /* XXX: actually, this decides based on whether the mouse is in the top
2774 or bottom half of a the waveform part RouteTimeAxisView;
2776 Note that entered_{track,regionview} is not always setup (e.g. if
2777 the mouse is over a TimeSelection), and to get a Region
2778 that we're over requires searching the playlist.
2781 if (!smart_mode_action->get_active() || (mouse_mode != MouseRange && mouse_mode != MouseObject)) {
2782 _join_object_range_state = JOIN_OBJECT_RANGE_NONE;
2786 if (mouse_mode == MouseObject) {
2787 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2788 } else if (mouse_mode == MouseRange) {
2789 _join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
2792 /* XXX: maybe we should make entered_track work in all cases, rather than resorting to this */
2793 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y + vertical_adjustment.get_value() - canvas_timebars_vsize);
2797 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
2802 rtv->canvas_display()->w2i (cx, cy);
2804 double const c = cy / (rtv->view()->child_height() - TimeAxisViewItem::NAME_HIGHLIGHT_SIZE);
2806 double const f = modf (c, &d);
2808 _join_object_range_state = f < 0.5 ? JOIN_OBJECT_RANGE_RANGE : JOIN_OBJECT_RANGE_OBJECT;
2814 Editor::effective_mouse_mode () const
2816 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
2818 } else if (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE) {
2826 Editor::remove_midi_note (ArdourCanvas::Item* item, GdkEvent *)
2828 ArdourCanvas::CanvasNoteEvent* e = dynamic_cast<ArdourCanvas::CanvasNoteEvent*> (item);
2831 e->region_view().delete_note (e->note ());
2835 Editor::set_canvas_cursor_for_region_view (double x, RegionView* rv)
2839 ArdourCanvas::Group* g = rv->get_canvas_group ();
2840 ArdourCanvas::Group* p = g->get_parent_group ();
2842 /* Compute x in region view parent coordinates */
2846 double x1, x2, y1, y2;
2847 g->get_bounds (x1, y1, x2, y2);
2849 /* Halfway across the region */
2850 double const h = (x1 + x2) / 2;
2852 Trimmable::CanTrim ct = rv->region()->can_trim ();
2854 if (ct & Trimmable::FrontTrimEarlier) {
2855 set_canvas_cursor (_cursors->left_side_trim);
2857 set_canvas_cursor (_cursors->left_side_trim_right_only);
2860 if (ct & Trimmable::EndTrimLater) {
2861 set_canvas_cursor (_cursors->right_side_trim);
2863 set_canvas_cursor (_cursors->right_side_trim_left_only);
2868 /** Obtain the pointer position in world coordinates */
2870 Editor::get_pointer_position (double& x, double& y) const
2873 track_canvas->get_pointer (px, py);
2874 track_canvas->window_to_world (px, py, x, y);