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 (join_object_range_button.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 (mouse_mode != MouseRange || _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
600 set_selected_regionview_from_click (press, op, true);
602 selection->clear_tracks ();
603 set_selected_track_as_side_effect (op, true);
605 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT && !selection->regions.empty()) {
606 clicked_selection = select_range_around_region (selection->regions.front());
610 case RegionViewNameHighlight:
612 case LeftFrameHandle:
613 case RightFrameHandle:
614 if (mouse_mode != MouseRange || _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
615 set_selected_regionview_from_click (press, op, true);
616 } else if (event->type == GDK_BUTTON_PRESS) {
617 set_selected_track_as_side_effect (op);
622 case FadeInHandleItem:
624 case FadeOutHandleItem:
626 if (mouse_mode != MouseRange || _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
627 set_selected_regionview_from_click (press, op, true);
628 } else if (event->type == GDK_BUTTON_PRESS) {
629 set_selected_track_as_side_effect (op);
633 case ControlPointItem:
634 set_selected_track_as_side_effect (op, true);
635 if (mouse_mode != MouseRange || _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
636 set_selected_control_point_from_click (op, false);
641 /* for context click, select track */
642 if (event->button.button == 3) {
643 selection->clear_tracks ();
644 set_selected_track_as_side_effect (op, true);
648 case AutomationTrackItem:
649 set_selected_track_as_side_effect (op, true);
658 Editor::button_press_handler_1 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
660 /* single mouse clicks on any of these item types operate
661 independent of mouse mode, mostly because they are
662 not on the main track canvas or because we want
667 case PlayheadCursorItem:
668 _drags->set (new CursorDrag (this, item, true), event);
672 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
673 hide_marker (item, event);
675 _drags->set (new MarkerDrag (this, item), event);
679 case TempoMarkerItem:
681 TempoMarker* m = reinterpret_cast<TempoMarker*> (item->get_data ("marker"));
683 if (m->tempo().movable ()) {
685 new TempoMarkerDrag (
688 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
698 case MeterMarkerItem:
700 MeterMarker* m = reinterpret_cast<MeterMarker*> (item->get_data ("marker"));
702 if (m->meter().movable ()) {
704 new MeterMarkerDrag (
707 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
720 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
721 _drags->set (new CursorDrag (this, &playhead_cursor->canvas_item, false), event);
727 case RangeMarkerBarItem:
728 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
729 _drags->set (new CursorDrag (this, &playhead_cursor->canvas_item, false), event);
731 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateRangeMarker), event);
736 case CdMarkerBarItem:
737 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
738 _drags->set (new CursorDrag (this, &playhead_cursor->canvas_item, false), event);
740 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateCDMarker), event);
745 case TransportMarkerBarItem:
746 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
747 _drags->set (new CursorDrag (this, &playhead_cursor->canvas_item, false), event);
749 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateTransportMarker), event);
758 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
759 /* special case: allow trim of range selections in joined object mode;
760 in theory eff should equal MouseRange in this case, but it doesn't
761 because entering the range selection canvas item results in entered_regionview
762 being set to 0, so update_join_object_range_location acts as if we aren't
765 if (item_type == StartSelectionTrimItem) {
766 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
767 } else if (item_type == EndSelectionTrimItem) {
768 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
772 Editing::MouseMode eff = effective_mouse_mode ();
774 /* special case: allow drag of region fade in/out in object mode with join object/range enabled */
775 if (item_type == FadeInHandleItem || item_type == FadeOutHandleItem) {
782 case StartSelectionTrimItem:
783 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
786 case EndSelectionTrimItem:
787 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
791 if (Keyboard::modifier_state_contains
792 (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier))) {
793 // contains and not equals because I can't use alt as a modifier alone.
794 start_selection_grab (item, event);
795 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
796 /* grab selection for moving */
797 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionMove), event);
799 double const y = event->button.y + vertical_adjustment.get_value() - canvas_timebars_vsize;
800 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
802 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
803 if (join_object_range_button.get_active() && atv) {
804 /* smart "join" mode: drag automation */
805 _drags->set (new AutomationRangeDrag (this, atv->base_item(), selection->time), event, _cursors->up_down);
807 /* this was debated, but decided the more common action was to
808 make a new selection */
809 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
816 if (internal_editing()) {
817 /* trim notes if we're in internal edit mode and near the ends of the note */
818 ArdourCanvas::CanvasNote* cn = dynamic_cast<ArdourCanvas::CanvasNote*> (item);
819 if (cn && cn->big_enough_to_trim() && cn->mouse_near_ends()) {
820 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
822 _drags->set (new NoteDrag (this, item), event);
828 if (internal_editing()) {
829 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
830 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
834 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
839 case RegionViewNameHighlight:
840 if (!clicked_regionview->region()->locked()) {
841 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
842 _drags->set (new TrimDrag (this, item, clicked_regionview, s.by_layer()), event);
847 case LeftFrameHandle:
848 case RightFrameHandle:
849 if (!internal_editing() && !clicked_regionview->region()->locked()) {
850 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
851 _drags->set (new TrimDrag (this, item, clicked_regionview, s.by_layer()), event);
857 if (!internal_editing()) {
858 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
867 if (internal_editing()) {
868 ArdourCanvas::CanvasNoteEvent* cn = dynamic_cast<ArdourCanvas::CanvasNoteEvent*> (item);
869 if (cn->mouse_near_ends()) {
870 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
872 _drags->set (new NoteDrag (this, item), event);
882 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)) &&
883 event->type == GDK_BUTTON_PRESS) {
885 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
887 } else if (event->type == GDK_BUTTON_PRESS) {
890 case FadeInHandleItem:
892 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
893 _drags->set (new FadeInDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), s), event, _cursors->fade_in);
897 case FadeOutHandleItem:
899 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
900 _drags->set (new FadeOutDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), s), event, _cursors->fade_out);
904 case FeatureLineItem:
906 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier)) {
907 remove_transient(item);
911 _drags->set (new FeatureLineDrag (this, item), event);
917 if (dynamic_cast<AutomationRegionView*> (clicked_regionview)) {
918 /* click on an automation region view; do nothing here and let the ARV's signal handler
924 if (internal_editing ()) {
925 if (event->type == GDK_2BUTTON_PRESS && event->button.button == 1) {
926 Glib::RefPtr<Action> act = ActionManager::get_action (X_("MouseMode"), X_("toggle-internal-edit"));
932 /* click on a normal region view */
933 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
934 add_region_copy_drag (item, event, clicked_regionview);
935 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
936 add_region_brush_drag (item, event, clicked_regionview);
938 add_region_drag (item, event, clicked_regionview);
941 if (!internal_editing() && (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT && !selection->regions.empty())) {
942 _drags->add (new SelectionDrag (this, clicked_axisview->get_selection_rect (clicked_selection)->rect, SelectionDrag::SelectionMove));
945 _drags->start_grab (event);
948 case RegionViewNameHighlight:
949 case LeftFrameHandle:
950 case RightFrameHandle:
951 if (!clicked_regionview->region()->locked()) {
952 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
953 _drags->set (new TrimDrag (this, item, clicked_regionview, s.by_layer()), event);
960 /* rename happens on edit clicks */
961 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
962 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, s.by_layer()), event);
967 case ControlPointItem:
968 _drags->set (new ControlPointDrag (this, item), event);
972 case AutomationLineItem:
973 _drags->set (new LineDrag (this, item), event);
978 if (internal_editing()) {
979 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
980 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
984 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
988 case AutomationTrackItem:
990 TimeAxisView* parent = clicked_axisview->get_parent ();
991 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (clicked_axisview);
993 if (parent && dynamic_cast<MidiTimeAxisView*> (parent) && atv->show_regions ()) {
994 /* create a MIDI region so that we have somewhere to put automation */
995 _drags->set (new RegionCreateDrag (this, item, parent), event);
997 /* rubberband drag to select automation points */
998 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
1005 if (join_object_range_button.get_active()) {
1006 /* we're in "smart" joined mode, and we've clicked on a Selection */
1007 double const y = event->button.y + vertical_adjustment.get_value() - canvas_timebars_vsize;
1008 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
1010 /* if we're over an automation track, start a drag of its data */
1011 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
1013 _drags->set (new AutomationRangeDrag (this, atv->base_item(), selection->time), event, _cursors->up_down);
1016 /* if we're over a track and a region, and in the `object' part of a region,
1017 put a selection around the region and drag both
1019 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1020 if (rtv && _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
1021 boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track> (rtv->route ());
1023 boost::shared_ptr<Playlist> pl = t->playlist ();
1026 boost::shared_ptr<Region> r = pl->top_region_at (event_frame (event));
1028 RegionView* rv = rtv->view()->find_view (r);
1029 clicked_selection = select_range_around_region (rv);
1030 _drags->add (new SelectionDrag (this, item, SelectionDrag::SelectionMove));
1031 list<RegionView*> rvs;
1033 _drags->add (new RegionMoveDrag (this, item, rv, rvs, false, false));
1034 _drags->start_grab (event);
1045 case ImageFrameHandleStartItem:
1046 imageframe_start_handle_op(item, event) ;
1049 case ImageFrameHandleEndItem:
1050 imageframe_end_handle_op(item, event) ;
1053 case MarkerViewHandleStartItem:
1054 markerview_item_start_handle_op(item, event) ;
1057 case MarkerViewHandleEndItem:
1058 markerview_item_end_handle_op(item, event) ;
1061 case MarkerViewItem:
1062 start_markerview_grab(item, event) ;
1064 case ImageFrameItem:
1065 start_imageframe_grab(item, event) ;
1081 switch (item_type) {
1083 /* start a grab so that if we finish after moving
1084 we can tell what happened.
1086 _drags->set (new RegionGainDrag (this, item), event, current_canvas_cursor);
1090 _drags->set (new LineDrag (this, item), event);
1093 case ControlPointItem:
1094 _drags->set (new ControlPointDrag (this, item), event);
1104 switch (item_type) {
1105 case ControlPointItem:
1106 _drags->set (new ControlPointDrag (this, item), event);
1109 case AutomationLineItem:
1110 _drags->set (new LineDrag (this, item), event);
1114 // XXX need automation mode to identify which
1116 // start_line_grab_from_regionview (item, event);
1126 if (event->type == GDK_BUTTON_PRESS) {
1127 _drags->set (new MouseZoomDrag (this, item), event);
1134 if (internal_editing() && item_type == NoteItem) {
1135 /* drag notes if we're in internal edit mode */
1136 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
1138 } else if ((!internal_editing() || dynamic_cast<AudioRegionView*> (clicked_regionview)) && clicked_regionview) {
1139 /* do time-FX if we're not in internal edit mode, or we are but we clicked on an audio region */
1140 _drags->set (new TimeFXDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1146 _drags->set (new ScrubDrag (this, item), event);
1147 scrub_reversals = 0;
1148 scrub_reverse_distance = 0;
1149 last_scrub_x = event->button.x;
1150 scrubbing_direction = 0;
1151 set_canvas_cursor (_cursors->transparent);
1163 Editor::button_press_handler_2 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1165 Editing::MouseMode const eff = effective_mouse_mode ();
1168 switch (item_type) {
1170 if (internal_editing ()) {
1171 /* no region drags in internal edit mode */
1175 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
1176 add_region_copy_drag (item, event, clicked_regionview);
1178 add_region_drag (item, event, clicked_regionview);
1180 _drags->start_grab (event);
1183 case ControlPointItem:
1184 _drags->set (new ControlPointDrag (this, item), event);
1192 switch (item_type) {
1193 case RegionViewNameHighlight:
1194 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1198 case LeftFrameHandle:
1199 case RightFrameHandle:
1200 if (!internal_editing ()) {
1201 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1206 case RegionViewName:
1207 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer()), event);
1221 /* relax till release */
1227 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1228 temporal_zoom_to_frame (false, event_frame (event));
1230 temporal_zoom_to_frame (true, event_frame(event));
1243 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1245 if (event->type != GDK_BUTTON_PRESS) {
1249 Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->track_canvas->get_window();
1251 if (canvas_window) {
1252 Glib::RefPtr<const Gdk::Window> pointer_window;
1255 Gdk::ModifierType mask;
1257 pointer_window = canvas_window->get_pointer (x, y, mask);
1259 if (pointer_window == track_canvas->get_bin_window()) {
1260 track_canvas->window_to_world (x, y, wx, wy);
1264 pre_press_cursor = current_canvas_cursor;
1266 track_canvas->grab_focus();
1268 if (_session && _session->actively_recording()) {
1274 if (internal_editing()) {
1275 bool leave_internal_edit_mode = false;
1277 switch (item_type) {
1282 if (!dynamic_cast<MidiRegionView*> (clicked_regionview)) {
1283 leave_internal_edit_mode = true;
1287 case PlayheadCursorItem:
1289 case TempoMarkerItem:
1290 case MeterMarkerItem:
1294 case RangeMarkerBarItem:
1295 case CdMarkerBarItem:
1296 case TransportMarkerBarItem:
1297 /* button press on these events never does anything to
1298 change the editing mode.
1303 leave_internal_edit_mode = true;
1310 if (leave_internal_edit_mode) {
1311 ActionManager::do_action ("MouseMode", "toggle-internal-edit");
1315 button_selection (item, event, item_type);
1317 if (!_drags->active () &&
1318 (Keyboard::is_delete_event (&event->button) ||
1319 Keyboard::is_context_menu_event (&event->button) ||
1320 Keyboard::is_edit_event (&event->button))) {
1322 /* handled by button release */
1326 switch (event->button.button) {
1328 return button_press_handler_1 (item, event, item_type);
1332 return button_press_handler_2 (item, event, item_type);
1339 return button_press_dispatch (&event->button);
1348 Editor::button_press_dispatch (GdkEventButton* ev)
1350 /* this function is intended only for buttons 4 and above.
1353 Gtkmm2ext::MouseButton b (ev->state, ev->button);
1354 return button_bindings->activate (b, Gtkmm2ext::Bindings::Press);
1358 Editor::button_release_dispatch (GdkEventButton* ev)
1360 /* this function is intended only for buttons 4 and above.
1363 Gtkmm2ext::MouseButton b (ev->state, ev->button);
1364 return button_bindings->activate (b, Gtkmm2ext::Bindings::Release);
1368 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1370 framepos_t where = event_frame (event, 0, 0);
1371 AutomationTimeAxisView* atv = 0;
1373 if (pre_press_cursor) {
1374 set_canvas_cursor (pre_press_cursor);
1375 pre_press_cursor = 0;
1378 /* no action if we're recording */
1380 if (_session && _session->actively_recording()) {
1384 /* see if we're finishing a drag */
1386 bool were_dragging = false;
1387 if (_drags->active ()) {
1388 bool const r = _drags->end_grab (event);
1390 /* grab dragged, so do nothing else */
1394 were_dragging = true;
1397 update_region_layering_order_editor ();
1399 /* edit events get handled here */
1401 if (!_drags->active () && Keyboard::is_edit_event (&event->button)) {
1402 switch (item_type) {
1404 show_region_properties ();
1407 case TempoMarkerItem:
1408 edit_tempo_marker (item);
1411 case MeterMarkerItem:
1412 edit_meter_marker (item);
1415 case RegionViewName:
1416 if (clicked_regionview->name_active()) {
1417 return mouse_rename_region (item, event);
1421 case ControlPointItem:
1422 edit_control_point (item);
1435 /* context menu events get handled here */
1437 if (Keyboard::is_context_menu_event (&event->button)) {
1439 context_click_event = *event;
1441 if (!_drags->active ()) {
1443 /* no matter which button pops up the context menu, tell the menu
1444 widget to use button 1 to drive menu selection.
1447 switch (item_type) {
1449 case FadeInHandleItem:
1451 case FadeOutHandleItem:
1452 popup_fade_context_menu (1, event->button.time, item, item_type);
1456 popup_track_context_menu (1, event->button.time, item_type, false);
1460 case RegionViewNameHighlight:
1461 case LeftFrameHandle:
1462 case RightFrameHandle:
1463 case RegionViewName:
1464 popup_track_context_menu (1, event->button.time, item_type, false);
1468 popup_track_context_menu (1, event->button.time, item_type, true);
1471 case AutomationTrackItem:
1472 popup_track_context_menu (1, event->button.time, item_type, false);
1476 case RangeMarkerBarItem:
1477 case TransportMarkerBarItem:
1478 case CdMarkerBarItem:
1481 popup_ruler_menu (where, item_type);
1485 marker_context_menu (&event->button, item);
1488 case TempoMarkerItem:
1489 tempo_or_meter_marker_context_menu (&event->button, item);
1492 case MeterMarkerItem:
1493 tempo_or_meter_marker_context_menu (&event->button, item);
1496 case CrossfadeViewItem:
1497 popup_track_context_menu (1, event->button.time, item_type, false);
1500 case ControlPointItem:
1501 popup_control_point_context_menu (item, event);
1505 case ImageFrameItem:
1506 popup_imageframe_edit_menu(1, event->button.time, item, true) ;
1508 case ImageFrameTimeAxisItem:
1509 popup_imageframe_edit_menu(1, event->button.time, item, false) ;
1511 case MarkerViewItem:
1512 popup_marker_time_axis_edit_menu(1, event->button.time, item, true) ;
1514 case MarkerTimeAxisItem:
1515 popup_marker_time_axis_edit_menu(1, event->button.time, item, false) ;
1527 /* delete events get handled here */
1529 Editing::MouseMode const eff = effective_mouse_mode ();
1531 if (!_drags->active () && Keyboard::is_delete_event (&event->button)) {
1533 switch (item_type) {
1534 case TempoMarkerItem:
1535 remove_tempo_marker (item);
1538 case MeterMarkerItem:
1539 remove_meter_marker (item);
1543 remove_marker (*item, event);
1547 if (eff == MouseObject) {
1548 remove_clicked_region ();
1552 case ControlPointItem:
1553 remove_control_point (item);
1557 remove_midi_note (item, event);
1566 switch (event->button.button) {
1569 switch (item_type) {
1570 /* see comments in button_press_handler */
1571 case PlayheadCursorItem:
1574 case AutomationLineItem:
1575 case StartSelectionTrimItem:
1576 case EndSelectionTrimItem:
1580 if (!_dragging_playhead) {
1581 snap_to_with_modifier (where, event, 0, true);
1582 mouse_add_new_marker (where);
1586 case CdMarkerBarItem:
1587 if (!_dragging_playhead) {
1588 // if we get here then a dragged range wasn't done
1589 snap_to_with_modifier (where, event, 0, true);
1590 mouse_add_new_marker (where, true);
1595 if (!_dragging_playhead) {
1596 snap_to_with_modifier (where, event);
1597 mouse_add_new_tempo_event (where);
1602 if (!_dragging_playhead) {
1603 mouse_add_new_meter_event (pixel_to_frame (event->button.x));
1614 switch (item_type) {
1615 case AutomationTrackItem:
1616 atv = dynamic_cast<AutomationTimeAxisView*>(clicked_axisview);
1618 atv->add_automation_event (event, where, event->button.y);
1629 switch (item_type) {
1632 /* check that we didn't drag before releasing, since
1633 its really annoying to create new control
1634 points when doing this.
1636 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (clicked_regionview);
1637 if (were_dragging && arv) {
1638 arv->add_gain_point_event (item, event);
1644 case AutomationTrackItem:
1645 dynamic_cast<AutomationTimeAxisView*>(clicked_axisview)->
1646 add_automation_event (event, where, event->button.y);
1655 set_canvas_cursor (current_canvas_cursor);
1656 if (scrubbing_direction == 0) {
1657 /* no drag, just a click */
1658 switch (item_type) {
1660 play_selected_region ();
1666 /* make sure we stop */
1667 _session->request_transport_speed (0.0);
1676 /* do any (de)selection operations that should occur on button release */
1677 button_selection (item, event, item_type);
1686 switch (item_type) {
1688 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1690 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier|Keyboard::SecondaryModifier))) {
1693 // Button2 click is unused
1708 // x_style_paste (where, 1.0);
1729 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1736 switch (item_type) {
1737 case ControlPointItem:
1738 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1739 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1740 cp->set_visible (true);
1744 at_y = cp->get_y ();
1745 cp->i2w (at_x, at_y);
1749 fraction = 1.0 - (cp->get_y() / cp->line().height());
1751 if (is_drawable() && !_drags->active ()) {
1752 set_canvas_cursor (_cursors->fader);
1755 _verbose_cursor->set (cp->line().get_verbose_cursor_string (fraction), at_x, at_y);
1756 _verbose_cursor->show ();
1761 if (mouse_mode == MouseGain) {
1762 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1764 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredGainLine.get();
1765 if (is_drawable()) {
1766 set_canvas_cursor (_cursors->fader);
1771 case AutomationLineItem:
1772 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1774 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1776 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredAutomationLine.get();
1778 if (is_drawable()) {
1779 set_canvas_cursor (_cursors->fader);
1784 case RegionViewNameHighlight:
1785 if (is_drawable() && mouse_mode == MouseObject && entered_regionview) {
1786 set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1787 _over_region_trim_target = true;
1791 case LeftFrameHandle:
1792 case RightFrameHandle:
1793 if (is_drawable() && mouse_mode == MouseObject && !internal_editing() && entered_regionview) {
1794 set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1798 case StartSelectionTrimItem:
1800 case ImageFrameHandleStartItem:
1801 case MarkerViewHandleStartItem:
1803 if (is_drawable()) {
1804 set_canvas_cursor (_cursors->left_side_trim);
1807 case EndSelectionTrimItem:
1809 case ImageFrameHandleEndItem:
1810 case MarkerViewHandleEndItem:
1812 if (is_drawable()) {
1813 set_canvas_cursor (_cursors->right_side_trim);
1817 case PlayheadCursorItem:
1818 if (is_drawable()) {
1819 switch (_edit_point) {
1821 set_canvas_cursor (_cursors->grabber_edit_point);
1824 set_canvas_cursor (_cursors->grabber);
1830 case RegionViewName:
1832 /* when the name is not an active item, the entire name highlight is for trimming */
1834 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1835 if (mouse_mode == MouseObject && is_drawable()) {
1836 set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1837 _over_region_trim_target = true;
1843 case AutomationTrackItem:
1844 if (is_drawable()) {
1845 Gdk::Cursor *cursor;
1846 switch (mouse_mode) {
1848 cursor = _cursors->selector;
1851 cursor = _cursors->zoom_in;
1854 cursor = _cursors->cross_hair;
1858 set_canvas_cursor (cursor);
1860 AutomationTimeAxisView* atv;
1861 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1862 clear_entered_track = false;
1863 set_entered_track (atv);
1869 case RangeMarkerBarItem:
1870 case TransportMarkerBarItem:
1871 case CdMarkerBarItem:
1874 if (is_drawable()) {
1875 set_canvas_cursor (_cursors->timebar);
1880 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1883 entered_marker = marker;
1884 marker->set_color_rgba (ARDOUR_UI::config()->canvasvar_EnteredMarker.get());
1886 case MeterMarkerItem:
1887 case TempoMarkerItem:
1888 if (is_drawable()) {
1889 set_canvas_cursor (_cursors->timebar);
1893 case FadeInHandleItem:
1894 if (mouse_mode == MouseObject && !internal_editing()) {
1895 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1897 rect->property_fill_color_rgba() = 0xBBBBBBAA;
1899 set_canvas_cursor (_cursors->fade_in);
1903 case FadeOutHandleItem:
1904 if (mouse_mode == MouseObject && !internal_editing()) {
1905 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1907 rect->property_fill_color_rgba() = 0xBBBBBBAA;
1909 set_canvas_cursor (_cursors->fade_out);
1912 case FeatureLineItem:
1914 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1915 line->property_fill_color_rgba() = 0xFF0000FF;
1919 if (join_object_range_button.get_active()) {
1920 set_canvas_cursor ();
1928 /* second pass to handle entered track status in a comprehensible way.
1931 switch (item_type) {
1933 case AutomationLineItem:
1934 case ControlPointItem:
1935 /* these do not affect the current entered track state */
1936 clear_entered_track = false;
1939 case AutomationTrackItem:
1940 /* handled above already */
1944 set_entered_track (0);
1952 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent*, ItemType item_type)
1962 switch (item_type) {
1963 case ControlPointItem:
1964 cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
1965 if (cp->line().the_list()->interpolation() != AutomationList::Discrete) {
1966 if (cp->line().npoints() > 1 && !cp->get_selected()) {
1967 cp->set_visible (false);
1971 if (is_drawable()) {
1972 set_canvas_cursor (current_canvas_cursor);
1975 _verbose_cursor->hide ();
1978 case RegionViewNameHighlight:
1979 case LeftFrameHandle:
1980 case RightFrameHandle:
1981 case StartSelectionTrimItem:
1982 case EndSelectionTrimItem:
1983 case PlayheadCursorItem:
1986 case ImageFrameHandleStartItem:
1987 case ImageFrameHandleEndItem:
1988 case MarkerViewHandleStartItem:
1989 case MarkerViewHandleEndItem:
1992 _over_region_trim_target = false;
1994 if (is_drawable()) {
1995 set_canvas_cursor (current_canvas_cursor);
2000 case AutomationLineItem:
2001 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
2003 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
2005 line->property_fill_color_rgba() = al->get_line_color();
2007 if (is_drawable()) {
2008 set_canvas_cursor (current_canvas_cursor);
2012 case RegionViewName:
2013 /* see enter_handler() for notes */
2014 _over_region_trim_target = false;
2016 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
2017 if (is_drawable() && mouse_mode == MouseObject) {
2018 set_canvas_cursor (current_canvas_cursor);
2023 case RangeMarkerBarItem:
2024 case TransportMarkerBarItem:
2025 case CdMarkerBarItem:
2029 if (is_drawable()) {
2030 set_canvas_cursor (current_canvas_cursor);
2035 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2039 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
2040 location_flags_changed (loc, this);
2043 case MeterMarkerItem:
2044 case TempoMarkerItem:
2046 if (is_drawable()) {
2047 set_canvas_cursor (_cursors->timebar);
2052 case FadeInHandleItem:
2053 case FadeOutHandleItem:
2054 rv = static_cast<RegionView*>(item->get_data ("regionview"));
2056 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
2058 rect->property_fill_color_rgba() = rv->get_fill_color();
2059 rect->property_outline_pixels() = 0;
2062 set_canvas_cursor (current_canvas_cursor);
2065 case AutomationTrackItem:
2066 if (is_drawable()) {
2067 set_canvas_cursor (current_canvas_cursor);
2068 clear_entered_track = true;
2069 Glib::signal_idle().connect (sigc::mem_fun(*this, &Editor::left_automation_track));
2072 case FeatureLineItem:
2074 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
2075 line->property_fill_color_rgba() = (guint) ARDOUR_UI::config()->canvasvar_ZeroLine.get();;
2087 Editor::left_automation_track ()
2089 if (clear_entered_track) {
2090 set_entered_track (0);
2091 clear_entered_track = false;
2097 Editor::scrub (framepos_t frame, double current_x)
2101 if (scrubbing_direction == 0) {
2103 _session->request_locate (frame, false);
2104 _session->request_transport_speed (0.1);
2105 scrubbing_direction = 1;
2109 if (last_scrub_x > current_x) {
2111 /* pointer moved to the left */
2113 if (scrubbing_direction > 0) {
2115 /* we reversed direction to go backwards */
2118 scrub_reverse_distance += (int) (last_scrub_x - current_x);
2122 /* still moving to the left (backwards) */
2124 scrub_reversals = 0;
2125 scrub_reverse_distance = 0;
2127 delta = 0.01 * (last_scrub_x - current_x);
2128 _session->request_transport_speed_nonzero (_session->transport_speed() - delta);
2132 /* pointer moved to the right */
2134 if (scrubbing_direction < 0) {
2135 /* we reversed direction to go forward */
2138 scrub_reverse_distance += (int) (current_x - last_scrub_x);
2141 /* still moving to the right */
2143 scrub_reversals = 0;
2144 scrub_reverse_distance = 0;
2146 delta = 0.01 * (current_x - last_scrub_x);
2147 _session->request_transport_speed_nonzero (_session->transport_speed() + delta);
2151 /* if there have been more than 2 opposite motion moves detected, or one that moves
2152 back more than 10 pixels, reverse direction
2155 if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
2157 if (scrubbing_direction > 0) {
2158 /* was forwards, go backwards */
2159 _session->request_transport_speed (-0.1);
2160 scrubbing_direction = -1;
2162 /* was backwards, go forwards */
2163 _session->request_transport_speed (0.1);
2164 scrubbing_direction = 1;
2167 scrub_reverse_distance = 0;
2168 scrub_reversals = 0;
2172 last_scrub_x = current_x;
2176 Editor::motion_handler (ArdourCanvas::Item* /*item*/, GdkEvent* event, bool from_autoscroll)
2178 _last_motion_y = event->motion.y;
2180 if (event->motion.is_hint) {
2183 /* We call this so that MOTION_NOTIFY events continue to be
2184 delivered to the canvas. We need to do this because we set
2185 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
2186 the density of the events, at the expense of a round-trip
2187 to the server. Given that this will mostly occur on cases
2188 where DISPLAY = :0.0, and given the cost of what the motion
2189 event might do, its a good tradeoff.
2192 track_canvas->get_pointer (x, y);
2195 if (current_stepping_trackview) {
2196 /* don't keep the persistent stepped trackview if the mouse moves */
2197 current_stepping_trackview = 0;
2198 step_timeout.disconnect ();
2201 if (_session && _session->actively_recording()) {
2202 /* Sorry. no dragging stuff around while we record */
2206 JoinObjectRangeState const old = _join_object_range_state;
2207 update_join_object_range_location (event->motion.x, event->motion.y);
2208 if (_join_object_range_state != old) {
2209 set_canvas_cursor ();
2212 if (_over_region_trim_target) {
2213 set_canvas_cursor_for_region_view (event->motion.x, entered_regionview);
2216 bool handled = false;
2217 if (_drags->active ()) {
2218 handled = _drags->motion_handler (event, from_autoscroll);
2225 track_canvas_motion (event);
2230 Editor::can_remove_control_point (ArdourCanvas::Item* item)
2232 ControlPoint* control_point;
2234 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2235 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2239 AutomationLine& line = control_point->line ();
2240 if (dynamic_cast<AudioRegionGainLine*> (&line)) {
2241 /* we shouldn't remove the first or last gain point in region gain lines */
2242 if (line.is_last_point(*control_point) || line.is_first_point(*control_point)) {
2251 Editor::remove_control_point (ArdourCanvas::Item* item)
2253 if (!can_remove_control_point (item)) {
2257 ControlPoint* control_point;
2259 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2260 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2264 control_point->line().remove_point (*control_point);
2268 Editor::edit_control_point (ArdourCanvas::Item* item)
2270 ControlPoint* p = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"));
2273 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2277 ControlPointDialog d (p);
2278 d.set_position (Gtk::WIN_POS_MOUSE);
2281 if (d.run () != RESPONSE_ACCEPT) {
2285 p->line().modify_point_y (*p, d.get_y_fraction ());
2289 Editor::edit_note (ArdourCanvas::Item* item)
2291 ArdourCanvas::CanvasNoteEvent* e = dynamic_cast<ArdourCanvas::CanvasNoteEvent*> (item);
2294 EditNoteDialog d (&e->region_view(), e);
2295 d.set_position (Gtk::WIN_POS_MOUSE);
2303 Editor::visible_order_range (int* low, int* high) const
2305 *low = TimeAxisView::max_order ();
2308 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
2310 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
2312 if (!rtv->hidden()) {
2314 if (*high < rtv->order()) {
2315 *high = rtv->order ();
2318 if (*low > rtv->order()) {
2319 *low = rtv->order ();
2326 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
2328 /* Either add to or set the set the region selection, unless
2329 this is an alignment click (control used)
2332 if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
2333 TimeAxisView* tv = &rv.get_time_axis_view();
2334 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
2336 if (rtv && rtv->is_track()) {
2337 speed = rtv->track()->speed();
2340 framepos_t where = get_preferred_edit_position();
2344 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
2346 align_region (rv.region(), SyncPoint, (framepos_t) (where * speed));
2348 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
2350 align_region (rv.region(), End, (framepos_t) (where * speed));
2354 align_region (rv.region(), Start, (framepos_t) (where * speed));
2361 Editor::collect_new_region_view (RegionView* rv)
2363 latest_regionviews.push_back (rv);
2367 Editor::collect_and_select_new_region_view (RegionView* rv)
2370 latest_regionviews.push_back (rv);
2374 Editor::cancel_selection ()
2376 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2377 (*i)->hide_selection ();
2380 selection->clear ();
2381 clicked_selection = 0;
2386 Editor::point_trim (GdkEvent* event, framepos_t new_bound)
2388 RegionView* rv = clicked_regionview;
2390 /* Choose action dependant on which button was pressed */
2391 switch (event->button.button) {
2393 begin_reversible_command (_("start point trim"));
2395 if (selection->selected (rv)) {
2396 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
2397 i != selection->regions.by_layer().end(); ++i)
2400 cerr << "region view contains null region" << endl;
2403 if (!(*i)->region()->locked()) {
2404 (*i)->region()->clear_changes ();
2405 (*i)->region()->trim_front (new_bound);
2406 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2411 if (!rv->region()->locked()) {
2412 rv->region()->clear_changes ();
2413 rv->region()->trim_front (new_bound);
2414 _session->add_command(new StatefulDiffCommand (rv->region()));
2418 commit_reversible_command();
2422 begin_reversible_command (_("End point trim"));
2424 if (selection->selected (rv)) {
2426 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
2428 if (!(*i)->region()->locked()) {
2429 (*i)->region()->clear_changes();
2430 (*i)->region()->trim_end (new_bound);
2431 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2437 if (!rv->region()->locked()) {
2438 rv->region()->clear_changes ();
2439 rv->region()->trim_end (new_bound);
2440 _session->add_command (new StatefulDiffCommand (rv->region()));
2444 commit_reversible_command();
2453 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* /*event*/)
2458 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2459 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2463 Location* location = find_location_from_marker (marker, is_start);
2464 location->set_hidden (true, this);
2469 Editor::reposition_zoom_rect (framepos_t start, framepos_t end)
2471 double x1 = frame_to_pixel (start);
2472 double x2 = frame_to_pixel (end);
2473 double y2 = full_canvas_height - 1.0;
2475 zoom_rect->property_x1() = x1;
2476 zoom_rect->property_y1() = 1.0;
2477 zoom_rect->property_x2() = x2;
2478 zoom_rect->property_y2() = y2;
2483 Editor::mouse_rename_region (ArdourCanvas::Item* /*item*/, GdkEvent* /*event*/)
2485 using namespace Gtkmm2ext;
2487 ArdourPrompter prompter (false);
2489 prompter.set_prompt (_("Name for region:"));
2490 prompter.set_initial_text (clicked_regionview->region()->name());
2491 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
2492 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
2493 prompter.show_all ();
2494 switch (prompter.run ()) {
2495 case Gtk::RESPONSE_ACCEPT:
2497 prompter.get_result(str);
2499 clicked_regionview->region()->set_name (str);
2508 Editor::mouse_brush_insert_region (RegionView* rv, framepos_t pos)
2510 /* no brushing without a useful snap setting */
2512 switch (_snap_mode) {
2514 return; /* can't work because it allows region to be placed anywhere */
2519 switch (_snap_type) {
2527 /* don't brush a copy over the original */
2529 if (pos == rv->region()->position()) {
2533 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
2535 if (rtv == 0 || !rtv->is_track()) {
2539 boost::shared_ptr<Playlist> playlist = rtv->playlist();
2540 double speed = rtv->track()->speed();
2542 playlist->clear_changes ();
2543 boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region(), true));
2544 playlist->add_region (new_region, (framepos_t) (pos * speed));
2545 _session->add_command (new StatefulDiffCommand (playlist));
2547 // playlist is frozen, so we have to update manually XXX this is disgusting
2549 playlist->RegionAdded (new_region); /* EMIT SIGNAL */
2553 Editor::track_height_step_timeout ()
2555 if (get_microseconds() - last_track_height_step_timestamp < 250000) {
2556 current_stepping_trackview = 0;
2563 Editor::add_region_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2565 assert (region_view);
2567 if (!region_view->region()->playlist()) {
2571 _region_motion_group->raise_to_top ();
2573 if (Config->get_edit_mode() == Splice) {
2574 _drags->add (new RegionSpliceDrag (this, item, region_view, selection->regions.by_layer()));
2576 RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id);
2577 _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), false, false));
2580 /* sync the canvas to what we think is its current state */
2581 update_canvas_now();
2585 Editor::add_region_copy_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2587 assert (region_view);
2589 if (!region_view->region()->playlist()) {
2593 _region_motion_group->raise_to_top ();
2595 RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id);
2596 _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), false, true));
2600 Editor::add_region_brush_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2602 assert (region_view);
2604 if (!region_view->region()->playlist()) {
2608 if (Config->get_edit_mode() == Splice) {
2612 RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id);
2613 _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), true, false));
2615 begin_reversible_command (Operations::drag_region_brush);
2618 /** Start a grab where a time range is selected, track(s) are selected, and the
2619 * user clicks and drags a region with a modifier in order to create a new region containing
2620 * the section of the clicked region that lies within the time range.
2623 Editor::start_selection_grab (ArdourCanvas::Item* /*item*/, GdkEvent* event)
2625 if (clicked_regionview == 0) {
2629 /* lets try to create new Region for the selection */
2631 vector<boost::shared_ptr<Region> > new_regions;
2632 create_region_from_selection (new_regions);
2634 if (new_regions.empty()) {
2638 /* XXX fix me one day to use all new regions */
2640 boost::shared_ptr<Region> region (new_regions.front());
2642 /* add it to the current stream/playlist.
2644 tricky: the streamview for the track will add a new regionview. we will
2645 catch the signal it sends when it creates the regionview to
2646 set the regionview we want to then drag.
2649 latest_regionviews.clear();
2650 sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
2652 /* A selection grab currently creates two undo/redo operations, one for
2653 creating the new region and another for moving it.
2656 begin_reversible_command (Operations::selection_grab);
2658 boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
2660 playlist->clear_changes ();
2661 clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
2662 _session->add_command(new StatefulDiffCommand (playlist));
2664 commit_reversible_command ();
2668 if (latest_regionviews.empty()) {
2669 /* something went wrong */
2673 /* we need to deselect all other regionviews, and select this one
2674 i'm ignoring undo stuff, because the region creation will take care of it
2676 selection->set (latest_regionviews);
2678 _drags->set (new RegionMoveDrag (this, latest_regionviews.front()->get_canvas_group(), latest_regionviews.front(), latest_regionviews, false, false), event);
2684 if (_drags->active ()) {
2687 selection->clear ();
2692 Editor::set_internal_edit (bool yn)
2694 _internal_editing = yn;
2697 pre_internal_mouse_mode = mouse_mode;
2699 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2700 (*i)->enter_internal_edit_mode ();
2704 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2705 (*i)->leave_internal_edit_mode ();
2708 if (mouse_mode == MouseDraw && pre_internal_mouse_mode != MouseDraw) {
2709 /* we were drawing .. flip back to something sensible */
2710 set_mouse_mode (pre_internal_mouse_mode);
2714 set_canvas_cursor ();
2717 /** Update _join_object_range_state which indicate whether we are over the top or bottom half of a region view,
2718 * used by the `join object/range' tool mode.
2721 Editor::update_join_object_range_location (double /*x*/, double y)
2723 /* XXX: actually, this decides based on whether the mouse is in the top or bottom half of a RouteTimeAxisView;
2724 entered_{track,regionview} is not always setup (e.g. if the mouse is over a TimeSelection), and to get a Region
2725 that we're over requires searching the playlist.
2728 if (join_object_range_button.get_active() == false || (mouse_mode != MouseRange && mouse_mode != MouseObject)) {
2729 _join_object_range_state = JOIN_OBJECT_RANGE_NONE;
2733 if (mouse_mode == MouseObject) {
2734 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2735 } else if (mouse_mode == MouseRange) {
2736 _join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
2739 /* XXX: maybe we should make entered_track work in all cases, rather than resorting to this */
2740 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y + vertical_adjustment.get_value() - canvas_timebars_vsize);
2744 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
2749 rtv->canvas_display()->w2i (cx, cy);
2751 double const c = cy / rtv->view()->child_height();
2753 double const f = modf (c, &d);
2755 _join_object_range_state = f < 0.5 ? JOIN_OBJECT_RANGE_RANGE : JOIN_OBJECT_RANGE_OBJECT;
2761 Editor::effective_mouse_mode () const
2763 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
2765 } else if (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE) {
2773 Editor::remove_midi_note (ArdourCanvas::Item* item, GdkEvent *)
2775 ArdourCanvas::CanvasNoteEvent* e = dynamic_cast<ArdourCanvas::CanvasNoteEvent*> (item);
2778 e->region_view().delete_note (e->note ());
2782 Editor::set_canvas_cursor_for_region_view (double x, RegionView* rv)
2786 ArdourCanvas::Group* g = rv->get_canvas_group ();
2787 ArdourCanvas::Group* p = g->get_parent_group ();
2789 /* Compute x in region view parent coordinates */
2793 double x1, x2, y1, y2;
2794 g->get_bounds (x1, y1, x2, y2);
2796 /* Halfway across the region */
2797 double const h = (x1 + x2) / 2;
2799 Trimmable::CanTrim ct = rv->region()->can_trim ();
2801 if (ct & Trimmable::FrontTrimEarlier) {
2802 set_canvas_cursor (_cursors->left_side_trim);
2804 set_canvas_cursor (_cursors->left_side_trim_right_only);
2807 if (ct & Trimmable::EndTrimLater) {
2808 set_canvas_cursor (_cursors->right_side_trim);
2810 set_canvas_cursor (_cursors->right_side_trim_left_only);
2815 /** Obtain the pointer position in world coordinates */
2817 Editor::get_pointer_position (double& x, double& y) const
2820 track_canvas->get_pointer (px, py);
2821 track_canvas->window_to_world (px, py, x, y);