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 if (!dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
1304 leave_internal_edit_mode = true;
1312 if (leave_internal_edit_mode) {
1313 ActionManager::do_action ("MouseMode", "toggle-internal-edit");
1317 button_selection (item, event, item_type);
1319 if (!_drags->active () &&
1320 (Keyboard::is_delete_event (&event->button) ||
1321 Keyboard::is_context_menu_event (&event->button) ||
1322 Keyboard::is_edit_event (&event->button))) {
1324 /* handled by button release */
1328 switch (event->button.button) {
1330 return button_press_handler_1 (item, event, item_type);
1334 return button_press_handler_2 (item, event, item_type);
1341 return button_press_dispatch (&event->button);
1350 Editor::button_press_dispatch (GdkEventButton* ev)
1352 /* this function is intended only for buttons 4 and above.
1355 Gtkmm2ext::MouseButton b (ev->state, ev->button);
1356 return button_bindings->activate (b, Gtkmm2ext::Bindings::Press);
1360 Editor::button_release_dispatch (GdkEventButton* ev)
1362 /* this function is intended only for buttons 4 and above.
1365 Gtkmm2ext::MouseButton b (ev->state, ev->button);
1366 return button_bindings->activate (b, Gtkmm2ext::Bindings::Release);
1370 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1372 framepos_t where = event_frame (event, 0, 0);
1373 AutomationTimeAxisView* atv = 0;
1375 if (pre_press_cursor) {
1376 set_canvas_cursor (pre_press_cursor);
1377 pre_press_cursor = 0;
1380 /* no action if we're recording */
1382 if (_session && _session->actively_recording()) {
1386 /* see if we're finishing a drag */
1388 bool were_dragging = false;
1389 if (_drags->active ()) {
1390 bool const r = _drags->end_grab (event);
1392 /* grab dragged, so do nothing else */
1396 were_dragging = true;
1399 update_region_layering_order_editor ();
1401 /* edit events get handled here */
1403 if (!_drags->active () && Keyboard::is_edit_event (&event->button)) {
1404 switch (item_type) {
1406 show_region_properties ();
1409 case TempoMarkerItem:
1410 edit_tempo_marker (item);
1413 case MeterMarkerItem:
1414 edit_meter_marker (item);
1417 case RegionViewName:
1418 if (clicked_regionview->name_active()) {
1419 return mouse_rename_region (item, event);
1423 case ControlPointItem:
1424 edit_control_point (item);
1437 /* context menu events get handled here */
1439 if (Keyboard::is_context_menu_event (&event->button)) {
1441 context_click_event = *event;
1443 if (!_drags->active ()) {
1445 /* no matter which button pops up the context menu, tell the menu
1446 widget to use button 1 to drive menu selection.
1449 switch (item_type) {
1451 case FadeInHandleItem:
1453 case FadeOutHandleItem:
1454 popup_fade_context_menu (1, event->button.time, item, item_type);
1458 popup_track_context_menu (1, event->button.time, item_type, false);
1462 case RegionViewNameHighlight:
1463 case LeftFrameHandle:
1464 case RightFrameHandle:
1465 case RegionViewName:
1466 popup_track_context_menu (1, event->button.time, item_type, false);
1470 popup_track_context_menu (1, event->button.time, item_type, true);
1473 case AutomationTrackItem:
1474 popup_track_context_menu (1, event->button.time, item_type, false);
1478 case RangeMarkerBarItem:
1479 case TransportMarkerBarItem:
1480 case CdMarkerBarItem:
1483 popup_ruler_menu (where, item_type);
1487 marker_context_menu (&event->button, item);
1490 case TempoMarkerItem:
1491 tempo_or_meter_marker_context_menu (&event->button, item);
1494 case MeterMarkerItem:
1495 tempo_or_meter_marker_context_menu (&event->button, item);
1498 case CrossfadeViewItem:
1499 popup_track_context_menu (1, event->button.time, item_type, false);
1502 case ControlPointItem:
1503 popup_control_point_context_menu (item, event);
1507 case ImageFrameItem:
1508 popup_imageframe_edit_menu(1, event->button.time, item, true) ;
1510 case ImageFrameTimeAxisItem:
1511 popup_imageframe_edit_menu(1, event->button.time, item, false) ;
1513 case MarkerViewItem:
1514 popup_marker_time_axis_edit_menu(1, event->button.time, item, true) ;
1516 case MarkerTimeAxisItem:
1517 popup_marker_time_axis_edit_menu(1, event->button.time, item, false) ;
1529 /* delete events get handled here */
1531 Editing::MouseMode const eff = effective_mouse_mode ();
1533 if (!_drags->active () && Keyboard::is_delete_event (&event->button)) {
1535 switch (item_type) {
1536 case TempoMarkerItem:
1537 remove_tempo_marker (item);
1540 case MeterMarkerItem:
1541 remove_meter_marker (item);
1545 remove_marker (*item, event);
1549 if (eff == MouseObject) {
1550 remove_clicked_region ();
1554 case ControlPointItem:
1555 remove_control_point (item);
1559 remove_midi_note (item, event);
1568 switch (event->button.button) {
1571 switch (item_type) {
1572 /* see comments in button_press_handler */
1573 case PlayheadCursorItem:
1576 case AutomationLineItem:
1577 case StartSelectionTrimItem:
1578 case EndSelectionTrimItem:
1582 if (!_dragging_playhead) {
1583 snap_to_with_modifier (where, event, 0, true);
1584 mouse_add_new_marker (where);
1588 case CdMarkerBarItem:
1589 if (!_dragging_playhead) {
1590 // if we get here then a dragged range wasn't done
1591 snap_to_with_modifier (where, event, 0, true);
1592 mouse_add_new_marker (where, true);
1597 if (!_dragging_playhead) {
1598 snap_to_with_modifier (where, event);
1599 mouse_add_new_tempo_event (where);
1604 if (!_dragging_playhead) {
1605 mouse_add_new_meter_event (pixel_to_frame (event->button.x));
1616 switch (item_type) {
1617 case AutomationTrackItem:
1618 atv = dynamic_cast<AutomationTimeAxisView*>(clicked_axisview);
1620 atv->add_automation_event (event, where, event->button.y);
1631 switch (item_type) {
1634 /* check that we didn't drag before releasing, since
1635 its really annoying to create new control
1636 points when doing this.
1638 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (clicked_regionview);
1639 if (were_dragging && arv) {
1640 arv->add_gain_point_event (item, event);
1646 case AutomationTrackItem:
1647 dynamic_cast<AutomationTimeAxisView*>(clicked_axisview)->
1648 add_automation_event (event, where, event->button.y);
1657 set_canvas_cursor (current_canvas_cursor);
1658 if (scrubbing_direction == 0) {
1659 /* no drag, just a click */
1660 switch (item_type) {
1662 play_selected_region ();
1668 /* make sure we stop */
1669 _session->request_transport_speed (0.0);
1678 /* do any (de)selection operations that should occur on button release */
1679 button_selection (item, event, item_type);
1688 switch (item_type) {
1690 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1692 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier|Keyboard::SecondaryModifier))) {
1695 // Button2 click is unused
1710 // x_style_paste (where, 1.0);
1731 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1738 switch (item_type) {
1739 case ControlPointItem:
1740 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1741 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1742 cp->set_visible (true);
1746 at_y = cp->get_y ();
1747 cp->i2w (at_x, at_y);
1751 fraction = 1.0 - (cp->get_y() / cp->line().height());
1753 if (is_drawable() && !_drags->active ()) {
1754 set_canvas_cursor (_cursors->fader);
1757 _verbose_cursor->set (cp->line().get_verbose_cursor_string (fraction), at_x, at_y);
1758 _verbose_cursor->show ();
1763 if (mouse_mode == MouseGain) {
1764 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1766 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredGainLine.get();
1767 if (is_drawable()) {
1768 set_canvas_cursor (_cursors->fader);
1773 case AutomationLineItem:
1774 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1776 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1778 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredAutomationLine.get();
1780 if (is_drawable()) {
1781 set_canvas_cursor (_cursors->fader);
1786 case RegionViewNameHighlight:
1787 if (is_drawable() && mouse_mode == MouseObject && entered_regionview) {
1788 set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1789 _over_region_trim_target = true;
1793 case LeftFrameHandle:
1794 case RightFrameHandle:
1795 if (is_drawable() && mouse_mode == MouseObject && !internal_editing() && entered_regionview) {
1796 set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1800 case StartSelectionTrimItem:
1802 case ImageFrameHandleStartItem:
1803 case MarkerViewHandleStartItem:
1805 if (is_drawable()) {
1806 set_canvas_cursor (_cursors->left_side_trim);
1809 case EndSelectionTrimItem:
1811 case ImageFrameHandleEndItem:
1812 case MarkerViewHandleEndItem:
1814 if (is_drawable()) {
1815 set_canvas_cursor (_cursors->right_side_trim);
1819 case PlayheadCursorItem:
1820 if (is_drawable()) {
1821 switch (_edit_point) {
1823 set_canvas_cursor (_cursors->grabber_edit_point);
1826 set_canvas_cursor (_cursors->grabber);
1832 case RegionViewName:
1834 /* when the name is not an active item, the entire name highlight is for trimming */
1836 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1837 if (mouse_mode == MouseObject && is_drawable()) {
1838 set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1839 _over_region_trim_target = true;
1845 case AutomationTrackItem:
1846 if (is_drawable()) {
1847 Gdk::Cursor *cursor;
1848 switch (mouse_mode) {
1850 cursor = _cursors->selector;
1853 cursor = _cursors->zoom_in;
1856 cursor = _cursors->cross_hair;
1860 set_canvas_cursor (cursor);
1862 AutomationTimeAxisView* atv;
1863 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1864 clear_entered_track = false;
1865 set_entered_track (atv);
1871 case RangeMarkerBarItem:
1872 case TransportMarkerBarItem:
1873 case CdMarkerBarItem:
1876 if (is_drawable()) {
1877 set_canvas_cursor (_cursors->timebar);
1882 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1885 entered_marker = marker;
1886 marker->set_color_rgba (ARDOUR_UI::config()->canvasvar_EnteredMarker.get());
1888 case MeterMarkerItem:
1889 case TempoMarkerItem:
1890 if (is_drawable()) {
1891 set_canvas_cursor (_cursors->timebar);
1895 case FadeInHandleItem:
1896 if (mouse_mode == MouseObject && !internal_editing()) {
1897 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1899 rect->property_fill_color_rgba() = 0xBBBBBBAA;
1901 set_canvas_cursor (_cursors->fade_in);
1905 case FadeOutHandleItem:
1906 if (mouse_mode == MouseObject && !internal_editing()) {
1907 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1909 rect->property_fill_color_rgba() = 0xBBBBBBAA;
1911 set_canvas_cursor (_cursors->fade_out);
1914 case FeatureLineItem:
1916 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1917 line->property_fill_color_rgba() = 0xFF0000FF;
1921 if (join_object_range_button.get_active()) {
1922 set_canvas_cursor ();
1930 /* second pass to handle entered track status in a comprehensible way.
1933 switch (item_type) {
1935 case AutomationLineItem:
1936 case ControlPointItem:
1937 /* these do not affect the current entered track state */
1938 clear_entered_track = false;
1941 case AutomationTrackItem:
1942 /* handled above already */
1946 set_entered_track (0);
1954 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent*, ItemType item_type)
1964 switch (item_type) {
1965 case ControlPointItem:
1966 cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
1967 if (cp->line().the_list()->interpolation() != AutomationList::Discrete) {
1968 if (cp->line().npoints() > 1 && !cp->get_selected()) {
1969 cp->set_visible (false);
1973 if (is_drawable()) {
1974 set_canvas_cursor (current_canvas_cursor);
1977 _verbose_cursor->hide ();
1980 case RegionViewNameHighlight:
1981 case LeftFrameHandle:
1982 case RightFrameHandle:
1983 case StartSelectionTrimItem:
1984 case EndSelectionTrimItem:
1985 case PlayheadCursorItem:
1988 case ImageFrameHandleStartItem:
1989 case ImageFrameHandleEndItem:
1990 case MarkerViewHandleStartItem:
1991 case MarkerViewHandleEndItem:
1994 _over_region_trim_target = false;
1996 if (is_drawable()) {
1997 set_canvas_cursor (current_canvas_cursor);
2002 case AutomationLineItem:
2003 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
2005 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
2007 line->property_fill_color_rgba() = al->get_line_color();
2009 if (is_drawable()) {
2010 set_canvas_cursor (current_canvas_cursor);
2014 case RegionViewName:
2015 /* see enter_handler() for notes */
2016 _over_region_trim_target = false;
2018 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
2019 if (is_drawable() && mouse_mode == MouseObject) {
2020 set_canvas_cursor (current_canvas_cursor);
2025 case RangeMarkerBarItem:
2026 case TransportMarkerBarItem:
2027 case CdMarkerBarItem:
2031 if (is_drawable()) {
2032 set_canvas_cursor (current_canvas_cursor);
2037 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2041 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
2042 location_flags_changed (loc, this);
2045 case MeterMarkerItem:
2046 case TempoMarkerItem:
2048 if (is_drawable()) {
2049 set_canvas_cursor (_cursors->timebar);
2054 case FadeInHandleItem:
2055 case FadeOutHandleItem:
2056 rv = static_cast<RegionView*>(item->get_data ("regionview"));
2058 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
2060 rect->property_fill_color_rgba() = rv->get_fill_color();
2061 rect->property_outline_pixels() = 0;
2064 set_canvas_cursor (current_canvas_cursor);
2067 case AutomationTrackItem:
2068 if (is_drawable()) {
2069 set_canvas_cursor (current_canvas_cursor);
2070 clear_entered_track = true;
2071 Glib::signal_idle().connect (sigc::mem_fun(*this, &Editor::left_automation_track));
2074 case FeatureLineItem:
2076 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
2077 line->property_fill_color_rgba() = (guint) ARDOUR_UI::config()->canvasvar_ZeroLine.get();;
2089 Editor::left_automation_track ()
2091 if (clear_entered_track) {
2092 set_entered_track (0);
2093 clear_entered_track = false;
2099 Editor::scrub (framepos_t frame, double current_x)
2103 if (scrubbing_direction == 0) {
2105 _session->request_locate (frame, false);
2106 _session->request_transport_speed (0.1);
2107 scrubbing_direction = 1;
2111 if (last_scrub_x > current_x) {
2113 /* pointer moved to the left */
2115 if (scrubbing_direction > 0) {
2117 /* we reversed direction to go backwards */
2120 scrub_reverse_distance += (int) (last_scrub_x - current_x);
2124 /* still moving to the left (backwards) */
2126 scrub_reversals = 0;
2127 scrub_reverse_distance = 0;
2129 delta = 0.01 * (last_scrub_x - current_x);
2130 _session->request_transport_speed_nonzero (_session->transport_speed() - delta);
2134 /* pointer moved to the right */
2136 if (scrubbing_direction < 0) {
2137 /* we reversed direction to go forward */
2140 scrub_reverse_distance += (int) (current_x - last_scrub_x);
2143 /* still moving to the right */
2145 scrub_reversals = 0;
2146 scrub_reverse_distance = 0;
2148 delta = 0.01 * (current_x - last_scrub_x);
2149 _session->request_transport_speed_nonzero (_session->transport_speed() + delta);
2153 /* if there have been more than 2 opposite motion moves detected, or one that moves
2154 back more than 10 pixels, reverse direction
2157 if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
2159 if (scrubbing_direction > 0) {
2160 /* was forwards, go backwards */
2161 _session->request_transport_speed (-0.1);
2162 scrubbing_direction = -1;
2164 /* was backwards, go forwards */
2165 _session->request_transport_speed (0.1);
2166 scrubbing_direction = 1;
2169 scrub_reverse_distance = 0;
2170 scrub_reversals = 0;
2174 last_scrub_x = current_x;
2178 Editor::motion_handler (ArdourCanvas::Item* /*item*/, GdkEvent* event, bool from_autoscroll)
2180 _last_motion_y = event->motion.y;
2182 if (event->motion.is_hint) {
2185 /* We call this so that MOTION_NOTIFY events continue to be
2186 delivered to the canvas. We need to do this because we set
2187 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
2188 the density of the events, at the expense of a round-trip
2189 to the server. Given that this will mostly occur on cases
2190 where DISPLAY = :0.0, and given the cost of what the motion
2191 event might do, its a good tradeoff.
2194 track_canvas->get_pointer (x, y);
2197 if (current_stepping_trackview) {
2198 /* don't keep the persistent stepped trackview if the mouse moves */
2199 current_stepping_trackview = 0;
2200 step_timeout.disconnect ();
2203 if (_session && _session->actively_recording()) {
2204 /* Sorry. no dragging stuff around while we record */
2208 JoinObjectRangeState const old = _join_object_range_state;
2209 update_join_object_range_location (event->motion.x, event->motion.y);
2210 if (_join_object_range_state != old) {
2211 set_canvas_cursor ();
2214 if (_over_region_trim_target) {
2215 set_canvas_cursor_for_region_view (event->motion.x, entered_regionview);
2218 bool handled = false;
2219 if (_drags->active ()) {
2220 handled = _drags->motion_handler (event, from_autoscroll);
2227 track_canvas_motion (event);
2232 Editor::can_remove_control_point (ArdourCanvas::Item* item)
2234 ControlPoint* control_point;
2236 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2237 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2241 AutomationLine& line = control_point->line ();
2242 if (dynamic_cast<AudioRegionGainLine*> (&line)) {
2243 /* we shouldn't remove the first or last gain point in region gain lines */
2244 if (line.is_last_point(*control_point) || line.is_first_point(*control_point)) {
2253 Editor::remove_control_point (ArdourCanvas::Item* item)
2255 if (!can_remove_control_point (item)) {
2259 ControlPoint* control_point;
2261 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2262 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2266 control_point->line().remove_point (*control_point);
2270 Editor::edit_control_point (ArdourCanvas::Item* item)
2272 ControlPoint* p = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"));
2275 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2279 ControlPointDialog d (p);
2280 d.set_position (Gtk::WIN_POS_MOUSE);
2283 if (d.run () != RESPONSE_ACCEPT) {
2287 p->line().modify_point_y (*p, d.get_y_fraction ());
2291 Editor::edit_note (ArdourCanvas::Item* item)
2293 ArdourCanvas::CanvasNoteEvent* e = dynamic_cast<ArdourCanvas::CanvasNoteEvent*> (item);
2296 EditNoteDialog d (&e->region_view(), e);
2297 d.set_position (Gtk::WIN_POS_MOUSE);
2305 Editor::visible_order_range (int* low, int* high) const
2307 *low = TimeAxisView::max_order ();
2310 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
2312 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
2314 if (!rtv->hidden()) {
2316 if (*high < rtv->order()) {
2317 *high = rtv->order ();
2320 if (*low > rtv->order()) {
2321 *low = rtv->order ();
2328 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
2330 /* Either add to or set the set the region selection, unless
2331 this is an alignment click (control used)
2334 if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
2335 TimeAxisView* tv = &rv.get_time_axis_view();
2336 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
2338 if (rtv && rtv->is_track()) {
2339 speed = rtv->track()->speed();
2342 framepos_t where = get_preferred_edit_position();
2346 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
2348 align_region (rv.region(), SyncPoint, (framepos_t) (where * speed));
2350 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
2352 align_region (rv.region(), End, (framepos_t) (where * speed));
2356 align_region (rv.region(), Start, (framepos_t) (where * speed));
2363 Editor::collect_new_region_view (RegionView* rv)
2365 latest_regionviews.push_back (rv);
2369 Editor::collect_and_select_new_region_view (RegionView* rv)
2372 latest_regionviews.push_back (rv);
2376 Editor::cancel_selection ()
2378 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2379 (*i)->hide_selection ();
2382 selection->clear ();
2383 clicked_selection = 0;
2388 Editor::point_trim (GdkEvent* event, framepos_t new_bound)
2390 RegionView* rv = clicked_regionview;
2392 /* Choose action dependant on which button was pressed */
2393 switch (event->button.button) {
2395 begin_reversible_command (_("start point trim"));
2397 if (selection->selected (rv)) {
2398 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
2399 i != selection->regions.by_layer().end(); ++i)
2402 cerr << "region view contains null region" << endl;
2405 if (!(*i)->region()->locked()) {
2406 (*i)->region()->clear_changes ();
2407 (*i)->region()->trim_front (new_bound);
2408 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2413 if (!rv->region()->locked()) {
2414 rv->region()->clear_changes ();
2415 rv->region()->trim_front (new_bound);
2416 _session->add_command(new StatefulDiffCommand (rv->region()));
2420 commit_reversible_command();
2424 begin_reversible_command (_("End point trim"));
2426 if (selection->selected (rv)) {
2428 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
2430 if (!(*i)->region()->locked()) {
2431 (*i)->region()->clear_changes();
2432 (*i)->region()->trim_end (new_bound);
2433 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2439 if (!rv->region()->locked()) {
2440 rv->region()->clear_changes ();
2441 rv->region()->trim_end (new_bound);
2442 _session->add_command (new StatefulDiffCommand (rv->region()));
2446 commit_reversible_command();
2455 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* /*event*/)
2460 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2461 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2465 Location* location = find_location_from_marker (marker, is_start);
2466 location->set_hidden (true, this);
2471 Editor::reposition_zoom_rect (framepos_t start, framepos_t end)
2473 double x1 = frame_to_pixel (start);
2474 double x2 = frame_to_pixel (end);
2475 double y2 = full_canvas_height - 1.0;
2477 zoom_rect->property_x1() = x1;
2478 zoom_rect->property_y1() = 1.0;
2479 zoom_rect->property_x2() = x2;
2480 zoom_rect->property_y2() = y2;
2485 Editor::mouse_rename_region (ArdourCanvas::Item* /*item*/, GdkEvent* /*event*/)
2487 using namespace Gtkmm2ext;
2489 ArdourPrompter prompter (false);
2491 prompter.set_prompt (_("Name for region:"));
2492 prompter.set_initial_text (clicked_regionview->region()->name());
2493 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
2494 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
2495 prompter.show_all ();
2496 switch (prompter.run ()) {
2497 case Gtk::RESPONSE_ACCEPT:
2499 prompter.get_result(str);
2501 clicked_regionview->region()->set_name (str);
2510 Editor::mouse_brush_insert_region (RegionView* rv, framepos_t pos)
2512 /* no brushing without a useful snap setting */
2514 switch (_snap_mode) {
2516 return; /* can't work because it allows region to be placed anywhere */
2521 switch (_snap_type) {
2529 /* don't brush a copy over the original */
2531 if (pos == rv->region()->position()) {
2535 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
2537 if (rtv == 0 || !rtv->is_track()) {
2541 boost::shared_ptr<Playlist> playlist = rtv->playlist();
2542 double speed = rtv->track()->speed();
2544 playlist->clear_changes ();
2545 boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region(), true));
2546 playlist->add_region (new_region, (framepos_t) (pos * speed));
2547 _session->add_command (new StatefulDiffCommand (playlist));
2549 // playlist is frozen, so we have to update manually XXX this is disgusting
2551 playlist->RegionAdded (new_region); /* EMIT SIGNAL */
2555 Editor::track_height_step_timeout ()
2557 if (get_microseconds() - last_track_height_step_timestamp < 250000) {
2558 current_stepping_trackview = 0;
2565 Editor::add_region_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2567 assert (region_view);
2569 if (!region_view->region()->playlist()) {
2573 _region_motion_group->raise_to_top ();
2575 if (Config->get_edit_mode() == Splice) {
2576 _drags->add (new RegionSpliceDrag (this, item, region_view, selection->regions.by_layer()));
2578 RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id);
2579 _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), false, false));
2582 /* sync the canvas to what we think is its current state */
2583 update_canvas_now();
2587 Editor::add_region_copy_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2589 assert (region_view);
2591 if (!region_view->region()->playlist()) {
2595 _region_motion_group->raise_to_top ();
2597 RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id);
2598 _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), false, true));
2602 Editor::add_region_brush_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2604 assert (region_view);
2606 if (!region_view->region()->playlist()) {
2610 if (Config->get_edit_mode() == Splice) {
2614 RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id);
2615 _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), true, false));
2617 begin_reversible_command (Operations::drag_region_brush);
2620 /** Start a grab where a time range is selected, track(s) are selected, and the
2621 * user clicks and drags a region with a modifier in order to create a new region containing
2622 * the section of the clicked region that lies within the time range.
2625 Editor::start_selection_grab (ArdourCanvas::Item* /*item*/, GdkEvent* event)
2627 if (clicked_regionview == 0) {
2631 /* lets try to create new Region for the selection */
2633 vector<boost::shared_ptr<Region> > new_regions;
2634 create_region_from_selection (new_regions);
2636 if (new_regions.empty()) {
2640 /* XXX fix me one day to use all new regions */
2642 boost::shared_ptr<Region> region (new_regions.front());
2644 /* add it to the current stream/playlist.
2646 tricky: the streamview for the track will add a new regionview. we will
2647 catch the signal it sends when it creates the regionview to
2648 set the regionview we want to then drag.
2651 latest_regionviews.clear();
2652 sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
2654 /* A selection grab currently creates two undo/redo operations, one for
2655 creating the new region and another for moving it.
2658 begin_reversible_command (Operations::selection_grab);
2660 boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
2662 playlist->clear_changes ();
2663 clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
2664 _session->add_command(new StatefulDiffCommand (playlist));
2666 commit_reversible_command ();
2670 if (latest_regionviews.empty()) {
2671 /* something went wrong */
2675 /* we need to deselect all other regionviews, and select this one
2676 i'm ignoring undo stuff, because the region creation will take care of it
2678 selection->set (latest_regionviews);
2680 _drags->set (new RegionMoveDrag (this, latest_regionviews.front()->get_canvas_group(), latest_regionviews.front(), latest_regionviews, false, false), event);
2686 if (_drags->active ()) {
2689 selection->clear ();
2694 Editor::set_internal_edit (bool yn)
2696 _internal_editing = yn;
2699 pre_internal_mouse_mode = mouse_mode;
2701 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2702 (*i)->enter_internal_edit_mode ();
2706 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2707 (*i)->leave_internal_edit_mode ();
2710 if (mouse_mode == MouseDraw && pre_internal_mouse_mode != MouseDraw) {
2711 /* we were drawing .. flip back to something sensible */
2712 set_mouse_mode (pre_internal_mouse_mode);
2716 set_canvas_cursor ();
2719 /** Update _join_object_range_state which indicate whether we are over the top or bottom half of a region view,
2720 * used by the `join object/range' tool mode.
2723 Editor::update_join_object_range_location (double /*x*/, double y)
2725 /* XXX: actually, this decides based on whether the mouse is in the top or bottom half of a RouteTimeAxisView;
2726 entered_{track,regionview} is not always setup (e.g. if the mouse is over a TimeSelection), and to get a Region
2727 that we're over requires searching the playlist.
2730 if (join_object_range_button.get_active() == false || (mouse_mode != MouseRange && mouse_mode != MouseObject)) {
2731 _join_object_range_state = JOIN_OBJECT_RANGE_NONE;
2735 if (mouse_mode == MouseObject) {
2736 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2737 } else if (mouse_mode == MouseRange) {
2738 _join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
2741 /* XXX: maybe we should make entered_track work in all cases, rather than resorting to this */
2742 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y + vertical_adjustment.get_value() - canvas_timebars_vsize);
2746 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
2751 rtv->canvas_display()->w2i (cx, cy);
2753 double const c = cy / rtv->view()->child_height();
2755 double const f = modf (c, &d);
2757 _join_object_range_state = f < 0.5 ? JOIN_OBJECT_RANGE_RANGE : JOIN_OBJECT_RANGE_OBJECT;
2763 Editor::effective_mouse_mode () const
2765 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
2767 } else if (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE) {
2775 Editor::remove_midi_note (ArdourCanvas::Item* item, GdkEvent *)
2777 ArdourCanvas::CanvasNoteEvent* e = dynamic_cast<ArdourCanvas::CanvasNoteEvent*> (item);
2780 e->region_view().delete_note (e->note ());
2784 Editor::set_canvas_cursor_for_region_view (double x, RegionView* rv)
2788 ArdourCanvas::Group* g = rv->get_canvas_group ();
2789 ArdourCanvas::Group* p = g->get_parent_group ();
2791 /* Compute x in region view parent coordinates */
2795 double x1, x2, y1, y2;
2796 g->get_bounds (x1, y1, x2, y2);
2798 /* Halfway across the region */
2799 double const h = (x1 + x2) / 2;
2801 Trimmable::CanTrim ct = rv->region()->can_trim ();
2803 if (ct & Trimmable::FrontTrimEarlier) {
2804 set_canvas_cursor (_cursors->left_side_trim);
2806 set_canvas_cursor (_cursors->left_side_trim_right_only);
2809 if (ct & Trimmable::EndTrimLater) {
2810 set_canvas_cursor (_cursors->right_side_trim);
2812 set_canvas_cursor (_cursors->right_side_trim_left_only);
2817 /** Obtain the pointer position in world coordinates */
2819 Editor::get_pointer_position (double& x, double& y) const
2822 track_canvas->get_pointer (px, py);
2823 track_canvas->window_to_world (px, py, x, y);