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 && _join_object_range_state == JOIN_OBJECT_RANGE_NONE) {
461 /* in all modes except range 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);
644 case FadeInHandleItem:
646 case FadeOutHandleItem:
648 if (doing_object_stuff() || (mouse_mode != MouseRange && mouse_mode != MouseObject)) {
649 set_selected_regionview_from_click (press, op);
650 } else if (event->type == GDK_BUTTON_PRESS) {
651 set_selected_track_as_side_effect (op);
655 case ControlPointItem:
656 set_selected_track_as_side_effect (op);
657 if (doing_object_stuff() || (mouse_mode != MouseRange && mouse_mode != MouseObject)) {
658 set_selected_control_point_from_click (op, false);
663 /* for context click, select track */
664 if (event->button.button == 3) {
665 selection->clear_tracks ();
666 set_selected_track_as_side_effect (op);
670 case AutomationTrackItem:
671 set_selected_track_as_side_effect (op);
680 Editor::button_press_handler_1 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
682 /* single mouse clicks on any of these item types operate
683 independent of mouse mode, mostly because they are
684 not on the main track canvas or because we want
689 case PlayheadCursorItem:
690 _drags->set (new CursorDrag (this, item, true), event);
694 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
695 hide_marker (item, event);
697 _drags->set (new MarkerDrag (this, item), event);
701 case TempoMarkerItem:
703 TempoMarker* m = reinterpret_cast<TempoMarker*> (item->get_data ("marker"));
705 if (m->tempo().movable ()) {
707 new TempoMarkerDrag (
710 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
720 case MeterMarkerItem:
722 MeterMarker* m = reinterpret_cast<MeterMarker*> (item->get_data ("marker"));
724 if (m->meter().movable ()) {
726 new MeterMarkerDrag (
729 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
742 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
743 _drags->set (new CursorDrag (this, &playhead_cursor->canvas_item, false), event);
749 case RangeMarkerBarItem:
750 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
751 _drags->set (new CursorDrag (this, &playhead_cursor->canvas_item, false), event);
753 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateRangeMarker), event);
758 case CdMarkerBarItem:
759 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
760 _drags->set (new CursorDrag (this, &playhead_cursor->canvas_item, false), event);
762 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateCDMarker), event);
767 case TransportMarkerBarItem:
768 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
769 _drags->set (new CursorDrag (this, &playhead_cursor->canvas_item, false), event);
771 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateTransportMarker), event);
780 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
781 /* special case: allow trim of range selections in joined object mode;
782 in theory eff should equal MouseRange in this case, but it doesn't
783 because entering the range selection canvas item results in entered_regionview
784 being set to 0, so update_join_object_range_location acts as if we aren't
787 if (item_type == StartSelectionTrimItem) {
788 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
789 } else if (item_type == EndSelectionTrimItem) {
790 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
794 Editing::MouseMode eff = effective_mouse_mode ();
796 /* special case: allow drag of region fade in/out in object mode with join object/range enabled */
797 if (item_type == FadeInHandleItem || item_type == FadeOutHandleItem) {
804 case StartSelectionTrimItem:
805 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
808 case EndSelectionTrimItem:
809 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
813 if (Keyboard::modifier_state_contains
814 (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier))) {
815 // contains and not equals because I can't use alt as a modifier alone.
816 start_selection_grab (item, event);
817 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
818 /* grab selection for moving */
819 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionMove), event);
821 double const y = event->button.y + vertical_adjustment.get_value() - canvas_timebars_vsize;
822 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
824 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
825 if (smart_mode_action->get_active() && atv) {
826 /* smart "join" mode: drag automation */
827 _drags->set (new AutomationRangeDrag (this, atv->base_item(), selection->time), event, _cursors->up_down);
829 /* this was debated, but decided the more common action was to
830 make a new selection */
831 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
838 if (internal_editing()) {
839 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
840 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
844 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
849 case RegionViewNameHighlight:
850 if (!clicked_regionview->region()->locked()) {
851 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
852 _drags->set (new TrimDrag (this, item, clicked_regionview, s.by_layer()), event);
857 case LeftFrameHandle:
858 case RightFrameHandle:
859 if (!internal_editing() && doing_object_stuff() && !clicked_regionview->region()->locked()) {
860 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
861 _drags->set (new TrimDrag (this, item, clicked_regionview, s.by_layer()), event);
867 if (!internal_editing()) {
868 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
877 if (internal_editing()) {
878 /* trim notes if we're in internal edit mode and near the ends of the note */
879 ArdourCanvas::CanvasNote* cn = dynamic_cast<ArdourCanvas::CanvasNote*> (item);
880 if (cn && cn->big_enough_to_trim() && cn->mouse_near_ends()) {
881 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
883 _drags->set (new NoteDrag (this, item), event);
896 if (internal_editing()) {
897 ArdourCanvas::CanvasNoteEvent* cn = dynamic_cast<ArdourCanvas::CanvasNoteEvent*> (item);
898 if (cn->mouse_near_ends()) {
899 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
901 _drags->set (new NoteDrag (this, item), event);
911 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)) &&
912 event->type == GDK_BUTTON_PRESS) {
914 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
916 } else if (event->type == GDK_BUTTON_PRESS) {
919 case FadeInHandleItem:
921 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
922 _drags->set (new FadeInDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), s), event, _cursors->fade_in);
926 case FadeOutHandleItem:
928 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
929 _drags->set (new FadeOutDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), s), event, _cursors->fade_out);
933 case FeatureLineItem:
935 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier)) {
936 remove_transient(item);
940 _drags->set (new FeatureLineDrag (this, item), event);
946 if (dynamic_cast<AutomationRegionView*> (clicked_regionview)) {
947 /* click on an automation region view; do nothing here and let the ARV's signal handler
953 if (internal_editing ()) {
954 if (event->type == GDK_2BUTTON_PRESS && event->button.button == 1) {
955 Glib::RefPtr<Action> act = ActionManager::get_action (X_("MouseMode"), X_("toggle-internal-edit"));
961 /* click on a normal region view */
962 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
963 add_region_copy_drag (item, event, clicked_regionview);
964 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
965 add_region_brush_drag (item, event, clicked_regionview);
967 add_region_drag (item, event, clicked_regionview);
971 if (!internal_editing() && (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE && !selection->regions.empty())) {
972 _drags->add (new SelectionDrag (this, clicked_axisview->get_selection_rect (clicked_selection)->rect, SelectionDrag::SelectionMove));
975 _drags->start_grab (event);
978 case RegionViewNameHighlight:
979 case LeftFrameHandle:
980 case RightFrameHandle:
981 if (!clicked_regionview->region()->locked()) {
982 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
983 _drags->set (new TrimDrag (this, item, clicked_regionview, s.by_layer()), event);
990 /* rename happens on edit clicks */
991 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
992 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, s.by_layer()), event);
997 case ControlPointItem:
998 _drags->set (new ControlPointDrag (this, item), event);
1002 case AutomationLineItem:
1003 _drags->set (new LineDrag (this, item), event);
1008 if (internal_editing()) {
1009 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
1010 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
1014 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
1018 case AutomationTrackItem:
1020 TimeAxisView* parent = clicked_axisview->get_parent ();
1021 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (clicked_axisview);
1023 if (parent && dynamic_cast<MidiTimeAxisView*> (parent) && atv->show_regions ()) {
1024 /* create a MIDI region so that we have somewhere to put automation */
1025 _drags->set (new RegionCreateDrag (this, item, parent), event);
1027 /* rubberband drag to select automation points */
1028 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
1035 if (smart_mode_action->get_active()) {
1036 /* we're in "smart" joined mode, and we've clicked on a Selection */
1037 double const y = event->button.y + vertical_adjustment.get_value() - canvas_timebars_vsize;
1038 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
1040 /* if we're over an automation track, start a drag of its data */
1041 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
1043 _drags->set (new AutomationRangeDrag (this, atv->base_item(), selection->time), event, _cursors->up_down);
1046 /* if we're over a track and a region, and in the `object' part of a region,
1047 put a selection around the region and drag both
1049 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1050 if (rtv && _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
1051 boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track> (rtv->route ());
1053 boost::shared_ptr<Playlist> pl = t->playlist ();
1056 boost::shared_ptr<Region> r = pl->top_region_at (event_frame (event));
1058 RegionView* rv = rtv->view()->find_view (r);
1059 clicked_selection = select_range (rv->region()->position(),
1060 rv->region()->last_frame()+1);
1061 _drags->add (new SelectionDrag (this, item, SelectionDrag::SelectionMove));
1062 list<RegionView*> rvs;
1064 _drags->add (new RegionMoveDrag (this, item, rv, rvs, false, false));
1065 _drags->start_grab (event);
1076 case ImageFrameHandleStartItem:
1077 imageframe_start_handle_op(item, event) ;
1080 case ImageFrameHandleEndItem:
1081 imageframe_end_handle_op(item, event) ;
1084 case MarkerViewHandleStartItem:
1085 markerview_item_start_handle_op(item, event) ;
1088 case MarkerViewHandleEndItem:
1089 markerview_item_end_handle_op(item, event) ;
1092 case MarkerViewItem:
1093 start_markerview_grab(item, event) ;
1095 case ImageFrameItem:
1096 start_imageframe_grab(item, event) ;
1112 switch (item_type) {
1114 _drags->set (new LineDrag (this, item), event);
1117 case ControlPointItem:
1118 _drags->set (new ControlPointDrag (this, item), event);
1122 case AutomationLineItem:
1123 _drags->set (new LineDrag (this, item), event);
1133 if (event->type == GDK_BUTTON_PRESS) {
1134 _drags->set (new MouseZoomDrag (this, item), event);
1141 if (internal_editing() && item_type == NoteItem) {
1142 /* drag notes if we're in internal edit mode */
1143 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
1145 } else if ((!internal_editing() || dynamic_cast<AudioRegionView*> (clicked_regionview)) && clicked_regionview) {
1146 /* do time-FX if we're not in internal edit mode, or we are but we clicked on an audio region */
1147 _drags->set (new TimeFXDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1153 _drags->set (new ScrubDrag (this, item), event);
1154 scrub_reversals = 0;
1155 scrub_reverse_distance = 0;
1156 last_scrub_x = event->button.x;
1157 scrubbing_direction = 0;
1158 set_canvas_cursor (_cursors->transparent);
1170 Editor::button_press_handler_2 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1172 Editing::MouseMode const eff = effective_mouse_mode ();
1175 switch (item_type) {
1177 if (internal_editing ()) {
1178 /* no region drags in internal edit mode */
1182 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
1183 add_region_copy_drag (item, event, clicked_regionview);
1185 add_region_drag (item, event, clicked_regionview);
1187 _drags->start_grab (event);
1190 case ControlPointItem:
1191 _drags->set (new ControlPointDrag (this, item), event);
1199 switch (item_type) {
1200 case RegionViewNameHighlight:
1201 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1205 case LeftFrameHandle:
1206 case RightFrameHandle:
1207 if (!internal_editing ()) {
1208 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1213 case RegionViewName:
1214 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer()), event);
1228 /* relax till release */
1234 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1235 temporal_zoom_to_frame (false, event_frame (event));
1237 temporal_zoom_to_frame (true, event_frame(event));
1250 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1252 if (event->type != GDK_BUTTON_PRESS) {
1256 Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->track_canvas->get_window();
1258 if (canvas_window) {
1259 Glib::RefPtr<const Gdk::Window> pointer_window;
1262 Gdk::ModifierType mask;
1264 pointer_window = canvas_window->get_pointer (x, y, mask);
1266 if (pointer_window == track_canvas->get_bin_window()) {
1267 track_canvas->window_to_world (x, y, wx, wy);
1271 pre_press_cursor = current_canvas_cursor;
1273 track_canvas->grab_focus();
1275 if (_session && _session->actively_recording()) {
1281 if (internal_editing()) {
1282 bool leave_internal_edit_mode = false;
1284 switch (item_type) {
1289 if (!dynamic_cast<MidiRegionView*> (clicked_regionview)) {
1290 leave_internal_edit_mode = true;
1294 case PlayheadCursorItem:
1296 case TempoMarkerItem:
1297 case MeterMarkerItem:
1301 case RangeMarkerBarItem:
1302 case CdMarkerBarItem:
1303 case TransportMarkerBarItem:
1304 /* button press on these events never does anything to
1305 change the editing mode.
1310 leave_internal_edit_mode = true;
1317 if (leave_internal_edit_mode) {
1318 ActionManager::do_action ("MouseMode", "toggle-internal-edit");
1322 button_selection (item, event, item_type);
1324 if (!_drags->active () &&
1325 (Keyboard::is_delete_event (&event->button) ||
1326 Keyboard::is_context_menu_event (&event->button) ||
1327 Keyboard::is_edit_event (&event->button))) {
1329 /* handled by button release */
1333 switch (event->button.button) {
1335 return button_press_handler_1 (item, event, item_type);
1339 return button_press_handler_2 (item, event, item_type);
1346 return button_press_dispatch (&event->button);
1355 Editor::button_press_dispatch (GdkEventButton* ev)
1357 /* this function is intended only for buttons 4 and above.
1360 Gtkmm2ext::MouseButton b (ev->state, ev->button);
1361 return button_bindings->activate (b, Gtkmm2ext::Bindings::Press);
1365 Editor::button_release_dispatch (GdkEventButton* ev)
1367 /* this function is intended only for buttons 4 and above.
1370 Gtkmm2ext::MouseButton b (ev->state, ev->button);
1371 return button_bindings->activate (b, Gtkmm2ext::Bindings::Release);
1375 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1377 framepos_t where = event_frame (event, 0, 0);
1378 AutomationTimeAxisView* atv = 0;
1380 if (pre_press_cursor) {
1381 set_canvas_cursor (pre_press_cursor);
1382 pre_press_cursor = 0;
1385 /* no action if we're recording */
1387 if (_session && _session->actively_recording()) {
1391 /* see if we're finishing a drag */
1393 bool were_dragging = false;
1394 if (_drags->active ()) {
1395 bool const r = _drags->end_grab (event);
1397 /* grab dragged, so do nothing else */
1401 were_dragging = true;
1404 update_region_layering_order_editor ();
1406 /* edit events get handled here */
1408 if (!_drags->active () && Keyboard::is_edit_event (&event->button)) {
1409 switch (item_type) {
1411 show_region_properties ();
1414 case TempoMarkerItem:
1415 edit_tempo_marker (item);
1418 case MeterMarkerItem:
1419 edit_meter_marker (item);
1422 case RegionViewName:
1423 if (clicked_regionview->name_active()) {
1424 return mouse_rename_region (item, event);
1428 case ControlPointItem:
1429 edit_control_point (item);
1442 /* context menu events get handled here */
1444 if (Keyboard::is_context_menu_event (&event->button)) {
1446 context_click_event = *event;
1448 if (!_drags->active ()) {
1450 /* no matter which button pops up the context menu, tell the menu
1451 widget to use button 1 to drive menu selection.
1454 switch (item_type) {
1456 case FadeInHandleItem:
1458 case FadeOutHandleItem:
1459 popup_fade_context_menu (1, event->button.time, item, item_type);
1463 popup_track_context_menu (1, event->button.time, item_type, false);
1467 case RegionViewNameHighlight:
1468 case LeftFrameHandle:
1469 case RightFrameHandle:
1470 case RegionViewName:
1471 popup_track_context_menu (1, event->button.time, item_type, false);
1475 popup_track_context_menu (1, event->button.time, item_type, true);
1478 case AutomationTrackItem:
1479 popup_track_context_menu (1, event->button.time, item_type, false);
1483 case RangeMarkerBarItem:
1484 case TransportMarkerBarItem:
1485 case CdMarkerBarItem:
1488 popup_ruler_menu (where, item_type);
1492 marker_context_menu (&event->button, item);
1495 case TempoMarkerItem:
1496 tempo_or_meter_marker_context_menu (&event->button, item);
1499 case MeterMarkerItem:
1500 tempo_or_meter_marker_context_menu (&event->button, item);
1503 case CrossfadeViewItem:
1504 popup_track_context_menu (1, event->button.time, item_type, false);
1507 case ControlPointItem:
1508 popup_control_point_context_menu (item, event);
1512 case ImageFrameItem:
1513 popup_imageframe_edit_menu(1, event->button.time, item, true) ;
1515 case ImageFrameTimeAxisItem:
1516 popup_imageframe_edit_menu(1, event->button.time, item, false) ;
1518 case MarkerViewItem:
1519 popup_marker_time_axis_edit_menu(1, event->button.time, item, true) ;
1521 case MarkerTimeAxisItem:
1522 popup_marker_time_axis_edit_menu(1, event->button.time, item, false) ;
1534 /* delete events get handled here */
1536 Editing::MouseMode const eff = effective_mouse_mode ();
1538 if (!_drags->active () && Keyboard::is_delete_event (&event->button)) {
1540 switch (item_type) {
1541 case TempoMarkerItem:
1542 remove_tempo_marker (item);
1545 case MeterMarkerItem:
1546 remove_meter_marker (item);
1550 remove_marker (*item, event);
1554 if (eff == MouseObject) {
1555 remove_clicked_region ();
1559 case ControlPointItem:
1560 remove_control_point (item);
1564 remove_midi_note (item, event);
1573 switch (event->button.button) {
1576 switch (item_type) {
1577 /* see comments in button_press_handler */
1578 case PlayheadCursorItem:
1581 case AutomationLineItem:
1582 case StartSelectionTrimItem:
1583 case EndSelectionTrimItem:
1587 if (!_dragging_playhead) {
1588 snap_to_with_modifier (where, event, 0, true);
1589 mouse_add_new_marker (where);
1593 case CdMarkerBarItem:
1594 if (!_dragging_playhead) {
1595 // if we get here then a dragged range wasn't done
1596 snap_to_with_modifier (where, event, 0, true);
1597 mouse_add_new_marker (where, true);
1602 if (!_dragging_playhead) {
1603 snap_to_with_modifier (where, event);
1604 mouse_add_new_tempo_event (where);
1609 if (!_dragging_playhead) {
1610 mouse_add_new_meter_event (pixel_to_frame (event->button.x));
1621 switch (item_type) {
1622 case AutomationTrackItem:
1623 atv = dynamic_cast<AutomationTimeAxisView*>(clicked_axisview);
1625 atv->add_automation_event (event, where, event->button.y);
1635 switch (item_type) {
1638 /* check that we didn't drag before releasing, since
1639 its really annoying to create new control
1640 points when doing this.
1642 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (clicked_regionview);
1643 if (!were_dragging && arv) {
1644 arv->add_gain_point_event (item, event);
1650 case AutomationTrackItem:
1651 dynamic_cast<AutomationTimeAxisView*>(clicked_axisview)->
1652 add_automation_event (event, where, event->button.y);
1661 set_canvas_cursor (current_canvas_cursor);
1662 if (scrubbing_direction == 0) {
1663 /* no drag, just a click */
1664 switch (item_type) {
1666 play_selected_region ();
1672 /* make sure we stop */
1673 _session->request_transport_speed (0.0);
1682 /* do any (de)selection operations that should occur on button release */
1683 button_selection (item, event, item_type);
1692 switch (item_type) {
1694 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1696 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier|Keyboard::SecondaryModifier))) {
1699 // Button2 click is unused
1714 // x_style_paste (where, 1.0);
1735 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1742 switch (item_type) {
1743 case ControlPointItem:
1744 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1745 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1746 cp->set_visible (true);
1750 at_y = cp->get_y ();
1751 cp->i2w (at_x, at_y);
1755 fraction = 1.0 - (cp->get_y() / cp->line().height());
1757 if (is_drawable() && !_drags->active ()) {
1758 set_canvas_cursor (_cursors->fader);
1761 _verbose_cursor->set (cp->line().get_verbose_cursor_string (fraction), at_x, at_y);
1762 _verbose_cursor->show ();
1767 if (mouse_mode == MouseGain) {
1768 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1770 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredGainLine.get();
1771 if (is_drawable()) {
1772 set_canvas_cursor (_cursors->fader);
1777 case AutomationLineItem:
1778 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1779 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1781 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredAutomationLine.get();
1783 if (is_drawable()) {
1784 set_canvas_cursor (_cursors->fader);
1789 case RegionViewNameHighlight:
1790 if (is_drawable() && doing_object_stuff() && entered_regionview) {
1791 set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1792 _over_region_trim_target = true;
1796 case LeftFrameHandle:
1797 case RightFrameHandle:
1798 if (is_drawable() && doing_object_stuff() && !internal_editing() && entered_regionview) {
1799 set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1803 case StartSelectionTrimItem:
1805 case ImageFrameHandleStartItem:
1806 case MarkerViewHandleStartItem:
1808 if (is_drawable()) {
1809 set_canvas_cursor (_cursors->left_side_trim);
1812 case EndSelectionTrimItem:
1814 case ImageFrameHandleEndItem:
1815 case MarkerViewHandleEndItem:
1817 if (is_drawable()) {
1818 set_canvas_cursor (_cursors->right_side_trim);
1822 case PlayheadCursorItem:
1823 if (is_drawable()) {
1824 switch (_edit_point) {
1826 set_canvas_cursor (_cursors->grabber_edit_point);
1829 set_canvas_cursor (_cursors->grabber);
1835 case RegionViewName:
1837 /* when the name is not an active item, the entire name highlight is for trimming */
1839 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1840 if (mouse_mode == MouseObject && is_drawable()) {
1841 set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1842 _over_region_trim_target = true;
1848 case AutomationTrackItem:
1849 if (is_drawable()) {
1850 Gdk::Cursor *cursor;
1851 switch (mouse_mode) {
1853 cursor = _cursors->selector;
1856 cursor = _cursors->zoom_in;
1859 cursor = _cursors->cross_hair;
1863 set_canvas_cursor (cursor);
1865 AutomationTimeAxisView* atv;
1866 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1867 clear_entered_track = false;
1868 set_entered_track (atv);
1874 case RangeMarkerBarItem:
1875 case TransportMarkerBarItem:
1876 case CdMarkerBarItem:
1879 if (is_drawable()) {
1880 set_canvas_cursor (_cursors->timebar);
1885 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1888 entered_marker = marker;
1889 marker->set_color_rgba (ARDOUR_UI::config()->canvasvar_EnteredMarker.get());
1891 case MeterMarkerItem:
1892 case TempoMarkerItem:
1893 if (is_drawable()) {
1894 set_canvas_cursor (_cursors->timebar);
1898 case FadeInHandleItem:
1899 if (mouse_mode == MouseObject && !internal_editing()) {
1900 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1902 rect->property_fill_color_rgba() = 0xBBBBBBAA;
1904 set_canvas_cursor (_cursors->fade_in);
1908 case FadeOutHandleItem:
1909 if (mouse_mode == MouseObject && !internal_editing()) {
1910 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1912 rect->property_fill_color_rgba() = 0xBBBBBBAA;
1914 set_canvas_cursor (_cursors->fade_out);
1917 case FeatureLineItem:
1919 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1920 line->property_fill_color_rgba() = 0xFF0000FF;
1924 if (smart_mode_action->get_active()) {
1925 set_canvas_cursor ();
1933 /* second pass to handle entered track status in a comprehensible way.
1936 switch (item_type) {
1938 case AutomationLineItem:
1939 case ControlPointItem:
1940 /* these do not affect the current entered track state */
1941 clear_entered_track = false;
1944 case AutomationTrackItem:
1945 /* handled above already */
1949 set_entered_track (0);
1957 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent*, ItemType item_type)
1967 switch (item_type) {
1968 case ControlPointItem:
1969 cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
1970 if (cp->line().the_list()->interpolation() != AutomationList::Discrete) {
1971 if (cp->line().npoints() > 1 && !cp->get_selected()) {
1972 cp->set_visible (false);
1976 if (is_drawable()) {
1977 set_canvas_cursor (current_canvas_cursor);
1980 _verbose_cursor->hide ();
1983 case RegionViewNameHighlight:
1984 case LeftFrameHandle:
1985 case RightFrameHandle:
1986 case StartSelectionTrimItem:
1987 case EndSelectionTrimItem:
1988 case PlayheadCursorItem:
1991 case ImageFrameHandleStartItem:
1992 case ImageFrameHandleEndItem:
1993 case MarkerViewHandleStartItem:
1994 case MarkerViewHandleEndItem:
1997 _over_region_trim_target = false;
1999 if (is_drawable()) {
2000 set_canvas_cursor (current_canvas_cursor);
2005 case AutomationLineItem:
2006 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
2008 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
2010 line->property_fill_color_rgba() = al->get_line_color();
2012 if (is_drawable()) {
2013 set_canvas_cursor (current_canvas_cursor);
2017 case RegionViewName:
2018 /* see enter_handler() for notes */
2019 _over_region_trim_target = false;
2021 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
2022 if (is_drawable() && mouse_mode == MouseObject) {
2023 set_canvas_cursor (current_canvas_cursor);
2028 case RangeMarkerBarItem:
2029 case TransportMarkerBarItem:
2030 case CdMarkerBarItem:
2034 if (is_drawable()) {
2035 set_canvas_cursor (current_canvas_cursor);
2040 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2044 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
2045 location_flags_changed (loc, this);
2048 case MeterMarkerItem:
2049 case TempoMarkerItem:
2051 if (is_drawable()) {
2052 set_canvas_cursor (current_canvas_cursor);
2057 case FadeInHandleItem:
2058 case FadeOutHandleItem:
2059 rv = static_cast<RegionView*>(item->get_data ("regionview"));
2061 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
2063 rect->property_fill_color_rgba() = rv->get_fill_color();
2064 rect->property_outline_pixels() = 0;
2067 set_canvas_cursor (current_canvas_cursor);
2070 case AutomationTrackItem:
2071 if (is_drawable()) {
2072 set_canvas_cursor (current_canvas_cursor);
2073 clear_entered_track = true;
2074 Glib::signal_idle().connect (sigc::mem_fun(*this, &Editor::left_automation_track));
2077 case FeatureLineItem:
2079 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
2080 line->property_fill_color_rgba() = (guint) ARDOUR_UI::config()->canvasvar_ZeroLine.get();;
2092 Editor::left_automation_track ()
2094 if (clear_entered_track) {
2095 set_entered_track (0);
2096 clear_entered_track = false;
2102 Editor::scrub (framepos_t frame, double current_x)
2106 if (scrubbing_direction == 0) {
2108 _session->request_locate (frame, false);
2109 _session->request_transport_speed (0.1);
2110 scrubbing_direction = 1;
2114 if (last_scrub_x > current_x) {
2116 /* pointer moved to the left */
2118 if (scrubbing_direction > 0) {
2120 /* we reversed direction to go backwards */
2123 scrub_reverse_distance += (int) (last_scrub_x - current_x);
2127 /* still moving to the left (backwards) */
2129 scrub_reversals = 0;
2130 scrub_reverse_distance = 0;
2132 delta = 0.01 * (last_scrub_x - current_x);
2133 _session->request_transport_speed_nonzero (_session->transport_speed() - delta);
2137 /* pointer moved to the right */
2139 if (scrubbing_direction < 0) {
2140 /* we reversed direction to go forward */
2143 scrub_reverse_distance += (int) (current_x - last_scrub_x);
2146 /* still moving to the right */
2148 scrub_reversals = 0;
2149 scrub_reverse_distance = 0;
2151 delta = 0.01 * (current_x - last_scrub_x);
2152 _session->request_transport_speed_nonzero (_session->transport_speed() + delta);
2156 /* if there have been more than 2 opposite motion moves detected, or one that moves
2157 back more than 10 pixels, reverse direction
2160 if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
2162 if (scrubbing_direction > 0) {
2163 /* was forwards, go backwards */
2164 _session->request_transport_speed (-0.1);
2165 scrubbing_direction = -1;
2167 /* was backwards, go forwards */
2168 _session->request_transport_speed (0.1);
2169 scrubbing_direction = 1;
2172 scrub_reverse_distance = 0;
2173 scrub_reversals = 0;
2177 last_scrub_x = current_x;
2181 Editor::motion_handler (ArdourCanvas::Item* /*item*/, GdkEvent* event, bool from_autoscroll)
2183 _last_motion_y = event->motion.y;
2185 if (event->motion.is_hint) {
2188 /* We call this so that MOTION_NOTIFY events continue to be
2189 delivered to the canvas. We need to do this because we set
2190 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
2191 the density of the events, at the expense of a round-trip
2192 to the server. Given that this will mostly occur on cases
2193 where DISPLAY = :0.0, and given the cost of what the motion
2194 event might do, its a good tradeoff.
2197 track_canvas->get_pointer (x, y);
2200 if (current_stepping_trackview) {
2201 /* don't keep the persistent stepped trackview if the mouse moves */
2202 current_stepping_trackview = 0;
2203 step_timeout.disconnect ();
2206 if (_session && _session->actively_recording()) {
2207 /* Sorry. no dragging stuff around while we record */
2211 JoinObjectRangeState const old = _join_object_range_state;
2212 update_join_object_range_location (event->motion.x, event->motion.y);
2213 if (_join_object_range_state != old) {
2214 set_canvas_cursor ();
2217 if (_over_region_trim_target) {
2218 set_canvas_cursor_for_region_view (event->motion.x, entered_regionview);
2221 bool handled = false;
2222 if (_drags->active ()) {
2223 handled = _drags->motion_handler (event, from_autoscroll);
2230 track_canvas_motion (event);
2235 Editor::can_remove_control_point (ArdourCanvas::Item* item)
2237 ControlPoint* control_point;
2239 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2240 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2244 AutomationLine& line = control_point->line ();
2245 if (dynamic_cast<AudioRegionGainLine*> (&line)) {
2246 /* we shouldn't remove the first or last gain point in region gain lines */
2247 if (line.is_last_point(*control_point) || line.is_first_point(*control_point)) {
2256 Editor::remove_control_point (ArdourCanvas::Item* item)
2258 if (!can_remove_control_point (item)) {
2262 ControlPoint* control_point;
2264 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2265 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2269 control_point->line().remove_point (*control_point);
2273 Editor::edit_control_point (ArdourCanvas::Item* item)
2275 ControlPoint* p = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"));
2278 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2282 ControlPointDialog d (p);
2283 d.set_position (Gtk::WIN_POS_MOUSE);
2286 if (d.run () != RESPONSE_ACCEPT) {
2290 p->line().modify_point_y (*p, d.get_y_fraction ());
2294 Editor::edit_note (ArdourCanvas::Item* item)
2296 ArdourCanvas::CanvasNoteEvent* e = dynamic_cast<ArdourCanvas::CanvasNoteEvent*> (item);
2299 EditNoteDialog d (&e->region_view(), e);
2300 d.set_position (Gtk::WIN_POS_MOUSE);
2308 Editor::visible_order_range (int* low, int* high) const
2310 *low = TimeAxisView::max_order ();
2313 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
2315 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
2317 if (!rtv->hidden()) {
2319 if (*high < rtv->order()) {
2320 *high = rtv->order ();
2323 if (*low > rtv->order()) {
2324 *low = rtv->order ();
2331 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
2333 /* Either add to or set the set the region selection, unless
2334 this is an alignment click (control used)
2337 if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
2338 TimeAxisView* tv = &rv.get_time_axis_view();
2339 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
2341 if (rtv && rtv->is_track()) {
2342 speed = rtv->track()->speed();
2345 framepos_t where = get_preferred_edit_position();
2349 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
2351 align_region (rv.region(), SyncPoint, (framepos_t) (where * speed));
2353 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
2355 align_region (rv.region(), End, (framepos_t) (where * speed));
2359 align_region (rv.region(), Start, (framepos_t) (where * speed));
2366 Editor::collect_new_region_view (RegionView* rv)
2368 latest_regionviews.push_back (rv);
2372 Editor::collect_and_select_new_region_view (RegionView* rv)
2375 latest_regionviews.push_back (rv);
2379 Editor::cancel_selection ()
2381 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2382 (*i)->hide_selection ();
2385 selection->clear ();
2386 clicked_selection = 0;
2391 Editor::point_trim (GdkEvent* event, framepos_t new_bound)
2393 RegionView* rv = clicked_regionview;
2395 /* Choose action dependant on which button was pressed */
2396 switch (event->button.button) {
2398 begin_reversible_command (_("start point trim"));
2400 if (selection->selected (rv)) {
2401 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
2402 i != selection->regions.by_layer().end(); ++i)
2405 cerr << "region view contains null region" << endl;
2408 if (!(*i)->region()->locked()) {
2409 (*i)->region()->clear_changes ();
2410 (*i)->region()->trim_front (new_bound);
2411 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2416 if (!rv->region()->locked()) {
2417 rv->region()->clear_changes ();
2418 rv->region()->trim_front (new_bound);
2419 _session->add_command(new StatefulDiffCommand (rv->region()));
2423 commit_reversible_command();
2427 begin_reversible_command (_("End point trim"));
2429 if (selection->selected (rv)) {
2431 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
2433 if (!(*i)->region()->locked()) {
2434 (*i)->region()->clear_changes();
2435 (*i)->region()->trim_end (new_bound);
2436 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2442 if (!rv->region()->locked()) {
2443 rv->region()->clear_changes ();
2444 rv->region()->trim_end (new_bound);
2445 _session->add_command (new StatefulDiffCommand (rv->region()));
2449 commit_reversible_command();
2458 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* /*event*/)
2463 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2464 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2468 Location* location = find_location_from_marker (marker, is_start);
2469 location->set_hidden (true, this);
2474 Editor::reposition_zoom_rect (framepos_t start, framepos_t end)
2476 double x1 = frame_to_pixel (start);
2477 double x2 = frame_to_pixel (end);
2478 double y2 = full_canvas_height - 1.0;
2480 zoom_rect->property_x1() = x1;
2481 zoom_rect->property_y1() = 1.0;
2482 zoom_rect->property_x2() = x2;
2483 zoom_rect->property_y2() = y2;
2488 Editor::mouse_rename_region (ArdourCanvas::Item* /*item*/, GdkEvent* /*event*/)
2490 using namespace Gtkmm2ext;
2492 ArdourPrompter prompter (false);
2494 prompter.set_prompt (_("Name for region:"));
2495 prompter.set_initial_text (clicked_regionview->region()->name());
2496 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
2497 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
2498 prompter.show_all ();
2499 switch (prompter.run ()) {
2500 case Gtk::RESPONSE_ACCEPT:
2502 prompter.get_result(str);
2504 clicked_regionview->region()->set_name (str);
2513 Editor::mouse_brush_insert_region (RegionView* rv, framepos_t pos)
2515 /* no brushing without a useful snap setting */
2517 switch (_snap_mode) {
2519 return; /* can't work because it allows region to be placed anywhere */
2524 switch (_snap_type) {
2532 /* don't brush a copy over the original */
2534 if (pos == rv->region()->position()) {
2538 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
2540 if (rtv == 0 || !rtv->is_track()) {
2544 boost::shared_ptr<Playlist> playlist = rtv->playlist();
2545 double speed = rtv->track()->speed();
2547 playlist->clear_changes ();
2548 boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region(), true));
2549 playlist->add_region (new_region, (framepos_t) (pos * speed));
2550 _session->add_command (new StatefulDiffCommand (playlist));
2552 // playlist is frozen, so we have to update manually XXX this is disgusting
2554 playlist->RegionAdded (new_region); /* EMIT SIGNAL */
2558 Editor::track_height_step_timeout ()
2560 if (get_microseconds() - last_track_height_step_timestamp < 250000) {
2561 current_stepping_trackview = 0;
2568 Editor::add_region_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2570 assert (region_view);
2572 if (!region_view->region()->playlist()) {
2576 _region_motion_group->raise_to_top ();
2578 if (Config->get_edit_mode() == Splice) {
2579 _drags->add (new RegionSpliceDrag (this, item, region_view, selection->regions.by_layer()));
2581 RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id);
2582 _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), false, false));
2585 /* sync the canvas to what we think is its current state */
2586 update_canvas_now();
2590 Editor::add_region_copy_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2592 assert (region_view);
2594 if (!region_view->region()->playlist()) {
2598 _region_motion_group->raise_to_top ();
2600 RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id);
2601 _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), false, true));
2605 Editor::add_region_brush_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2607 assert (region_view);
2609 if (!region_view->region()->playlist()) {
2613 if (Config->get_edit_mode() == Splice) {
2617 RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id);
2618 _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), true, false));
2620 begin_reversible_command (Operations::drag_region_brush);
2623 /** Start a grab where a time range is selected, track(s) are selected, and the
2624 * user clicks and drags a region with a modifier in order to create a new region containing
2625 * the section of the clicked region that lies within the time range.
2628 Editor::start_selection_grab (ArdourCanvas::Item* /*item*/, GdkEvent* event)
2630 if (clicked_regionview == 0) {
2634 /* lets try to create new Region for the selection */
2636 vector<boost::shared_ptr<Region> > new_regions;
2637 create_region_from_selection (new_regions);
2639 if (new_regions.empty()) {
2643 /* XXX fix me one day to use all new regions */
2645 boost::shared_ptr<Region> region (new_regions.front());
2647 /* add it to the current stream/playlist.
2649 tricky: the streamview for the track will add a new regionview. we will
2650 catch the signal it sends when it creates the regionview to
2651 set the regionview we want to then drag.
2654 latest_regionviews.clear();
2655 sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
2657 /* A selection grab currently creates two undo/redo operations, one for
2658 creating the new region and another for moving it.
2661 begin_reversible_command (Operations::selection_grab);
2663 boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
2665 playlist->clear_changes ();
2666 clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
2667 _session->add_command(new StatefulDiffCommand (playlist));
2669 commit_reversible_command ();
2673 if (latest_regionviews.empty()) {
2674 /* something went wrong */
2678 /* we need to deselect all other regionviews, and select this one
2679 i'm ignoring undo stuff, because the region creation will take care of it
2681 selection->set (latest_regionviews);
2683 _drags->set (new RegionMoveDrag (this, latest_regionviews.front()->get_canvas_group(), latest_regionviews.front(), latest_regionviews, false, false), event);
2689 if (_drags->active ()) {
2692 selection->clear ();
2697 Editor::set_internal_edit (bool yn)
2699 if (_internal_editing == yn) {
2703 _internal_editing = yn;
2706 pre_internal_mouse_mode = mouse_mode;
2707 pre_internal_snap_type = _snap_type;
2708 pre_internal_snap_mode = _snap_mode;
2710 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2711 (*i)->enter_internal_edit_mode ();
2714 set_snap_to (internal_snap_type);
2715 set_snap_mode (internal_snap_mode);
2719 internal_snap_mode = _snap_mode;
2720 internal_snap_type = _snap_type;
2722 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2723 (*i)->leave_internal_edit_mode ();
2726 if (mouse_mode == MouseDraw && pre_internal_mouse_mode != MouseDraw) {
2727 /* we were drawing .. flip back to something sensible */
2728 set_mouse_mode (pre_internal_mouse_mode);
2731 set_snap_to (pre_internal_snap_type);
2732 set_snap_mode (pre_internal_snap_mode);
2735 set_canvas_cursor ();
2738 /** Update _join_object_range_state which indicate whether we are over the top or bottom half of a region view,
2739 * used by the `join object/range' tool mode.
2742 Editor::update_join_object_range_location (double /*x*/, double y)
2744 /* XXX: actually, this decides based on whether the mouse is in the top
2745 or bottom half of a the waveform part RouteTimeAxisView;
2747 Note that entered_{track,regionview} is not always setup (e.g. if
2748 the mouse is over a TimeSelection), and to get a Region
2749 that we're over requires searching the playlist.
2752 if (!smart_mode_action->get_active() || (mouse_mode != MouseRange && mouse_mode != MouseObject)) {
2753 _join_object_range_state = JOIN_OBJECT_RANGE_NONE;
2757 if (mouse_mode == MouseObject) {
2758 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2759 } else if (mouse_mode == MouseRange) {
2760 _join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
2763 /* XXX: maybe we should make entered_track work in all cases, rather than resorting to this */
2764 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y + vertical_adjustment.get_value() - canvas_timebars_vsize);
2768 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
2773 rtv->canvas_display()->w2i (cx, cy);
2775 double const c = cy / (rtv->view()->child_height() - TimeAxisViewItem::NAME_HIGHLIGHT_SIZE);
2777 double const f = modf (c, &d);
2779 _join_object_range_state = f < 0.5 ? JOIN_OBJECT_RANGE_RANGE : JOIN_OBJECT_RANGE_OBJECT;
2785 Editor::effective_mouse_mode () const
2787 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
2789 } else if (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE) {
2797 Editor::remove_midi_note (ArdourCanvas::Item* item, GdkEvent *)
2799 ArdourCanvas::CanvasNoteEvent* e = dynamic_cast<ArdourCanvas::CanvasNoteEvent*> (item);
2802 e->region_view().delete_note (e->note ());
2806 Editor::set_canvas_cursor_for_region_view (double x, RegionView* rv)
2810 ArdourCanvas::Group* g = rv->get_canvas_group ();
2811 ArdourCanvas::Group* p = g->get_parent_group ();
2813 /* Compute x in region view parent coordinates */
2817 double x1, x2, y1, y2;
2818 g->get_bounds (x1, y1, x2, y2);
2820 /* Halfway across the region */
2821 double const h = (x1 + x2) / 2;
2823 Trimmable::CanTrim ct = rv->region()->can_trim ();
2825 if (ct & Trimmable::FrontTrimEarlier) {
2826 set_canvas_cursor (_cursors->left_side_trim);
2828 set_canvas_cursor (_cursors->left_side_trim_right_only);
2831 if (ct & Trimmable::EndTrimLater) {
2832 set_canvas_cursor (_cursors->right_side_trim);
2834 set_canvas_cursor (_cursors->right_side_trim_left_only);
2839 /** Obtain the pointer position in world coordinates */
2841 Editor::get_pointer_position (double& x, double& y) const
2844 track_canvas->get_pointer (px, py);
2845 track_canvas->window_to_world (px, py, x, y);