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
96 Gdk::ModifierType mask;
97 Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->track_canvas->get_window();
98 Glib::RefPtr<const Gdk::Window> pointer_window;
100 if (!canvas_window) {
104 pointer_window = canvas_window->get_pointer (x, y, mask);
106 if (pointer_window == track_canvas->get_bin_window()) {
109 in_track_canvas = true;
112 in_track_canvas = false;
117 event.type = GDK_BUTTON_RELEASE;
121 where = event_frame (&event, 0, 0);
126 Editor::event_frame (GdkEvent const * event, double* pcx, double* pcy) const
140 switch (event->type) {
141 case GDK_BUTTON_RELEASE:
142 case GDK_BUTTON_PRESS:
143 case GDK_2BUTTON_PRESS:
144 case GDK_3BUTTON_PRESS:
145 *pcx = event->button.x;
146 *pcy = event->button.y;
147 _trackview_group->w2i(*pcx, *pcy);
149 case GDK_MOTION_NOTIFY:
150 *pcx = event->motion.x;
151 *pcy = event->motion.y;
152 _trackview_group->w2i(*pcx, *pcy);
154 case GDK_ENTER_NOTIFY:
155 case GDK_LEAVE_NOTIFY:
156 track_canvas->w2c(event->crossing.x, event->crossing.y, *pcx, *pcy);
159 case GDK_KEY_RELEASE:
160 // track_canvas->w2c(event->key.x, event->key.y, *pcx, *pcy);
163 warning << string_compose (_("Editor::event_frame() used on unhandled event type %1"), event->type) << endmsg;
167 /* note that pixel_to_frame() never returns less than zero, so even if the pixel
168 position is negative (as can be the case with motion events in particular),
169 the frame location is always positive.
172 return pixel_to_frame (*pcx);
176 Editor::which_grabber_cursor ()
178 Gdk::Cursor* c = _cursors->grabber;
180 if (_internal_editing) {
181 switch (mouse_mode) {
183 c = _cursors->midi_pencil;
187 c = _cursors->grabber_note;
191 c = _cursors->midi_resize;
200 switch (_edit_point) {
202 c = _cursors->grabber_edit_point;
205 boost::shared_ptr<Movable> m = _movable.lock();
206 if (m && m->locked()) {
207 c = _cursors->speaker;
217 Editor::set_current_trimmable (boost::shared_ptr<Trimmable> t)
219 boost::shared_ptr<Trimmable> st = _trimmable.lock();
221 if (!st || st == t) {
223 set_canvas_cursor ();
228 Editor::set_current_movable (boost::shared_ptr<Movable> m)
230 boost::shared_ptr<Movable> sm = _movable.lock();
232 if (!sm || sm != m) {
234 set_canvas_cursor ();
239 Editor::set_canvas_cursor ()
241 if (_internal_editing) {
243 switch (mouse_mode) {
245 current_canvas_cursor = _cursors->midi_pencil;
249 current_canvas_cursor = which_grabber_cursor();
253 current_canvas_cursor = _cursors->midi_resize;
262 switch (mouse_mode) {
264 current_canvas_cursor = _cursors->selector;
268 current_canvas_cursor = which_grabber_cursor();
272 /* shouldn't be possible, but just cover it anyway ... */
273 current_canvas_cursor = _cursors->midi_pencil;
277 current_canvas_cursor = _cursors->cross_hair;
281 if (Keyboard::the_keyboard().key_is_down (GDK_Control_L)) {
282 current_canvas_cursor = _cursors->zoom_out;
284 current_canvas_cursor = _cursors->zoom_in;
289 current_canvas_cursor = _cursors->time_fx; // just use playhead
293 current_canvas_cursor = _cursors->speaker;
298 switch (_join_object_range_state) {
299 case JOIN_OBJECT_RANGE_NONE:
301 case JOIN_OBJECT_RANGE_OBJECT:
302 current_canvas_cursor = which_grabber_cursor ();
304 case JOIN_OBJECT_RANGE_RANGE:
305 current_canvas_cursor = _cursors->selector;
309 /* up-down cursor as a cue that automation can be dragged up and down when in join object/range mode */
310 if (smart_mode_action->get_active()) {
312 get_pointer_position (x, y);
313 ArdourCanvas::Item* i = track_canvas->get_item_at (x, y);
314 if (i && i->property_parent() && (*i->property_parent()).get_data (X_("timeselection"))) {
315 pair<TimeAxisView*, int> tvp = trackview_by_y_position (_last_motion_y + vertical_adjustment.get_value() - canvas_timebars_vsize);
316 if (dynamic_cast<AutomationTimeAxisView*> (tvp.first)) {
317 current_canvas_cursor = _cursors->up_down;
322 set_canvas_cursor (current_canvas_cursor, true);
326 Editor::set_mouse_mode (MouseMode m, bool force)
328 if (_drags->active ()) {
332 if (!force && m == mouse_mode) {
336 Glib::RefPtr<Action> act;
340 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-range"));
344 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-object"));
348 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-draw"));
352 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-gain"));
356 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-zoom"));
360 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-timefx"));
364 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-audition"));
370 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
373 /* go there and back to ensure that the toggled handler is called to set up mouse_mode */
374 tact->set_active (false);
375 tact->set_active (true);
377 MouseModeChanged (); /* EMIT SIGNAL */
381 Editor::mouse_mode_toggled (MouseMode m)
383 Glib::RefPtr<Action> act;
384 Glib::RefPtr<ToggleAction> tact;
388 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-range"));
392 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-object"));
396 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-draw"));
400 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-gain"));
404 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-zoom"));
408 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-timefx"));
412 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-audition"));
418 tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
421 if (!tact->get_active()) {
422 /* this was just the notification that the old mode has been
423 * left. we'll get called again with the new mode active in a
431 act = ActionManager::get_action (X_("MouseMode"), X_("toggle-internal-edit"));
432 tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
433 tact->set_active (true);
443 if (!internal_editing()) {
444 if (mouse_mode != MouseRange && _join_object_range_state == JOIN_OBJECT_RANGE_NONE) {
446 /* in all modes except range and joined object/range, hide the range selection,
447 show the object (region) selection.
450 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
451 (*i)->hide_selection ();
457 in range or object/range mode, show the range selection.
460 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
461 (*i)->show_selection (selection->time);
466 set_canvas_cursor ();
468 MouseModeChanged (); /* EMIT SIGNAL */
472 Editor::step_mouse_mode (bool next)
474 switch (current_mouse_mode()) {
477 if (Profile->get_sae()) {
478 set_mouse_mode (MouseZoom);
480 set_mouse_mode (MouseRange);
483 set_mouse_mode (MouseTimeFX);
488 if (next) set_mouse_mode (MouseDraw);
489 else set_mouse_mode (MouseObject);
493 if (next) set_mouse_mode (MouseZoom);
494 else set_mouse_mode (MouseRange);
499 if (Profile->get_sae()) {
500 set_mouse_mode (MouseTimeFX);
502 set_mouse_mode (MouseGain);
505 if (Profile->get_sae()) {
506 set_mouse_mode (MouseObject);
508 set_mouse_mode (MouseDraw);
514 if (next) set_mouse_mode (MouseTimeFX);
515 else set_mouse_mode (MouseZoom);
520 set_mouse_mode (MouseAudition);
522 if (Profile->get_sae()) {
523 set_mouse_mode (MouseZoom);
525 set_mouse_mode (MouseGain);
531 if (next) set_mouse_mode (MouseObject);
532 else set_mouse_mode (MouseTimeFX);
538 Editor::toggle_internal_editing_from_double_click (GdkEvent* event)
540 if (_drags->active()) {
541 _drags->end_grab (event);
544 ActionManager::do_action ("MouseMode", "toggle-internal-edit");
546 /* prevent reversion of edit cursor on button release */
548 pre_press_cursor = 0;
554 Editor::button_selection (ArdourCanvas::Item* /*item*/, GdkEvent* event, ItemType item_type)
556 /* in object/audition/timefx/gain-automation mode,
557 any button press sets the selection if the object
558 can be selected. this is a bit of hack, because
559 we want to avoid this if the mouse operation is a
562 note: not dbl-click or triple-click
564 Also note that there is no region selection in internal edit mode, otherwise
565 for operations operating on the selection (e.g. cut) it is not obvious whether
566 to cut notes or regions.
569 if (((mouse_mode != MouseObject) &&
570 (_join_object_range_state != JOIN_OBJECT_RANGE_OBJECT) &&
571 (mouse_mode != MouseAudition || item_type != RegionItem) &&
572 (mouse_mode != MouseTimeFX || item_type != RegionItem) &&
573 (mouse_mode != MouseGain) &&
574 (mouse_mode != MouseRange) &&
575 (mouse_mode != MouseDraw)) ||
576 ((event->type != GDK_BUTTON_PRESS && event->type != GDK_BUTTON_RELEASE) || event->button.button > 3) ||
577 internal_editing()) {
582 if (event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) {
584 if ((event->button.state & Keyboard::RelevantModifierKeyMask) && event->button.button != 1) {
586 /* almost no selection action on modified button-2 or button-3 events */
588 if (item_type != RegionItem && event->button.button != 2) {
594 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
595 bool press = (event->type == GDK_BUTTON_PRESS);
599 if (!doing_range_stuff()) {
600 set_selected_regionview_from_click (press, op);
604 if (doing_range_stuff()) {
605 /* don't change the selection unless the
606 clicked track is not currently selected. if
607 so, "collapse" the selection to just this
610 if (!selection->selected (clicked_axisview)) {
611 set_selected_track_as_side_effect (Selection::Set);
617 case RegionViewNameHighlight:
619 case LeftFrameHandle:
620 case RightFrameHandle:
621 if (doing_object_stuff() || (mouse_mode != MouseRange && mouse_mode != MouseObject)) {
622 set_selected_regionview_from_click (press, op);
623 } else if (event->type == GDK_BUTTON_PRESS) {
624 set_selected_track_as_side_effect (op);
629 case FadeInHandleItem:
631 case FadeOutHandleItem:
633 if (doing_object_stuff() || (mouse_mode != MouseRange && mouse_mode != MouseObject)) {
634 set_selected_regionview_from_click (press, op);
635 } else if (event->type == GDK_BUTTON_PRESS) {
636 set_selected_track_as_side_effect (op);
640 case ControlPointItem:
641 set_selected_track_as_side_effect (op);
642 if (doing_object_stuff() || (mouse_mode != MouseRange && mouse_mode != MouseObject)) {
643 set_selected_control_point_from_click (op, false);
648 /* for context click, select track */
649 if (event->button.button == 3) {
650 selection->clear_tracks ();
651 set_selected_track_as_side_effect (op);
655 case AutomationTrackItem:
656 set_selected_track_as_side_effect (op);
665 Editor::button_press_handler_1 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
667 /* single mouse clicks on any of these item types operate
668 independent of mouse mode, mostly because they are
669 not on the main track canvas or because we want
674 case PlayheadCursorItem:
675 _drags->set (new CursorDrag (this, item, true), event);
679 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
680 hide_marker (item, event);
682 _drags->set (new MarkerDrag (this, item), event);
686 case TempoMarkerItem:
688 TempoMarker* m = reinterpret_cast<TempoMarker*> (item->get_data ("marker"));
690 if (m->tempo().movable ()) {
692 new TempoMarkerDrag (
695 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
705 case MeterMarkerItem:
707 MeterMarker* m = reinterpret_cast<MeterMarker*> (item->get_data ("marker"));
709 if (m->meter().movable ()) {
711 new MeterMarkerDrag (
714 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
727 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
728 _drags->set (new CursorDrag (this, &playhead_cursor->canvas_item, false), event);
734 case RangeMarkerBarItem:
735 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
736 _drags->set (new CursorDrag (this, &playhead_cursor->canvas_item, false), event);
738 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateRangeMarker), event);
743 case CdMarkerBarItem:
744 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
745 _drags->set (new CursorDrag (this, &playhead_cursor->canvas_item, false), event);
747 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateCDMarker), event);
752 case TransportMarkerBarItem:
753 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
754 _drags->set (new CursorDrag (this, &playhead_cursor->canvas_item, false), event);
756 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateTransportMarker), event);
765 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
766 /* special case: allow trim of range selections in joined object mode;
767 in theory eff should equal MouseRange in this case, but it doesn't
768 because entering the range selection canvas item results in entered_regionview
769 being set to 0, so update_join_object_range_location acts as if we aren't
772 if (item_type == StartSelectionTrimItem) {
773 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
774 } else if (item_type == EndSelectionTrimItem) {
775 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
779 Editing::MouseMode eff = effective_mouse_mode ();
781 /* special case: allow drag of region fade in/out in object mode with join object/range enabled */
782 if (item_type == FadeInHandleItem || item_type == FadeOutHandleItem) {
789 case StartSelectionTrimItem:
790 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
793 case EndSelectionTrimItem:
794 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
798 if (Keyboard::modifier_state_contains
799 (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier))) {
800 // contains and not equals because I can't use alt as a modifier alone.
801 start_selection_grab (item, event);
802 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
803 /* grab selection for moving */
804 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionMove), event);
806 double const y = event->button.y + vertical_adjustment.get_value() - canvas_timebars_vsize;
807 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
809 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
810 if (smart_mode_action->get_active() && atv) {
811 /* smart "join" mode: drag automation */
812 _drags->set (new AutomationRangeDrag (this, atv->base_item(), selection->time), event, _cursors->up_down);
814 /* this was debated, but decided the more common action was to
815 make a new selection */
816 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
823 if (internal_editing()) {
824 /* trim notes if we're in internal edit mode and near the ends of the note */
825 ArdourCanvas::CanvasNote* cn = dynamic_cast<ArdourCanvas::CanvasNote*> (item);
826 if (cn && cn->big_enough_to_trim() && cn->mouse_near_ends()) {
827 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
829 _drags->set (new NoteDrag (this, item), event);
835 if (internal_editing()) {
836 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
837 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
841 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
846 case RegionViewNameHighlight:
847 if (!clicked_regionview->region()->locked()) {
848 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
849 _drags->set (new TrimDrag (this, item, clicked_regionview, s.by_layer()), event);
854 case LeftFrameHandle:
855 case RightFrameHandle:
856 if (!internal_editing() && doing_object_stuff() && !clicked_regionview->region()->locked()) {
857 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
858 _drags->set (new TrimDrag (this, item, clicked_regionview, s.by_layer()), event);
864 if (!internal_editing()) {
865 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
874 if (internal_editing()) {
875 ArdourCanvas::CanvasNoteEvent* cn = dynamic_cast<ArdourCanvas::CanvasNoteEvent*> (item);
876 if (cn->mouse_near_ends()) {
877 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
879 _drags->set (new NoteDrag (this, item), event);
889 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)) &&
890 event->type == GDK_BUTTON_PRESS) {
892 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
894 } else if (event->type == GDK_BUTTON_PRESS) {
897 case FadeInHandleItem:
899 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
900 _drags->set (new FadeInDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), s), event, _cursors->fade_in);
904 case FadeOutHandleItem:
906 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
907 _drags->set (new FadeOutDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), s), event, _cursors->fade_out);
911 case FeatureLineItem:
913 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier)) {
914 remove_transient(item);
918 _drags->set (new FeatureLineDrag (this, item), event);
924 if (dynamic_cast<AutomationRegionView*> (clicked_regionview)) {
925 /* click on an automation region view; do nothing here and let the ARV's signal handler
931 if (internal_editing ()) {
932 if (event->type == GDK_2BUTTON_PRESS && event->button.button == 1) {
933 Glib::RefPtr<Action> act = ActionManager::get_action (X_("MouseMode"), X_("toggle-internal-edit"));
939 /* click on a normal region view */
940 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
941 add_region_copy_drag (item, event, clicked_regionview);
942 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
943 add_region_brush_drag (item, event, clicked_regionview);
945 add_region_drag (item, event, clicked_regionview);
949 if (!internal_editing() && (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE && !selection->regions.empty())) {
950 _drags->add (new SelectionDrag (this, clicked_axisview->get_selection_rect (clicked_selection)->rect, SelectionDrag::SelectionMove));
953 _drags->start_grab (event);
956 case RegionViewNameHighlight:
957 case LeftFrameHandle:
958 case RightFrameHandle:
959 if (!clicked_regionview->region()->locked()) {
960 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
961 _drags->set (new TrimDrag (this, item, clicked_regionview, s.by_layer()), event);
968 /* rename happens on edit clicks */
969 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
970 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, s.by_layer()), event);
975 case ControlPointItem:
976 _drags->set (new ControlPointDrag (this, item), event);
980 case AutomationLineItem:
981 _drags->set (new LineDrag (this, item), event);
986 if (internal_editing()) {
987 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
988 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
992 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
996 case AutomationTrackItem:
998 TimeAxisView* parent = clicked_axisview->get_parent ();
999 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (clicked_axisview);
1001 if (parent && dynamic_cast<MidiTimeAxisView*> (parent) && atv->show_regions ()) {
1002 /* create a MIDI region so that we have somewhere to put automation */
1003 _drags->set (new RegionCreateDrag (this, item, parent), event);
1005 /* rubberband drag to select automation points */
1006 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
1013 if (smart_mode_action->get_active()) {
1014 /* we're in "smart" joined mode, and we've clicked on a Selection */
1015 double const y = event->button.y + vertical_adjustment.get_value() - canvas_timebars_vsize;
1016 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
1018 /* if we're over an automation track, start a drag of its data */
1019 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
1021 _drags->set (new AutomationRangeDrag (this, atv->base_item(), selection->time), event, _cursors->up_down);
1024 /* if we're over a track and a region, and in the `object' part of a region,
1025 put a selection around the region and drag both
1027 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1028 if (rtv && _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
1029 boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track> (rtv->route ());
1031 boost::shared_ptr<Playlist> pl = t->playlist ();
1034 boost::shared_ptr<Region> r = pl->top_region_at (event_frame (event));
1036 RegionView* rv = rtv->view()->find_view (r);
1037 clicked_selection = select_range (rv->region()->position(),
1038 rv->region()->last_frame()+1);
1039 _drags->add (new SelectionDrag (this, item, SelectionDrag::SelectionMove));
1040 list<RegionView*> rvs;
1042 _drags->add (new RegionMoveDrag (this, item, rv, rvs, false, false));
1043 _drags->start_grab (event);
1054 case ImageFrameHandleStartItem:
1055 imageframe_start_handle_op(item, event) ;
1058 case ImageFrameHandleEndItem:
1059 imageframe_end_handle_op(item, event) ;
1062 case MarkerViewHandleStartItem:
1063 markerview_item_start_handle_op(item, event) ;
1066 case MarkerViewHandleEndItem:
1067 markerview_item_end_handle_op(item, event) ;
1070 case MarkerViewItem:
1071 start_markerview_grab(item, event) ;
1073 case ImageFrameItem:
1074 start_imageframe_grab(item, event) ;
1090 switch (item_type) {
1092 /* start a grab so that if we finish after moving
1093 we can tell what happened.
1095 _drags->set (new RegionGainDrag (this, item), event, current_canvas_cursor);
1099 _drags->set (new LineDrag (this, item), event);
1102 case ControlPointItem:
1103 _drags->set (new ControlPointDrag (this, item), event);
1113 switch (item_type) {
1114 case ControlPointItem:
1115 _drags->set (new ControlPointDrag (this, item), event);
1118 case AutomationLineItem:
1119 _drags->set (new LineDrag (this, item), event);
1123 // XXX need automation mode to identify which
1125 // start_line_grab_from_regionview (item, event);
1135 if (event->type == GDK_BUTTON_PRESS) {
1136 _drags->set (new MouseZoomDrag (this, item), event);
1143 if (internal_editing() && item_type == NoteItem) {
1144 /* drag notes if we're in internal edit mode */
1145 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
1147 } else if ((!internal_editing() || dynamic_cast<AudioRegionView*> (clicked_regionview)) && clicked_regionview) {
1148 /* do time-FX if we're not in internal edit mode, or we are but we clicked on an audio region */
1149 _drags->set (new TimeFXDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1155 _drags->set (new ScrubDrag (this, item), event);
1156 scrub_reversals = 0;
1157 scrub_reverse_distance = 0;
1158 last_scrub_x = event->button.x;
1159 scrubbing_direction = 0;
1160 set_canvas_cursor (_cursors->transparent);
1172 Editor::button_press_handler_2 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1174 Editing::MouseMode const eff = effective_mouse_mode ();
1177 switch (item_type) {
1179 if (internal_editing ()) {
1180 /* no region drags in internal edit mode */
1184 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
1185 add_region_copy_drag (item, event, clicked_regionview);
1187 add_region_drag (item, event, clicked_regionview);
1189 _drags->start_grab (event);
1192 case ControlPointItem:
1193 _drags->set (new ControlPointDrag (this, item), event);
1201 switch (item_type) {
1202 case RegionViewNameHighlight:
1203 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1207 case LeftFrameHandle:
1208 case RightFrameHandle:
1209 if (!internal_editing ()) {
1210 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1215 case RegionViewName:
1216 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer()), event);
1230 /* relax till release */
1236 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1237 temporal_zoom_to_frame (false, event_frame (event));
1239 temporal_zoom_to_frame (true, event_frame(event));
1252 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1254 if (event->type != GDK_BUTTON_PRESS) {
1258 Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->track_canvas->get_window();
1260 if (canvas_window) {
1261 Glib::RefPtr<const Gdk::Window> pointer_window;
1264 Gdk::ModifierType mask;
1266 pointer_window = canvas_window->get_pointer (x, y, mask);
1268 if (pointer_window == track_canvas->get_bin_window()) {
1269 track_canvas->window_to_world (x, y, wx, wy);
1273 pre_press_cursor = current_canvas_cursor;
1275 track_canvas->grab_focus();
1277 if (_session && _session->actively_recording()) {
1283 if (internal_editing()) {
1284 bool leave_internal_edit_mode = false;
1286 switch (item_type) {
1291 if (!dynamic_cast<MidiRegionView*> (clicked_regionview)) {
1292 leave_internal_edit_mode = true;
1296 case PlayheadCursorItem:
1298 case TempoMarkerItem:
1299 case MeterMarkerItem:
1303 case RangeMarkerBarItem:
1304 case CdMarkerBarItem:
1305 case TransportMarkerBarItem:
1306 /* button press on these events never does anything to
1307 change the editing mode.
1312 leave_internal_edit_mode = true;
1319 if (leave_internal_edit_mode) {
1320 ActionManager::do_action ("MouseMode", "toggle-internal-edit");
1324 button_selection (item, event, item_type);
1326 if (!_drags->active () &&
1327 (Keyboard::is_delete_event (&event->button) ||
1328 Keyboard::is_context_menu_event (&event->button) ||
1329 Keyboard::is_edit_event (&event->button))) {
1331 /* handled by button release */
1335 switch (event->button.button) {
1337 return button_press_handler_1 (item, event, item_type);
1341 return button_press_handler_2 (item, event, item_type);
1348 return button_press_dispatch (&event->button);
1357 Editor::button_press_dispatch (GdkEventButton* ev)
1359 /* this function is intended only for buttons 4 and above.
1362 Gtkmm2ext::MouseButton b (ev->state, ev->button);
1363 return button_bindings->activate (b, Gtkmm2ext::Bindings::Press);
1367 Editor::button_release_dispatch (GdkEventButton* ev)
1369 /* this function is intended only for buttons 4 and above.
1372 Gtkmm2ext::MouseButton b (ev->state, ev->button);
1373 return button_bindings->activate (b, Gtkmm2ext::Bindings::Release);
1377 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1379 framepos_t where = event_frame (event, 0, 0);
1380 AutomationTimeAxisView* atv = 0;
1382 if (pre_press_cursor) {
1383 set_canvas_cursor (pre_press_cursor);
1384 pre_press_cursor = 0;
1387 /* no action if we're recording */
1389 if (_session && _session->actively_recording()) {
1393 /* see if we're finishing a drag */
1395 bool were_dragging = false;
1396 if (_drags->active ()) {
1397 bool const r = _drags->end_grab (event);
1399 /* grab dragged, so do nothing else */
1403 were_dragging = true;
1406 update_region_layering_order_editor ();
1408 /* edit events get handled here */
1410 if (!_drags->active () && Keyboard::is_edit_event (&event->button)) {
1411 switch (item_type) {
1413 show_region_properties ();
1416 case TempoMarkerItem:
1417 edit_tempo_marker (item);
1420 case MeterMarkerItem:
1421 edit_meter_marker (item);
1424 case RegionViewName:
1425 if (clicked_regionview->name_active()) {
1426 return mouse_rename_region (item, event);
1430 case ControlPointItem:
1431 edit_control_point (item);
1444 /* context menu events get handled here */
1446 if (Keyboard::is_context_menu_event (&event->button)) {
1448 context_click_event = *event;
1450 if (!_drags->active ()) {
1452 /* no matter which button pops up the context menu, tell the menu
1453 widget to use button 1 to drive menu selection.
1456 switch (item_type) {
1458 case FadeInHandleItem:
1460 case FadeOutHandleItem:
1461 popup_fade_context_menu (1, event->button.time, item, item_type);
1465 popup_track_context_menu (1, event->button.time, item_type, false);
1469 case RegionViewNameHighlight:
1470 case LeftFrameHandle:
1471 case RightFrameHandle:
1472 case RegionViewName:
1473 popup_track_context_menu (1, event->button.time, item_type, false);
1477 popup_track_context_menu (1, event->button.time, item_type, true);
1480 case AutomationTrackItem:
1481 popup_track_context_menu (1, event->button.time, item_type, false);
1485 case RangeMarkerBarItem:
1486 case TransportMarkerBarItem:
1487 case CdMarkerBarItem:
1490 popup_ruler_menu (where, item_type);
1494 marker_context_menu (&event->button, item);
1497 case TempoMarkerItem:
1498 tempo_or_meter_marker_context_menu (&event->button, item);
1501 case MeterMarkerItem:
1502 tempo_or_meter_marker_context_menu (&event->button, item);
1505 case CrossfadeViewItem:
1506 popup_track_context_menu (1, event->button.time, item_type, false);
1509 case ControlPointItem:
1510 popup_control_point_context_menu (item, event);
1514 case ImageFrameItem:
1515 popup_imageframe_edit_menu(1, event->button.time, item, true) ;
1517 case ImageFrameTimeAxisItem:
1518 popup_imageframe_edit_menu(1, event->button.time, item, false) ;
1520 case MarkerViewItem:
1521 popup_marker_time_axis_edit_menu(1, event->button.time, item, true) ;
1523 case MarkerTimeAxisItem:
1524 popup_marker_time_axis_edit_menu(1, event->button.time, item, false) ;
1536 /* delete events get handled here */
1538 Editing::MouseMode const eff = effective_mouse_mode ();
1540 if (!_drags->active () && Keyboard::is_delete_event (&event->button)) {
1542 switch (item_type) {
1543 case TempoMarkerItem:
1544 remove_tempo_marker (item);
1547 case MeterMarkerItem:
1548 remove_meter_marker (item);
1552 remove_marker (*item, event);
1556 if (eff == MouseObject) {
1557 remove_clicked_region ();
1561 case ControlPointItem:
1562 remove_control_point (item);
1566 remove_midi_note (item, event);
1575 switch (event->button.button) {
1578 switch (item_type) {
1579 /* see comments in button_press_handler */
1580 case PlayheadCursorItem:
1583 case AutomationLineItem:
1584 case StartSelectionTrimItem:
1585 case EndSelectionTrimItem:
1589 if (!_dragging_playhead) {
1590 snap_to_with_modifier (where, event, 0, true);
1591 mouse_add_new_marker (where);
1595 case CdMarkerBarItem:
1596 if (!_dragging_playhead) {
1597 // if we get here then a dragged range wasn't done
1598 snap_to_with_modifier (where, event, 0, true);
1599 mouse_add_new_marker (where, true);
1604 if (!_dragging_playhead) {
1605 snap_to_with_modifier (where, event);
1606 mouse_add_new_tempo_event (where);
1611 if (!_dragging_playhead) {
1612 mouse_add_new_meter_event (pixel_to_frame (event->button.x));
1623 switch (item_type) {
1624 case AutomationTrackItem:
1625 atv = dynamic_cast<AutomationTimeAxisView*>(clicked_axisview);
1627 atv->add_automation_event (event, where, event->button.y);
1637 switch (item_type) {
1640 /* check that we didn't drag before releasing, since
1641 its really annoying to create new control
1642 points when doing this.
1644 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (clicked_regionview);
1645 if (were_dragging && arv) {
1646 arv->add_gain_point_event (item, event);
1652 case AutomationTrackItem:
1653 dynamic_cast<AutomationTimeAxisView*>(clicked_axisview)->
1654 add_automation_event (event, where, event->button.y);
1663 set_canvas_cursor (current_canvas_cursor);
1664 if (scrubbing_direction == 0) {
1665 /* no drag, just a click */
1666 switch (item_type) {
1668 play_selected_region ();
1674 /* make sure we stop */
1675 _session->request_transport_speed (0.0);
1684 /* do any (de)selection operations that should occur on button release */
1685 button_selection (item, event, item_type);
1694 switch (item_type) {
1696 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1698 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier|Keyboard::SecondaryModifier))) {
1701 // Button2 click is unused
1716 // x_style_paste (where, 1.0);
1737 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1744 switch (item_type) {
1745 case ControlPointItem:
1746 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1747 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1748 cp->set_visible (true);
1752 at_y = cp->get_y ();
1753 cp->i2w (at_x, at_y);
1757 fraction = 1.0 - (cp->get_y() / cp->line().height());
1759 if (is_drawable() && !_drags->active ()) {
1760 set_canvas_cursor (_cursors->fader);
1763 _verbose_cursor->set (cp->line().get_verbose_cursor_string (fraction), at_x, at_y);
1764 _verbose_cursor->show ();
1769 if (mouse_mode == MouseGain) {
1770 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1772 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredGainLine.get();
1773 if (is_drawable()) {
1774 set_canvas_cursor (_cursors->fader);
1779 case AutomationLineItem:
1780 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1781 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1783 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredAutomationLine.get();
1785 if (is_drawable()) {
1786 set_canvas_cursor (_cursors->fader);
1791 case RegionViewNameHighlight:
1792 if (is_drawable() && doing_object_stuff() && entered_regionview) {
1793 set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1794 _over_region_trim_target = true;
1798 case LeftFrameHandle:
1799 case RightFrameHandle:
1800 if (is_drawable() && doing_object_stuff() && !internal_editing() && entered_regionview) {
1801 set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1805 case StartSelectionTrimItem:
1807 case ImageFrameHandleStartItem:
1808 case MarkerViewHandleStartItem:
1810 if (is_drawable()) {
1811 set_canvas_cursor (_cursors->left_side_trim);
1814 case EndSelectionTrimItem:
1816 case ImageFrameHandleEndItem:
1817 case MarkerViewHandleEndItem:
1819 if (is_drawable()) {
1820 set_canvas_cursor (_cursors->right_side_trim);
1824 case PlayheadCursorItem:
1825 if (is_drawable()) {
1826 switch (_edit_point) {
1828 set_canvas_cursor (_cursors->grabber_edit_point);
1831 set_canvas_cursor (_cursors->grabber);
1837 case RegionViewName:
1839 /* when the name is not an active item, the entire name highlight is for trimming */
1841 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1842 if (mouse_mode == MouseObject && is_drawable()) {
1843 set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1844 _over_region_trim_target = true;
1850 case AutomationTrackItem:
1851 if (is_drawable()) {
1852 Gdk::Cursor *cursor;
1853 switch (mouse_mode) {
1855 cursor = _cursors->selector;
1858 cursor = _cursors->zoom_in;
1861 cursor = _cursors->cross_hair;
1865 set_canvas_cursor (cursor);
1867 AutomationTimeAxisView* atv;
1868 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1869 clear_entered_track = false;
1870 set_entered_track (atv);
1876 case RangeMarkerBarItem:
1877 case TransportMarkerBarItem:
1878 case CdMarkerBarItem:
1881 if (is_drawable()) {
1882 set_canvas_cursor (_cursors->timebar);
1887 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1890 entered_marker = marker;
1891 marker->set_color_rgba (ARDOUR_UI::config()->canvasvar_EnteredMarker.get());
1893 case MeterMarkerItem:
1894 case TempoMarkerItem:
1895 if (is_drawable()) {
1896 set_canvas_cursor (_cursors->timebar);
1900 case FadeInHandleItem:
1901 if (mouse_mode == MouseObject && !internal_editing()) {
1902 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1904 rect->property_fill_color_rgba() = 0xBBBBBBAA;
1906 set_canvas_cursor (_cursors->fade_in);
1910 case FadeOutHandleItem:
1911 if (mouse_mode == MouseObject && !internal_editing()) {
1912 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1914 rect->property_fill_color_rgba() = 0xBBBBBBAA;
1916 set_canvas_cursor (_cursors->fade_out);
1919 case FeatureLineItem:
1921 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1922 line->property_fill_color_rgba() = 0xFF0000FF;
1926 if (smart_mode_action->get_active()) {
1927 set_canvas_cursor ();
1935 /* second pass to handle entered track status in a comprehensible way.
1938 switch (item_type) {
1940 case AutomationLineItem:
1941 case ControlPointItem:
1942 /* these do not affect the current entered track state */
1943 clear_entered_track = false;
1946 case AutomationTrackItem:
1947 /* handled above already */
1951 set_entered_track (0);
1959 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent*, ItemType item_type)
1969 switch (item_type) {
1970 case ControlPointItem:
1971 cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
1972 if (cp->line().the_list()->interpolation() != AutomationList::Discrete) {
1973 if (cp->line().npoints() > 1 && !cp->get_selected()) {
1974 cp->set_visible (false);
1978 if (is_drawable()) {
1979 set_canvas_cursor (current_canvas_cursor);
1982 _verbose_cursor->hide ();
1985 case RegionViewNameHighlight:
1986 case LeftFrameHandle:
1987 case RightFrameHandle:
1988 case StartSelectionTrimItem:
1989 case EndSelectionTrimItem:
1990 case PlayheadCursorItem:
1993 case ImageFrameHandleStartItem:
1994 case ImageFrameHandleEndItem:
1995 case MarkerViewHandleStartItem:
1996 case MarkerViewHandleEndItem:
1999 _over_region_trim_target = false;
2001 if (is_drawable()) {
2002 set_canvas_cursor (current_canvas_cursor);
2007 case AutomationLineItem:
2008 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
2010 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
2012 line->property_fill_color_rgba() = al->get_line_color();
2014 if (is_drawable()) {
2015 set_canvas_cursor (current_canvas_cursor);
2019 case RegionViewName:
2020 /* see enter_handler() for notes */
2021 _over_region_trim_target = false;
2023 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
2024 if (is_drawable() && mouse_mode == MouseObject) {
2025 set_canvas_cursor (current_canvas_cursor);
2030 case RangeMarkerBarItem:
2031 case TransportMarkerBarItem:
2032 case CdMarkerBarItem:
2036 if (is_drawable()) {
2037 set_canvas_cursor (current_canvas_cursor);
2042 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2046 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
2047 location_flags_changed (loc, this);
2050 case MeterMarkerItem:
2051 case TempoMarkerItem:
2053 if (is_drawable()) {
2054 set_canvas_cursor (current_canvas_cursor);
2059 case FadeInHandleItem:
2060 case FadeOutHandleItem:
2061 rv = static_cast<RegionView*>(item->get_data ("regionview"));
2063 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
2065 rect->property_fill_color_rgba() = rv->get_fill_color();
2066 rect->property_outline_pixels() = 0;
2069 set_canvas_cursor (current_canvas_cursor);
2072 case AutomationTrackItem:
2073 if (is_drawable()) {
2074 set_canvas_cursor (current_canvas_cursor);
2075 clear_entered_track = true;
2076 Glib::signal_idle().connect (sigc::mem_fun(*this, &Editor::left_automation_track));
2079 case FeatureLineItem:
2081 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
2082 line->property_fill_color_rgba() = (guint) ARDOUR_UI::config()->canvasvar_ZeroLine.get();;
2094 Editor::left_automation_track ()
2096 if (clear_entered_track) {
2097 set_entered_track (0);
2098 clear_entered_track = false;
2104 Editor::scrub (framepos_t frame, double current_x)
2108 if (scrubbing_direction == 0) {
2110 _session->request_locate (frame, false);
2111 _session->request_transport_speed (0.1);
2112 scrubbing_direction = 1;
2116 if (last_scrub_x > current_x) {
2118 /* pointer moved to the left */
2120 if (scrubbing_direction > 0) {
2122 /* we reversed direction to go backwards */
2125 scrub_reverse_distance += (int) (last_scrub_x - current_x);
2129 /* still moving to the left (backwards) */
2131 scrub_reversals = 0;
2132 scrub_reverse_distance = 0;
2134 delta = 0.01 * (last_scrub_x - current_x);
2135 _session->request_transport_speed_nonzero (_session->transport_speed() - delta);
2139 /* pointer moved to the right */
2141 if (scrubbing_direction < 0) {
2142 /* we reversed direction to go forward */
2145 scrub_reverse_distance += (int) (current_x - last_scrub_x);
2148 /* still moving to the right */
2150 scrub_reversals = 0;
2151 scrub_reverse_distance = 0;
2153 delta = 0.01 * (current_x - last_scrub_x);
2154 _session->request_transport_speed_nonzero (_session->transport_speed() + delta);
2158 /* if there have been more than 2 opposite motion moves detected, or one that moves
2159 back more than 10 pixels, reverse direction
2162 if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
2164 if (scrubbing_direction > 0) {
2165 /* was forwards, go backwards */
2166 _session->request_transport_speed (-0.1);
2167 scrubbing_direction = -1;
2169 /* was backwards, go forwards */
2170 _session->request_transport_speed (0.1);
2171 scrubbing_direction = 1;
2174 scrub_reverse_distance = 0;
2175 scrub_reversals = 0;
2179 last_scrub_x = current_x;
2183 Editor::motion_handler (ArdourCanvas::Item* /*item*/, GdkEvent* event, bool from_autoscroll)
2185 _last_motion_y = event->motion.y;
2187 if (event->motion.is_hint) {
2190 /* We call this so that MOTION_NOTIFY events continue to be
2191 delivered to the canvas. We need to do this because we set
2192 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
2193 the density of the events, at the expense of a round-trip
2194 to the server. Given that this will mostly occur on cases
2195 where DISPLAY = :0.0, and given the cost of what the motion
2196 event might do, its a good tradeoff.
2199 track_canvas->get_pointer (x, y);
2202 if (current_stepping_trackview) {
2203 /* don't keep the persistent stepped trackview if the mouse moves */
2204 current_stepping_trackview = 0;
2205 step_timeout.disconnect ();
2208 if (_session && _session->actively_recording()) {
2209 /* Sorry. no dragging stuff around while we record */
2213 JoinObjectRangeState const old = _join_object_range_state;
2214 update_join_object_range_location (event->motion.x, event->motion.y);
2215 if (_join_object_range_state != old) {
2216 set_canvas_cursor ();
2219 if (_over_region_trim_target) {
2220 set_canvas_cursor_for_region_view (event->motion.x, entered_regionview);
2223 bool handled = false;
2224 if (_drags->active ()) {
2225 handled = _drags->motion_handler (event, from_autoscroll);
2232 track_canvas_motion (event);
2237 Editor::can_remove_control_point (ArdourCanvas::Item* item)
2239 ControlPoint* control_point;
2241 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2242 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2246 AutomationLine& line = control_point->line ();
2247 if (dynamic_cast<AudioRegionGainLine*> (&line)) {
2248 /* we shouldn't remove the first or last gain point in region gain lines */
2249 if (line.is_last_point(*control_point) || line.is_first_point(*control_point)) {
2258 Editor::remove_control_point (ArdourCanvas::Item* item)
2260 if (!can_remove_control_point (item)) {
2264 ControlPoint* control_point;
2266 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2267 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2271 control_point->line().remove_point (*control_point);
2275 Editor::edit_control_point (ArdourCanvas::Item* item)
2277 ControlPoint* p = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"));
2280 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2284 ControlPointDialog d (p);
2285 d.set_position (Gtk::WIN_POS_MOUSE);
2288 if (d.run () != RESPONSE_ACCEPT) {
2292 p->line().modify_point_y (*p, d.get_y_fraction ());
2296 Editor::edit_note (ArdourCanvas::Item* item)
2298 ArdourCanvas::CanvasNoteEvent* e = dynamic_cast<ArdourCanvas::CanvasNoteEvent*> (item);
2301 EditNoteDialog d (&e->region_view(), e);
2302 d.set_position (Gtk::WIN_POS_MOUSE);
2310 Editor::visible_order_range (int* low, int* high) const
2312 *low = TimeAxisView::max_order ();
2315 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
2317 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
2319 if (!rtv->hidden()) {
2321 if (*high < rtv->order()) {
2322 *high = rtv->order ();
2325 if (*low > rtv->order()) {
2326 *low = rtv->order ();
2333 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
2335 /* Either add to or set the set the region selection, unless
2336 this is an alignment click (control used)
2339 if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
2340 TimeAxisView* tv = &rv.get_time_axis_view();
2341 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
2343 if (rtv && rtv->is_track()) {
2344 speed = rtv->track()->speed();
2347 framepos_t where = get_preferred_edit_position();
2351 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
2353 align_region (rv.region(), SyncPoint, (framepos_t) (where * speed));
2355 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
2357 align_region (rv.region(), End, (framepos_t) (where * speed));
2361 align_region (rv.region(), Start, (framepos_t) (where * speed));
2368 Editor::collect_new_region_view (RegionView* rv)
2370 latest_regionviews.push_back (rv);
2374 Editor::collect_and_select_new_region_view (RegionView* rv)
2377 latest_regionviews.push_back (rv);
2381 Editor::cancel_selection ()
2383 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2384 (*i)->hide_selection ();
2387 selection->clear ();
2388 clicked_selection = 0;
2393 Editor::point_trim (GdkEvent* event, framepos_t new_bound)
2395 RegionView* rv = clicked_regionview;
2397 /* Choose action dependant on which button was pressed */
2398 switch (event->button.button) {
2400 begin_reversible_command (_("start point trim"));
2402 if (selection->selected (rv)) {
2403 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
2404 i != selection->regions.by_layer().end(); ++i)
2407 cerr << "region view contains null region" << endl;
2410 if (!(*i)->region()->locked()) {
2411 (*i)->region()->clear_changes ();
2412 (*i)->region()->trim_front (new_bound);
2413 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2418 if (!rv->region()->locked()) {
2419 rv->region()->clear_changes ();
2420 rv->region()->trim_front (new_bound);
2421 _session->add_command(new StatefulDiffCommand (rv->region()));
2425 commit_reversible_command();
2429 begin_reversible_command (_("End point trim"));
2431 if (selection->selected (rv)) {
2433 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
2435 if (!(*i)->region()->locked()) {
2436 (*i)->region()->clear_changes();
2437 (*i)->region()->trim_end (new_bound);
2438 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2444 if (!rv->region()->locked()) {
2445 rv->region()->clear_changes ();
2446 rv->region()->trim_end (new_bound);
2447 _session->add_command (new StatefulDiffCommand (rv->region()));
2451 commit_reversible_command();
2460 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* /*event*/)
2465 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2466 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2470 Location* location = find_location_from_marker (marker, is_start);
2471 location->set_hidden (true, this);
2476 Editor::reposition_zoom_rect (framepos_t start, framepos_t end)
2478 double x1 = frame_to_pixel (start);
2479 double x2 = frame_to_pixel (end);
2480 double y2 = full_canvas_height - 1.0;
2482 zoom_rect->property_x1() = x1;
2483 zoom_rect->property_y1() = 1.0;
2484 zoom_rect->property_x2() = x2;
2485 zoom_rect->property_y2() = y2;
2490 Editor::mouse_rename_region (ArdourCanvas::Item* /*item*/, GdkEvent* /*event*/)
2492 using namespace Gtkmm2ext;
2494 ArdourPrompter prompter (false);
2496 prompter.set_prompt (_("Name for region:"));
2497 prompter.set_initial_text (clicked_regionview->region()->name());
2498 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
2499 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
2500 prompter.show_all ();
2501 switch (prompter.run ()) {
2502 case Gtk::RESPONSE_ACCEPT:
2504 prompter.get_result(str);
2506 clicked_regionview->region()->set_name (str);
2515 Editor::mouse_brush_insert_region (RegionView* rv, framepos_t pos)
2517 /* no brushing without a useful snap setting */
2519 switch (_snap_mode) {
2521 return; /* can't work because it allows region to be placed anywhere */
2526 switch (_snap_type) {
2534 /* don't brush a copy over the original */
2536 if (pos == rv->region()->position()) {
2540 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
2542 if (rtv == 0 || !rtv->is_track()) {
2546 boost::shared_ptr<Playlist> playlist = rtv->playlist();
2547 double speed = rtv->track()->speed();
2549 playlist->clear_changes ();
2550 boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region(), true));
2551 playlist->add_region (new_region, (framepos_t) (pos * speed));
2552 _session->add_command (new StatefulDiffCommand (playlist));
2554 // playlist is frozen, so we have to update manually XXX this is disgusting
2556 playlist->RegionAdded (new_region); /* EMIT SIGNAL */
2560 Editor::track_height_step_timeout ()
2562 if (get_microseconds() - last_track_height_step_timestamp < 250000) {
2563 current_stepping_trackview = 0;
2570 Editor::add_region_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2572 assert (region_view);
2574 if (!region_view->region()->playlist()) {
2578 _region_motion_group->raise_to_top ();
2580 if (Config->get_edit_mode() == Splice) {
2581 _drags->add (new RegionSpliceDrag (this, item, region_view, selection->regions.by_layer()));
2583 RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id);
2584 _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), false, false));
2587 /* sync the canvas to what we think is its current state */
2588 update_canvas_now();
2592 Editor::add_region_copy_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2594 assert (region_view);
2596 if (!region_view->region()->playlist()) {
2600 _region_motion_group->raise_to_top ();
2602 RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id);
2603 _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), false, true));
2607 Editor::add_region_brush_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2609 assert (region_view);
2611 if (!region_view->region()->playlist()) {
2615 if (Config->get_edit_mode() == Splice) {
2619 RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id);
2620 _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), true, false));
2622 begin_reversible_command (Operations::drag_region_brush);
2625 /** Start a grab where a time range is selected, track(s) are selected, and the
2626 * user clicks and drags a region with a modifier in order to create a new region containing
2627 * the section of the clicked region that lies within the time range.
2630 Editor::start_selection_grab (ArdourCanvas::Item* /*item*/, GdkEvent* event)
2632 if (clicked_regionview == 0) {
2636 /* lets try to create new Region for the selection */
2638 vector<boost::shared_ptr<Region> > new_regions;
2639 create_region_from_selection (new_regions);
2641 if (new_regions.empty()) {
2645 /* XXX fix me one day to use all new regions */
2647 boost::shared_ptr<Region> region (new_regions.front());
2649 /* add it to the current stream/playlist.
2651 tricky: the streamview for the track will add a new regionview. we will
2652 catch the signal it sends when it creates the regionview to
2653 set the regionview we want to then drag.
2656 latest_regionviews.clear();
2657 sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
2659 /* A selection grab currently creates two undo/redo operations, one for
2660 creating the new region and another for moving it.
2663 begin_reversible_command (Operations::selection_grab);
2665 boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
2667 playlist->clear_changes ();
2668 clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
2669 _session->add_command(new StatefulDiffCommand (playlist));
2671 commit_reversible_command ();
2675 if (latest_regionviews.empty()) {
2676 /* something went wrong */
2680 /* we need to deselect all other regionviews, and select this one
2681 i'm ignoring undo stuff, because the region creation will take care of it
2683 selection->set (latest_regionviews);
2685 _drags->set (new RegionMoveDrag (this, latest_regionviews.front()->get_canvas_group(), latest_regionviews.front(), latest_regionviews, false, false), event);
2691 if (_drags->active ()) {
2694 selection->clear ();
2699 Editor::set_internal_edit (bool yn)
2701 _internal_editing = yn;
2704 pre_internal_mouse_mode = mouse_mode;
2706 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2707 (*i)->enter_internal_edit_mode ();
2711 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2712 (*i)->leave_internal_edit_mode ();
2715 if (mouse_mode == MouseDraw && pre_internal_mouse_mode != MouseDraw) {
2716 /* we were drawing .. flip back to something sensible */
2717 set_mouse_mode (pre_internal_mouse_mode);
2721 set_canvas_cursor ();
2724 /** Update _join_object_range_state which indicate whether we are over the top or bottom half of a region view,
2725 * used by the `join object/range' tool mode.
2728 Editor::update_join_object_range_location (double /*x*/, double y)
2730 /* XXX: actually, this decides based on whether the mouse is in the top
2731 or bottom half of a the waveform part RouteTimeAxisView;
2733 Note that entered_{track,regionview} is not always setup (e.g. if
2734 the mouse is over a TimeSelection), and to get a Region
2735 that we're over requires searching the playlist.
2738 if (!smart_mode_action->get_active() || (mouse_mode != MouseRange && mouse_mode != MouseObject)) {
2739 _join_object_range_state = JOIN_OBJECT_RANGE_NONE;
2743 if (mouse_mode == MouseObject) {
2744 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2745 } else if (mouse_mode == MouseRange) {
2746 _join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
2749 /* XXX: maybe we should make entered_track work in all cases, rather than resorting to this */
2750 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y + vertical_adjustment.get_value() - canvas_timebars_vsize);
2754 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
2759 rtv->canvas_display()->w2i (cx, cy);
2761 double const c = cy / (rtv->view()->child_height() - TimeAxisViewItem::NAME_HIGHLIGHT_SIZE);
2763 double const f = modf (c, &d);
2765 _join_object_range_state = f < 0.5 ? JOIN_OBJECT_RANGE_RANGE : JOIN_OBJECT_RANGE_OBJECT;
2771 Editor::effective_mouse_mode () const
2773 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
2775 } else if (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE) {
2783 Editor::remove_midi_note (ArdourCanvas::Item* item, GdkEvent *)
2785 ArdourCanvas::CanvasNoteEvent* e = dynamic_cast<ArdourCanvas::CanvasNoteEvent*> (item);
2788 e->region_view().delete_note (e->note ());
2792 Editor::set_canvas_cursor_for_region_view (double x, RegionView* rv)
2796 ArdourCanvas::Group* g = rv->get_canvas_group ();
2797 ArdourCanvas::Group* p = g->get_parent_group ();
2799 /* Compute x in region view parent coordinates */
2803 double x1, x2, y1, y2;
2804 g->get_bounds (x1, y1, x2, y2);
2806 /* Halfway across the region */
2807 double const h = (x1 + x2) / 2;
2809 Trimmable::CanTrim ct = rv->region()->can_trim ();
2811 if (ct & Trimmable::FrontTrimEarlier) {
2812 set_canvas_cursor (_cursors->left_side_trim);
2814 set_canvas_cursor (_cursors->left_side_trim_right_only);
2817 if (ct & Trimmable::EndTrimLater) {
2818 set_canvas_cursor (_cursors->right_side_trim);
2820 set_canvas_cursor (_cursors->right_side_trim_left_only);
2825 /** Obtain the pointer position in world coordinates */
2827 Editor::get_pointer_position (double& x, double& y) const
2830 track_canvas->get_pointer (px, py);
2831 track_canvas->window_to_world (px, py, x, y);