2 Copyright (C) 2000-2001 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
28 #include "pbd/error.h"
29 #include "pbd/enumwriter.h"
30 #include "pbd/memento_command.h"
31 #include "pbd/basename.h"
32 #include "pbd/stateful_diff_command.h"
34 #include "gtkmm2ext/bindings.h"
35 #include "gtkmm2ext/utils.h"
36 #include "gtkmm2ext/tearoff.h"
38 #include "ardour_ui.h"
40 #include "canvas-note.h"
42 #include "time_axis_view.h"
43 #include "audio_time_axis.h"
44 #include "audio_region_view.h"
45 #include "midi_region_view.h"
47 #include "streamview.h"
48 #include "region_gain_line.h"
49 #include "automation_time_axis.h"
50 #include "control_point.h"
53 #include "selection.h"
56 #include "rgb_macros.h"
57 #include "control_point_dialog.h"
58 #include "editor_drag.h"
59 #include "automation_region_view.h"
60 #include "edit_note_dialog.h"
61 #include "mouse_cursors.h"
62 #include "editor_cursors.h"
63 #include "verbose_cursor.h"
65 #include "ardour/types.h"
66 #include "ardour/profile.h"
67 #include "ardour/route.h"
68 #include "ardour/audio_track.h"
69 #include "ardour/playlist.h"
70 #include "ardour/audioplaylist.h"
71 #include "ardour/audioregion.h"
72 #include "ardour/midi_region.h"
73 #include "ardour/dB.h"
74 #include "ardour/utils.h"
75 #include "ardour/region_factory.h"
76 #include "ardour/source_factory.h"
77 #include "ardour/session.h"
78 #include "ardour/operations.h"
85 using namespace ARDOUR;
88 using namespace Editing;
89 using Gtkmm2ext::Keyboard;
92 Editor::mouse_frame (framepos_t& where, bool& in_track_canvas) const
96 Gdk::ModifierType mask;
97 Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->track_canvas->get_window();
98 Glib::RefPtr<const Gdk::Window> pointer_window;
100 if (!canvas_window) {
104 pointer_window = canvas_window->get_pointer (x, y, mask);
106 if (pointer_window == track_canvas->get_bin_window()) {
109 in_track_canvas = true;
112 in_track_canvas = false;
117 event.type = GDK_BUTTON_RELEASE;
121 where = event_frame (&event, 0, 0);
126 Editor::event_frame (GdkEvent const * event, double* pcx, double* pcy) const
140 switch (event->type) {
141 case GDK_BUTTON_RELEASE:
142 case GDK_BUTTON_PRESS:
143 case GDK_2BUTTON_PRESS:
144 case GDK_3BUTTON_PRESS:
145 *pcx = event->button.x;
146 *pcy = event->button.y;
147 _trackview_group->w2i(*pcx, *pcy);
149 case GDK_MOTION_NOTIFY:
150 *pcx = event->motion.x;
151 *pcy = event->motion.y;
152 _trackview_group->w2i(*pcx, *pcy);
154 case GDK_ENTER_NOTIFY:
155 case GDK_LEAVE_NOTIFY:
156 track_canvas->w2c(event->crossing.x, event->crossing.y, *pcx, *pcy);
159 case GDK_KEY_RELEASE:
160 // track_canvas->w2c(event->key.x, event->key.y, *pcx, *pcy);
163 warning << string_compose (_("Editor::event_frame() used on unhandled event type %1"), event->type) << endmsg;
167 /* note that pixel_to_frame() never returns less than zero, so even if the pixel
168 position is negative (as can be the case with motion events in particular),
169 the frame location is always positive.
172 return pixel_to_frame (*pcx);
176 Editor::which_grabber_cursor ()
178 Gdk::Cursor* c = _cursors->grabber;
180 if (_internal_editing) {
181 switch (mouse_mode) {
183 c = _cursors->midi_pencil;
187 c = _cursors->grabber_note;
191 c = _cursors->midi_resize;
200 switch (_edit_point) {
202 c = _cursors->grabber_edit_point;
205 boost::shared_ptr<Movable> m = _movable.lock();
206 if (m && m->locked()) {
207 c = _cursors->speaker;
217 Editor::set_current_trimmable (boost::shared_ptr<Trimmable> t)
219 boost::shared_ptr<Trimmable> st = _trimmable.lock();
221 if (!st || st == t) {
223 set_canvas_cursor ();
228 Editor::set_current_movable (boost::shared_ptr<Movable> m)
230 boost::shared_ptr<Movable> sm = _movable.lock();
232 if (!sm || sm != m) {
234 set_canvas_cursor ();
239 Editor::set_canvas_cursor ()
241 if (_internal_editing) {
243 switch (mouse_mode) {
245 current_canvas_cursor = _cursors->midi_pencil;
249 current_canvas_cursor = which_grabber_cursor();
253 current_canvas_cursor = _cursors->midi_resize;
262 switch (mouse_mode) {
264 current_canvas_cursor = _cursors->selector;
268 current_canvas_cursor = which_grabber_cursor();
272 /* shouldn't be possible, but just cover it anyway ... */
273 current_canvas_cursor = _cursors->midi_pencil;
277 current_canvas_cursor = _cursors->cross_hair;
281 if (Keyboard::the_keyboard().key_is_down (GDK_Control_L)) {
282 current_canvas_cursor = _cursors->zoom_out;
284 current_canvas_cursor = _cursors->zoom_in;
289 current_canvas_cursor = _cursors->time_fx; // just use playhead
293 current_canvas_cursor = _cursors->speaker;
298 switch (_join_object_range_state) {
299 case JOIN_OBJECT_RANGE_NONE:
301 case JOIN_OBJECT_RANGE_OBJECT:
302 current_canvas_cursor = which_grabber_cursor ();
304 case JOIN_OBJECT_RANGE_RANGE:
305 current_canvas_cursor = _cursors->selector;
309 /* up-down cursor as a cue that automation can be dragged up and down when in join object/range mode */
310 if (smart_mode_action->get_active()) {
312 get_pointer_position (x, y);
313 ArdourCanvas::Item* i = track_canvas->get_item_at (x, y);
314 if (i && i->property_parent() && (*i->property_parent()).get_data (X_("timeselection"))) {
315 pair<TimeAxisView*, int> tvp = trackview_by_y_position (_last_motion_y + vertical_adjustment.get_value() - canvas_timebars_vsize);
316 if (dynamic_cast<AutomationTimeAxisView*> (tvp.first)) {
317 current_canvas_cursor = _cursors->up_down;
322 set_canvas_cursor (current_canvas_cursor, true);
326 Editor::set_mouse_mode (MouseMode m, bool force)
328 if (_drags->active ()) {
332 if (!force && m == mouse_mode) {
336 Glib::RefPtr<Action> act;
340 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-range"));
344 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-object"));
348 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-draw"));
352 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-gain"));
356 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-zoom"));
360 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-timefx"));
364 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-audition"));
370 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
373 /* go there and back to ensure that the toggled handler is called to set up mouse_mode */
374 tact->set_active (false);
375 tact->set_active (true);
377 MouseModeChanged (); /* EMIT SIGNAL */
381 Editor::mouse_mode_toggled (MouseMode m)
383 Glib::RefPtr<Action> act;
384 Glib::RefPtr<ToggleAction> tact;
388 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-range"));
392 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-object"));
396 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-draw"));
400 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-gain"));
404 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-zoom"));
408 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-timefx"));
412 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-audition"));
418 tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
421 if (!tact->get_active()) {
422 /* this was just the notification that the old mode has been
423 * left. we'll get called again with the new mode active in a
431 act = ActionManager::get_action (X_("MouseMode"), X_("toggle-internal-edit"));
432 tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
433 tact->set_active (true);
443 if (!internal_editing()) {
444 if (mouse_mode != MouseRange && _join_object_range_state == JOIN_OBJECT_RANGE_NONE) {
446 /* in all modes except range and joined object/range, hide the range selection,
447 show the object (region) selection.
450 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
451 (*i)->hide_selection ();
457 in range or object/range mode, show the range selection.
460 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
461 (*i)->show_selection (selection->time);
466 set_canvas_cursor ();
468 MouseModeChanged (); /* EMIT SIGNAL */
472 Editor::step_mouse_mode (bool next)
474 switch (current_mouse_mode()) {
477 if (Profile->get_sae()) {
478 set_mouse_mode (MouseZoom);
480 set_mouse_mode (MouseRange);
483 set_mouse_mode (MouseTimeFX);
488 if (next) set_mouse_mode (MouseDraw);
489 else set_mouse_mode (MouseObject);
493 if (next) set_mouse_mode (MouseZoom);
494 else set_mouse_mode (MouseRange);
499 if (Profile->get_sae()) {
500 set_mouse_mode (MouseTimeFX);
502 set_mouse_mode (MouseGain);
505 if (Profile->get_sae()) {
506 set_mouse_mode (MouseObject);
508 set_mouse_mode (MouseDraw);
514 if (next) set_mouse_mode (MouseTimeFX);
515 else set_mouse_mode (MouseZoom);
520 set_mouse_mode (MouseAudition);
522 if (Profile->get_sae()) {
523 set_mouse_mode (MouseZoom);
525 set_mouse_mode (MouseGain);
531 if (next) set_mouse_mode (MouseObject);
532 else set_mouse_mode (MouseTimeFX);
538 Editor::toggle_internal_editing_from_double_click (GdkEvent* event)
540 if (_drags->active()) {
541 _drags->end_grab (event);
544 ActionManager::do_action ("MouseMode", "toggle-internal-edit");
546 /* prevent reversion of edit cursor on button release */
548 pre_press_cursor = 0;
554 Editor::button_selection (ArdourCanvas::Item* /*item*/, GdkEvent* event, ItemType item_type)
556 /* in object/audition/timefx/gain-automation mode,
557 any button press sets the selection if the object
558 can be selected. this is a bit of hack, because
559 we want to avoid this if the mouse operation is a
562 note: not dbl-click or triple-click
564 Also note that there is no region selection in internal edit mode, otherwise
565 for operations operating on the selection (e.g. cut) it is not obvious whether
566 to cut notes or regions.
569 if (((mouse_mode != MouseObject) &&
570 (_join_object_range_state != JOIN_OBJECT_RANGE_OBJECT) &&
571 (mouse_mode != MouseAudition || item_type != RegionItem) &&
572 (mouse_mode != MouseTimeFX || item_type != RegionItem) &&
573 (mouse_mode != MouseGain) &&
574 (mouse_mode != MouseRange) &&
575 (mouse_mode != MouseDraw)) ||
576 ((event->type != GDK_BUTTON_PRESS && event->type != GDK_BUTTON_RELEASE) || event->button.button > 3) ||
577 internal_editing()) {
582 if (event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) {
584 if ((event->button.state & Keyboard::RelevantModifierKeyMask) && event->button.button != 1) {
586 /* almost no selection action on modified button-2 or button-3 events */
588 if (item_type != RegionItem && event->button.button != 2) {
594 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
595 bool press = (event->type == GDK_BUTTON_PRESS);
599 if (!doing_range_stuff()) {
600 set_selected_regionview_from_click (press, op);
604 if (doing_range_stuff()) {
605 /* don't change the selection unless the
606 clicked track is not currently selected. if
607 so, "collapse" the selection to just this
610 if (!selection->selected (clicked_axisview)) {
611 set_selected_track_as_side_effect (Selection::Set);
617 case RegionViewNameHighlight:
619 case LeftFrameHandle:
620 case RightFrameHandle:
621 if (doing_object_stuff() || (mouse_mode != MouseRange && mouse_mode != MouseObject)) {
622 set_selected_regionview_from_click (press, op);
623 } else if (event->type == GDK_BUTTON_PRESS) {
624 set_selected_track_as_side_effect (op);
629 case FadeInHandleItem:
631 case FadeOutHandleItem:
633 if (doing_object_stuff() || (mouse_mode != MouseRange && mouse_mode != MouseObject)) {
634 set_selected_regionview_from_click (press, op);
635 } else if (event->type == GDK_BUTTON_PRESS) {
636 set_selected_track_as_side_effect (op);
640 case ControlPointItem:
641 set_selected_track_as_side_effect (op);
642 if (doing_object_stuff() || (mouse_mode != MouseRange && mouse_mode != MouseObject)) {
643 set_selected_control_point_from_click (op, false);
648 /* for context click, select track */
649 if (event->button.button == 3) {
650 selection->clear_tracks ();
651 set_selected_track_as_side_effect (op);
655 case AutomationTrackItem:
656 set_selected_track_as_side_effect (op);
665 Editor::button_press_handler_1 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
667 /* single mouse clicks on any of these item types operate
668 independent of mouse mode, mostly because they are
669 not on the main track canvas or because we want
674 case PlayheadCursorItem:
675 _drags->set (new CursorDrag (this, item, true), event);
679 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
680 hide_marker (item, event);
682 _drags->set (new MarkerDrag (this, item), event);
686 case TempoMarkerItem:
688 TempoMarker* m = reinterpret_cast<TempoMarker*> (item->get_data ("marker"));
690 if (m->tempo().movable ()) {
692 new TempoMarkerDrag (
695 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
705 case MeterMarkerItem:
707 MeterMarker* m = reinterpret_cast<MeterMarker*> (item->get_data ("marker"));
709 if (m->meter().movable ()) {
711 new MeterMarkerDrag (
714 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
727 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
728 _drags->set (new CursorDrag (this, &playhead_cursor->canvas_item, false), event);
734 case RangeMarkerBarItem:
735 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
736 _drags->set (new CursorDrag (this, &playhead_cursor->canvas_item, false), event);
738 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateRangeMarker), event);
743 case CdMarkerBarItem:
744 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
745 _drags->set (new CursorDrag (this, &playhead_cursor->canvas_item, false), event);
747 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateCDMarker), event);
752 case TransportMarkerBarItem:
753 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
754 _drags->set (new CursorDrag (this, &playhead_cursor->canvas_item, false), event);
756 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateTransportMarker), event);
765 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
766 /* special case: allow trim of range selections in joined object mode;
767 in theory eff should equal MouseRange in this case, but it doesn't
768 because entering the range selection canvas item results in entered_regionview
769 being set to 0, so update_join_object_range_location acts as if we aren't
772 if (item_type == StartSelectionTrimItem) {
773 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
774 } else if (item_type == EndSelectionTrimItem) {
775 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
779 Editing::MouseMode eff = effective_mouse_mode ();
781 /* special case: allow drag of region fade in/out in object mode with join object/range enabled */
782 if (item_type == FadeInHandleItem || item_type == FadeOutHandleItem) {
789 case StartSelectionTrimItem:
790 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
793 case EndSelectionTrimItem:
794 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
798 if (Keyboard::modifier_state_contains
799 (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier))) {
800 // contains and not equals because I can't use alt as a modifier alone.
801 start_selection_grab (item, event);
802 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
803 /* grab selection for moving */
804 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionMove), event);
806 double const y = event->button.y + vertical_adjustment.get_value() - canvas_timebars_vsize;
807 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
809 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
810 if (smart_mode_action->get_active() && atv) {
811 /* smart "join" mode: drag automation */
812 _drags->set (new AutomationRangeDrag (this, atv->base_item(), selection->time), event, _cursors->up_down);
814 /* this was debated, but decided the more common action was to
815 make a new selection */
816 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
823 if (internal_editing()) {
824 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
825 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
829 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
834 case RegionViewNameHighlight:
835 if (!clicked_regionview->region()->locked()) {
836 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
837 _drags->set (new TrimDrag (this, item, clicked_regionview, s.by_layer()), event);
842 case LeftFrameHandle:
843 case RightFrameHandle:
844 if (!internal_editing() && doing_object_stuff() && !clicked_regionview->region()->locked()) {
845 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
846 _drags->set (new TrimDrag (this, item, clicked_regionview, s.by_layer()), event);
852 if (!internal_editing()) {
853 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
862 if (internal_editing()) {
863 /* trim notes if we're in internal edit mode and near the ends of the note */
864 ArdourCanvas::CanvasNote* cn = dynamic_cast<ArdourCanvas::CanvasNote*> (item);
865 if (cn && cn->big_enough_to_trim() && cn->mouse_near_ends()) {
866 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
868 _drags->set (new NoteDrag (this, item), event);
881 if (internal_editing()) {
882 ArdourCanvas::CanvasNoteEvent* cn = dynamic_cast<ArdourCanvas::CanvasNoteEvent*> (item);
883 if (cn->mouse_near_ends()) {
884 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
886 _drags->set (new NoteDrag (this, item), event);
896 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)) &&
897 event->type == GDK_BUTTON_PRESS) {
899 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
901 } else if (event->type == GDK_BUTTON_PRESS) {
904 case FadeInHandleItem:
906 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
907 _drags->set (new FadeInDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), s), event, _cursors->fade_in);
911 case FadeOutHandleItem:
913 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
914 _drags->set (new FadeOutDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), s), event, _cursors->fade_out);
918 case FeatureLineItem:
920 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier)) {
921 remove_transient(item);
925 _drags->set (new FeatureLineDrag (this, item), event);
931 if (dynamic_cast<AutomationRegionView*> (clicked_regionview)) {
932 /* click on an automation region view; do nothing here and let the ARV's signal handler
938 if (internal_editing ()) {
939 if (event->type == GDK_2BUTTON_PRESS && event->button.button == 1) {
940 Glib::RefPtr<Action> act = ActionManager::get_action (X_("MouseMode"), X_("toggle-internal-edit"));
946 /* click on a normal region view */
947 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
948 add_region_copy_drag (item, event, clicked_regionview);
949 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
950 add_region_brush_drag (item, event, clicked_regionview);
952 add_region_drag (item, event, clicked_regionview);
956 if (!internal_editing() && (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE && !selection->regions.empty())) {
957 _drags->add (new SelectionDrag (this, clicked_axisview->get_selection_rect (clicked_selection)->rect, SelectionDrag::SelectionMove));
960 _drags->start_grab (event);
963 case RegionViewNameHighlight:
964 case LeftFrameHandle:
965 case RightFrameHandle:
966 if (!clicked_regionview->region()->locked()) {
967 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
968 _drags->set (new TrimDrag (this, item, clicked_regionview, s.by_layer()), event);
975 /* rename happens on edit clicks */
976 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
977 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, s.by_layer()), event);
982 case ControlPointItem:
983 _drags->set (new ControlPointDrag (this, item), event);
987 case AutomationLineItem:
988 _drags->set (new LineDrag (this, item), event);
993 if (internal_editing()) {
994 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
995 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
999 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
1003 case AutomationTrackItem:
1005 TimeAxisView* parent = clicked_axisview->get_parent ();
1006 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (clicked_axisview);
1008 if (parent && dynamic_cast<MidiTimeAxisView*> (parent) && atv->show_regions ()) {
1009 /* create a MIDI region so that we have somewhere to put automation */
1010 _drags->set (new RegionCreateDrag (this, item, parent), event);
1012 /* rubberband drag to select automation points */
1013 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
1020 if (smart_mode_action->get_active()) {
1021 /* we're in "smart" joined mode, and we've clicked on a Selection */
1022 double const y = event->button.y + vertical_adjustment.get_value() - canvas_timebars_vsize;
1023 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
1025 /* if we're over an automation track, start a drag of its data */
1026 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
1028 _drags->set (new AutomationRangeDrag (this, atv->base_item(), selection->time), event, _cursors->up_down);
1031 /* if we're over a track and a region, and in the `object' part of a region,
1032 put a selection around the region and drag both
1034 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1035 if (rtv && _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
1036 boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track> (rtv->route ());
1038 boost::shared_ptr<Playlist> pl = t->playlist ();
1041 boost::shared_ptr<Region> r = pl->top_region_at (event_frame (event));
1043 RegionView* rv = rtv->view()->find_view (r);
1044 clicked_selection = select_range (rv->region()->position(),
1045 rv->region()->last_frame()+1);
1046 _drags->add (new SelectionDrag (this, item, SelectionDrag::SelectionMove));
1047 list<RegionView*> rvs;
1049 _drags->add (new RegionMoveDrag (this, item, rv, rvs, false, false));
1050 _drags->start_grab (event);
1061 case ImageFrameHandleStartItem:
1062 imageframe_start_handle_op(item, event) ;
1065 case ImageFrameHandleEndItem:
1066 imageframe_end_handle_op(item, event) ;
1069 case MarkerViewHandleStartItem:
1070 markerview_item_start_handle_op(item, event) ;
1073 case MarkerViewHandleEndItem:
1074 markerview_item_end_handle_op(item, event) ;
1077 case MarkerViewItem:
1078 start_markerview_grab(item, event) ;
1080 case ImageFrameItem:
1081 start_imageframe_grab(item, event) ;
1097 switch (item_type) {
1099 /* start a grab so that if we finish after moving
1100 we can tell what happened.
1102 _drags->set (new RegionGainDrag (this, item), event, current_canvas_cursor);
1106 _drags->set (new LineDrag (this, item), event);
1109 case ControlPointItem:
1110 _drags->set (new ControlPointDrag (this, item), event);
1120 switch (item_type) {
1121 case ControlPointItem:
1122 _drags->set (new ControlPointDrag (this, item), event);
1125 case AutomationLineItem:
1126 _drags->set (new LineDrag (this, item), event);
1130 // XXX need automation mode to identify which
1132 // start_line_grab_from_regionview (item, event);
1142 if (event->type == GDK_BUTTON_PRESS) {
1143 _drags->set (new MouseZoomDrag (this, item), event);
1150 if (internal_editing() && item_type == NoteItem) {
1151 /* drag notes if we're in internal edit mode */
1152 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
1154 } else if ((!internal_editing() || dynamic_cast<AudioRegionView*> (clicked_regionview)) && clicked_regionview) {
1155 /* do time-FX if we're not in internal edit mode, or we are but we clicked on an audio region */
1156 _drags->set (new TimeFXDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1162 _drags->set (new ScrubDrag (this, item), event);
1163 scrub_reversals = 0;
1164 scrub_reverse_distance = 0;
1165 last_scrub_x = event->button.x;
1166 scrubbing_direction = 0;
1167 set_canvas_cursor (_cursors->transparent);
1179 Editor::button_press_handler_2 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1181 Editing::MouseMode const eff = effective_mouse_mode ();
1184 switch (item_type) {
1186 if (internal_editing ()) {
1187 /* no region drags in internal edit mode */
1191 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
1192 add_region_copy_drag (item, event, clicked_regionview);
1194 add_region_drag (item, event, clicked_regionview);
1196 _drags->start_grab (event);
1199 case ControlPointItem:
1200 _drags->set (new ControlPointDrag (this, item), event);
1208 switch (item_type) {
1209 case RegionViewNameHighlight:
1210 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1214 case LeftFrameHandle:
1215 case RightFrameHandle:
1216 if (!internal_editing ()) {
1217 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1222 case RegionViewName:
1223 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer()), event);
1237 /* relax till release */
1243 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1244 temporal_zoom_to_frame (false, event_frame (event));
1246 temporal_zoom_to_frame (true, event_frame(event));
1259 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1261 if (event->type != GDK_BUTTON_PRESS) {
1265 Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->track_canvas->get_window();
1267 if (canvas_window) {
1268 Glib::RefPtr<const Gdk::Window> pointer_window;
1271 Gdk::ModifierType mask;
1273 pointer_window = canvas_window->get_pointer (x, y, mask);
1275 if (pointer_window == track_canvas->get_bin_window()) {
1276 track_canvas->window_to_world (x, y, wx, wy);
1280 pre_press_cursor = current_canvas_cursor;
1282 track_canvas->grab_focus();
1284 if (_session && _session->actively_recording()) {
1290 if (internal_editing()) {
1291 bool leave_internal_edit_mode = false;
1293 switch (item_type) {
1298 if (!dynamic_cast<MidiRegionView*> (clicked_regionview)) {
1299 leave_internal_edit_mode = true;
1303 case PlayheadCursorItem:
1305 case TempoMarkerItem:
1306 case MeterMarkerItem:
1310 case RangeMarkerBarItem:
1311 case CdMarkerBarItem:
1312 case TransportMarkerBarItem:
1313 /* button press on these events never does anything to
1314 change the editing mode.
1319 leave_internal_edit_mode = true;
1326 if (leave_internal_edit_mode) {
1327 ActionManager::do_action ("MouseMode", "toggle-internal-edit");
1331 button_selection (item, event, item_type);
1333 if (!_drags->active () &&
1334 (Keyboard::is_delete_event (&event->button) ||
1335 Keyboard::is_context_menu_event (&event->button) ||
1336 Keyboard::is_edit_event (&event->button))) {
1338 /* handled by button release */
1342 switch (event->button.button) {
1344 return button_press_handler_1 (item, event, item_type);
1348 return button_press_handler_2 (item, event, item_type);
1355 return button_press_dispatch (&event->button);
1364 Editor::button_press_dispatch (GdkEventButton* ev)
1366 /* this function is intended only for buttons 4 and above.
1369 Gtkmm2ext::MouseButton b (ev->state, ev->button);
1370 return button_bindings->activate (b, Gtkmm2ext::Bindings::Press);
1374 Editor::button_release_dispatch (GdkEventButton* ev)
1376 /* this function is intended only for buttons 4 and above.
1379 Gtkmm2ext::MouseButton b (ev->state, ev->button);
1380 return button_bindings->activate (b, Gtkmm2ext::Bindings::Release);
1384 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1386 framepos_t where = event_frame (event, 0, 0);
1387 AutomationTimeAxisView* atv = 0;
1389 if (pre_press_cursor) {
1390 set_canvas_cursor (pre_press_cursor);
1391 pre_press_cursor = 0;
1394 /* no action if we're recording */
1396 if (_session && _session->actively_recording()) {
1400 /* see if we're finishing a drag */
1402 bool were_dragging = false;
1403 if (_drags->active ()) {
1404 bool const r = _drags->end_grab (event);
1406 /* grab dragged, so do nothing else */
1410 were_dragging = true;
1413 update_region_layering_order_editor ();
1415 /* edit events get handled here */
1417 if (!_drags->active () && Keyboard::is_edit_event (&event->button)) {
1418 switch (item_type) {
1420 show_region_properties ();
1423 case TempoMarkerItem:
1424 edit_tempo_marker (item);
1427 case MeterMarkerItem:
1428 edit_meter_marker (item);
1431 case RegionViewName:
1432 if (clicked_regionview->name_active()) {
1433 return mouse_rename_region (item, event);
1437 case ControlPointItem:
1438 edit_control_point (item);
1451 /* context menu events get handled here */
1453 if (Keyboard::is_context_menu_event (&event->button)) {
1455 context_click_event = *event;
1457 if (!_drags->active ()) {
1459 /* no matter which button pops up the context menu, tell the menu
1460 widget to use button 1 to drive menu selection.
1463 switch (item_type) {
1465 case FadeInHandleItem:
1467 case FadeOutHandleItem:
1468 popup_fade_context_menu (1, event->button.time, item, item_type);
1472 popup_track_context_menu (1, event->button.time, item_type, false);
1476 case RegionViewNameHighlight:
1477 case LeftFrameHandle:
1478 case RightFrameHandle:
1479 case RegionViewName:
1480 popup_track_context_menu (1, event->button.time, item_type, false);
1484 popup_track_context_menu (1, event->button.time, item_type, true);
1487 case AutomationTrackItem:
1488 popup_track_context_menu (1, event->button.time, item_type, false);
1492 case RangeMarkerBarItem:
1493 case TransportMarkerBarItem:
1494 case CdMarkerBarItem:
1497 popup_ruler_menu (where, item_type);
1501 marker_context_menu (&event->button, item);
1504 case TempoMarkerItem:
1505 tempo_or_meter_marker_context_menu (&event->button, item);
1508 case MeterMarkerItem:
1509 tempo_or_meter_marker_context_menu (&event->button, item);
1512 case CrossfadeViewItem:
1513 popup_track_context_menu (1, event->button.time, item_type, false);
1516 case ControlPointItem:
1517 popup_control_point_context_menu (item, event);
1521 case ImageFrameItem:
1522 popup_imageframe_edit_menu(1, event->button.time, item, true) ;
1524 case ImageFrameTimeAxisItem:
1525 popup_imageframe_edit_menu(1, event->button.time, item, false) ;
1527 case MarkerViewItem:
1528 popup_marker_time_axis_edit_menu(1, event->button.time, item, true) ;
1530 case MarkerTimeAxisItem:
1531 popup_marker_time_axis_edit_menu(1, event->button.time, item, false) ;
1543 /* delete events get handled here */
1545 Editing::MouseMode const eff = effective_mouse_mode ();
1547 if (!_drags->active () && Keyboard::is_delete_event (&event->button)) {
1549 switch (item_type) {
1550 case TempoMarkerItem:
1551 remove_tempo_marker (item);
1554 case MeterMarkerItem:
1555 remove_meter_marker (item);
1559 remove_marker (*item, event);
1563 if (eff == MouseObject) {
1564 remove_clicked_region ();
1568 case ControlPointItem:
1569 remove_control_point (item);
1573 remove_midi_note (item, event);
1582 switch (event->button.button) {
1585 switch (item_type) {
1586 /* see comments in button_press_handler */
1587 case PlayheadCursorItem:
1590 case AutomationLineItem:
1591 case StartSelectionTrimItem:
1592 case EndSelectionTrimItem:
1596 if (!_dragging_playhead) {
1597 snap_to_with_modifier (where, event, 0, true);
1598 mouse_add_new_marker (where);
1602 case CdMarkerBarItem:
1603 if (!_dragging_playhead) {
1604 // if we get here then a dragged range wasn't done
1605 snap_to_with_modifier (where, event, 0, true);
1606 mouse_add_new_marker (where, true);
1611 if (!_dragging_playhead) {
1612 snap_to_with_modifier (where, event);
1613 mouse_add_new_tempo_event (where);
1618 if (!_dragging_playhead) {
1619 mouse_add_new_meter_event (pixel_to_frame (event->button.x));
1630 switch (item_type) {
1631 case AutomationTrackItem:
1632 atv = dynamic_cast<AutomationTimeAxisView*>(clicked_axisview);
1634 atv->add_automation_event (event, where, event->button.y);
1644 switch (item_type) {
1647 /* check that we didn't drag before releasing, since
1648 its really annoying to create new control
1649 points when doing this.
1651 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (clicked_regionview);
1652 if (were_dragging && arv) {
1653 arv->add_gain_point_event (item, event);
1659 case AutomationTrackItem:
1660 dynamic_cast<AutomationTimeAxisView*>(clicked_axisview)->
1661 add_automation_event (event, where, event->button.y);
1670 set_canvas_cursor (current_canvas_cursor);
1671 if (scrubbing_direction == 0) {
1672 /* no drag, just a click */
1673 switch (item_type) {
1675 play_selected_region ();
1681 /* make sure we stop */
1682 _session->request_transport_speed (0.0);
1691 /* do any (de)selection operations that should occur on button release */
1692 button_selection (item, event, item_type);
1701 switch (item_type) {
1703 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1705 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier|Keyboard::SecondaryModifier))) {
1708 // Button2 click is unused
1723 // x_style_paste (where, 1.0);
1744 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1751 switch (item_type) {
1752 case ControlPointItem:
1753 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1754 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1755 cp->set_visible (true);
1759 at_y = cp->get_y ();
1760 cp->i2w (at_x, at_y);
1764 fraction = 1.0 - (cp->get_y() / cp->line().height());
1766 if (is_drawable() && !_drags->active ()) {
1767 set_canvas_cursor (_cursors->fader);
1770 _verbose_cursor->set (cp->line().get_verbose_cursor_string (fraction), at_x, at_y);
1771 _verbose_cursor->show ();
1776 if (mouse_mode == MouseGain) {
1777 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1779 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredGainLine.get();
1780 if (is_drawable()) {
1781 set_canvas_cursor (_cursors->fader);
1786 case AutomationLineItem:
1787 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1788 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1790 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredAutomationLine.get();
1792 if (is_drawable()) {
1793 set_canvas_cursor (_cursors->fader);
1798 case RegionViewNameHighlight:
1799 if (is_drawable() && doing_object_stuff() && entered_regionview) {
1800 set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1801 _over_region_trim_target = true;
1805 case LeftFrameHandle:
1806 case RightFrameHandle:
1807 if (is_drawable() && doing_object_stuff() && !internal_editing() && entered_regionview) {
1808 set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1812 case StartSelectionTrimItem:
1814 case ImageFrameHandleStartItem:
1815 case MarkerViewHandleStartItem:
1817 if (is_drawable()) {
1818 set_canvas_cursor (_cursors->left_side_trim);
1821 case EndSelectionTrimItem:
1823 case ImageFrameHandleEndItem:
1824 case MarkerViewHandleEndItem:
1826 if (is_drawable()) {
1827 set_canvas_cursor (_cursors->right_side_trim);
1831 case PlayheadCursorItem:
1832 if (is_drawable()) {
1833 switch (_edit_point) {
1835 set_canvas_cursor (_cursors->grabber_edit_point);
1838 set_canvas_cursor (_cursors->grabber);
1844 case RegionViewName:
1846 /* when the name is not an active item, the entire name highlight is for trimming */
1848 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1849 if (mouse_mode == MouseObject && is_drawable()) {
1850 set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1851 _over_region_trim_target = true;
1857 case AutomationTrackItem:
1858 if (is_drawable()) {
1859 Gdk::Cursor *cursor;
1860 switch (mouse_mode) {
1862 cursor = _cursors->selector;
1865 cursor = _cursors->zoom_in;
1868 cursor = _cursors->cross_hair;
1872 set_canvas_cursor (cursor);
1874 AutomationTimeAxisView* atv;
1875 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1876 clear_entered_track = false;
1877 set_entered_track (atv);
1883 case RangeMarkerBarItem:
1884 case TransportMarkerBarItem:
1885 case CdMarkerBarItem:
1888 if (is_drawable()) {
1889 set_canvas_cursor (_cursors->timebar);
1894 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1897 entered_marker = marker;
1898 marker->set_color_rgba (ARDOUR_UI::config()->canvasvar_EnteredMarker.get());
1900 case MeterMarkerItem:
1901 case TempoMarkerItem:
1902 if (is_drawable()) {
1903 set_canvas_cursor (_cursors->timebar);
1907 case FadeInHandleItem:
1908 if (mouse_mode == MouseObject && !internal_editing()) {
1909 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1911 rect->property_fill_color_rgba() = 0xBBBBBBAA;
1913 set_canvas_cursor (_cursors->fade_in);
1917 case FadeOutHandleItem:
1918 if (mouse_mode == MouseObject && !internal_editing()) {
1919 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1921 rect->property_fill_color_rgba() = 0xBBBBBBAA;
1923 set_canvas_cursor (_cursors->fade_out);
1926 case FeatureLineItem:
1928 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1929 line->property_fill_color_rgba() = 0xFF0000FF;
1933 if (smart_mode_action->get_active()) {
1934 set_canvas_cursor ();
1942 /* second pass to handle entered track status in a comprehensible way.
1945 switch (item_type) {
1947 case AutomationLineItem:
1948 case ControlPointItem:
1949 /* these do not affect the current entered track state */
1950 clear_entered_track = false;
1953 case AutomationTrackItem:
1954 /* handled above already */
1958 set_entered_track (0);
1966 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent*, ItemType item_type)
1976 switch (item_type) {
1977 case ControlPointItem:
1978 cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
1979 if (cp->line().the_list()->interpolation() != AutomationList::Discrete) {
1980 if (cp->line().npoints() > 1 && !cp->get_selected()) {
1981 cp->set_visible (false);
1985 if (is_drawable()) {
1986 set_canvas_cursor (current_canvas_cursor);
1989 _verbose_cursor->hide ();
1992 case RegionViewNameHighlight:
1993 case LeftFrameHandle:
1994 case RightFrameHandle:
1995 case StartSelectionTrimItem:
1996 case EndSelectionTrimItem:
1997 case PlayheadCursorItem:
2000 case ImageFrameHandleStartItem:
2001 case ImageFrameHandleEndItem:
2002 case MarkerViewHandleStartItem:
2003 case MarkerViewHandleEndItem:
2006 _over_region_trim_target = false;
2008 if (is_drawable()) {
2009 set_canvas_cursor (current_canvas_cursor);
2014 case AutomationLineItem:
2015 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
2017 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
2019 line->property_fill_color_rgba() = al->get_line_color();
2021 if (is_drawable()) {
2022 set_canvas_cursor (current_canvas_cursor);
2026 case RegionViewName:
2027 /* see enter_handler() for notes */
2028 _over_region_trim_target = false;
2030 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
2031 if (is_drawable() && mouse_mode == MouseObject) {
2032 set_canvas_cursor (current_canvas_cursor);
2037 case RangeMarkerBarItem:
2038 case TransportMarkerBarItem:
2039 case CdMarkerBarItem:
2043 if (is_drawable()) {
2044 set_canvas_cursor (current_canvas_cursor);
2049 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2053 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
2054 location_flags_changed (loc, this);
2057 case MeterMarkerItem:
2058 case TempoMarkerItem:
2060 if (is_drawable()) {
2061 set_canvas_cursor (current_canvas_cursor);
2066 case FadeInHandleItem:
2067 case FadeOutHandleItem:
2068 rv = static_cast<RegionView*>(item->get_data ("regionview"));
2070 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
2072 rect->property_fill_color_rgba() = rv->get_fill_color();
2073 rect->property_outline_pixels() = 0;
2076 set_canvas_cursor (current_canvas_cursor);
2079 case AutomationTrackItem:
2080 if (is_drawable()) {
2081 set_canvas_cursor (current_canvas_cursor);
2082 clear_entered_track = true;
2083 Glib::signal_idle().connect (sigc::mem_fun(*this, &Editor::left_automation_track));
2086 case FeatureLineItem:
2088 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
2089 line->property_fill_color_rgba() = (guint) ARDOUR_UI::config()->canvasvar_ZeroLine.get();;
2101 Editor::left_automation_track ()
2103 if (clear_entered_track) {
2104 set_entered_track (0);
2105 clear_entered_track = false;
2111 Editor::scrub (framepos_t frame, double current_x)
2115 if (scrubbing_direction == 0) {
2117 _session->request_locate (frame, false);
2118 _session->request_transport_speed (0.1);
2119 scrubbing_direction = 1;
2123 if (last_scrub_x > current_x) {
2125 /* pointer moved to the left */
2127 if (scrubbing_direction > 0) {
2129 /* we reversed direction to go backwards */
2132 scrub_reverse_distance += (int) (last_scrub_x - current_x);
2136 /* still moving to the left (backwards) */
2138 scrub_reversals = 0;
2139 scrub_reverse_distance = 0;
2141 delta = 0.01 * (last_scrub_x - current_x);
2142 _session->request_transport_speed_nonzero (_session->transport_speed() - delta);
2146 /* pointer moved to the right */
2148 if (scrubbing_direction < 0) {
2149 /* we reversed direction to go forward */
2152 scrub_reverse_distance += (int) (current_x - last_scrub_x);
2155 /* still moving to the right */
2157 scrub_reversals = 0;
2158 scrub_reverse_distance = 0;
2160 delta = 0.01 * (current_x - last_scrub_x);
2161 _session->request_transport_speed_nonzero (_session->transport_speed() + delta);
2165 /* if there have been more than 2 opposite motion moves detected, or one that moves
2166 back more than 10 pixels, reverse direction
2169 if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
2171 if (scrubbing_direction > 0) {
2172 /* was forwards, go backwards */
2173 _session->request_transport_speed (-0.1);
2174 scrubbing_direction = -1;
2176 /* was backwards, go forwards */
2177 _session->request_transport_speed (0.1);
2178 scrubbing_direction = 1;
2181 scrub_reverse_distance = 0;
2182 scrub_reversals = 0;
2186 last_scrub_x = current_x;
2190 Editor::motion_handler (ArdourCanvas::Item* /*item*/, GdkEvent* event, bool from_autoscroll)
2192 _last_motion_y = event->motion.y;
2194 if (event->motion.is_hint) {
2197 /* We call this so that MOTION_NOTIFY events continue to be
2198 delivered to the canvas. We need to do this because we set
2199 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
2200 the density of the events, at the expense of a round-trip
2201 to the server. Given that this will mostly occur on cases
2202 where DISPLAY = :0.0, and given the cost of what the motion
2203 event might do, its a good tradeoff.
2206 track_canvas->get_pointer (x, y);
2209 if (current_stepping_trackview) {
2210 /* don't keep the persistent stepped trackview if the mouse moves */
2211 current_stepping_trackview = 0;
2212 step_timeout.disconnect ();
2215 if (_session && _session->actively_recording()) {
2216 /* Sorry. no dragging stuff around while we record */
2220 JoinObjectRangeState const old = _join_object_range_state;
2221 update_join_object_range_location (event->motion.x, event->motion.y);
2222 if (_join_object_range_state != old) {
2223 set_canvas_cursor ();
2226 if (_over_region_trim_target) {
2227 set_canvas_cursor_for_region_view (event->motion.x, entered_regionview);
2230 bool handled = false;
2231 if (_drags->active ()) {
2232 handled = _drags->motion_handler (event, from_autoscroll);
2239 track_canvas_motion (event);
2244 Editor::can_remove_control_point (ArdourCanvas::Item* item)
2246 ControlPoint* control_point;
2248 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2249 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2253 AutomationLine& line = control_point->line ();
2254 if (dynamic_cast<AudioRegionGainLine*> (&line)) {
2255 /* we shouldn't remove the first or last gain point in region gain lines */
2256 if (line.is_last_point(*control_point) || line.is_first_point(*control_point)) {
2265 Editor::remove_control_point (ArdourCanvas::Item* item)
2267 if (!can_remove_control_point (item)) {
2271 ControlPoint* control_point;
2273 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2274 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2278 control_point->line().remove_point (*control_point);
2282 Editor::edit_control_point (ArdourCanvas::Item* item)
2284 ControlPoint* p = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"));
2287 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2291 ControlPointDialog d (p);
2292 d.set_position (Gtk::WIN_POS_MOUSE);
2295 if (d.run () != RESPONSE_ACCEPT) {
2299 p->line().modify_point_y (*p, d.get_y_fraction ());
2303 Editor::edit_note (ArdourCanvas::Item* item)
2305 ArdourCanvas::CanvasNoteEvent* e = dynamic_cast<ArdourCanvas::CanvasNoteEvent*> (item);
2308 EditNoteDialog d (&e->region_view(), e);
2309 d.set_position (Gtk::WIN_POS_MOUSE);
2317 Editor::visible_order_range (int* low, int* high) const
2319 *low = TimeAxisView::max_order ();
2322 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
2324 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
2326 if (!rtv->hidden()) {
2328 if (*high < rtv->order()) {
2329 *high = rtv->order ();
2332 if (*low > rtv->order()) {
2333 *low = rtv->order ();
2340 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
2342 /* Either add to or set the set the region selection, unless
2343 this is an alignment click (control used)
2346 if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
2347 TimeAxisView* tv = &rv.get_time_axis_view();
2348 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
2350 if (rtv && rtv->is_track()) {
2351 speed = rtv->track()->speed();
2354 framepos_t where = get_preferred_edit_position();
2358 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
2360 align_region (rv.region(), SyncPoint, (framepos_t) (where * speed));
2362 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
2364 align_region (rv.region(), End, (framepos_t) (where * speed));
2368 align_region (rv.region(), Start, (framepos_t) (where * speed));
2375 Editor::collect_new_region_view (RegionView* rv)
2377 latest_regionviews.push_back (rv);
2381 Editor::collect_and_select_new_region_view (RegionView* rv)
2384 latest_regionviews.push_back (rv);
2388 Editor::cancel_selection ()
2390 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2391 (*i)->hide_selection ();
2394 selection->clear ();
2395 clicked_selection = 0;
2400 Editor::point_trim (GdkEvent* event, framepos_t new_bound)
2402 RegionView* rv = clicked_regionview;
2404 /* Choose action dependant on which button was pressed */
2405 switch (event->button.button) {
2407 begin_reversible_command (_("start point trim"));
2409 if (selection->selected (rv)) {
2410 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
2411 i != selection->regions.by_layer().end(); ++i)
2414 cerr << "region view contains null region" << endl;
2417 if (!(*i)->region()->locked()) {
2418 (*i)->region()->clear_changes ();
2419 (*i)->region()->trim_front (new_bound);
2420 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2425 if (!rv->region()->locked()) {
2426 rv->region()->clear_changes ();
2427 rv->region()->trim_front (new_bound);
2428 _session->add_command(new StatefulDiffCommand (rv->region()));
2432 commit_reversible_command();
2436 begin_reversible_command (_("End point trim"));
2438 if (selection->selected (rv)) {
2440 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
2442 if (!(*i)->region()->locked()) {
2443 (*i)->region()->clear_changes();
2444 (*i)->region()->trim_end (new_bound);
2445 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2451 if (!rv->region()->locked()) {
2452 rv->region()->clear_changes ();
2453 rv->region()->trim_end (new_bound);
2454 _session->add_command (new StatefulDiffCommand (rv->region()));
2458 commit_reversible_command();
2467 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* /*event*/)
2472 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2473 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2477 Location* location = find_location_from_marker (marker, is_start);
2478 location->set_hidden (true, this);
2483 Editor::reposition_zoom_rect (framepos_t start, framepos_t end)
2485 double x1 = frame_to_pixel (start);
2486 double x2 = frame_to_pixel (end);
2487 double y2 = full_canvas_height - 1.0;
2489 zoom_rect->property_x1() = x1;
2490 zoom_rect->property_y1() = 1.0;
2491 zoom_rect->property_x2() = x2;
2492 zoom_rect->property_y2() = y2;
2497 Editor::mouse_rename_region (ArdourCanvas::Item* /*item*/, GdkEvent* /*event*/)
2499 using namespace Gtkmm2ext;
2501 ArdourPrompter prompter (false);
2503 prompter.set_prompt (_("Name for region:"));
2504 prompter.set_initial_text (clicked_regionview->region()->name());
2505 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
2506 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
2507 prompter.show_all ();
2508 switch (prompter.run ()) {
2509 case Gtk::RESPONSE_ACCEPT:
2511 prompter.get_result(str);
2513 clicked_regionview->region()->set_name (str);
2522 Editor::mouse_brush_insert_region (RegionView* rv, framepos_t pos)
2524 /* no brushing without a useful snap setting */
2526 switch (_snap_mode) {
2528 return; /* can't work because it allows region to be placed anywhere */
2533 switch (_snap_type) {
2541 /* don't brush a copy over the original */
2543 if (pos == rv->region()->position()) {
2547 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
2549 if (rtv == 0 || !rtv->is_track()) {
2553 boost::shared_ptr<Playlist> playlist = rtv->playlist();
2554 double speed = rtv->track()->speed();
2556 playlist->clear_changes ();
2557 boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region(), true));
2558 playlist->add_region (new_region, (framepos_t) (pos * speed));
2559 _session->add_command (new StatefulDiffCommand (playlist));
2561 // playlist is frozen, so we have to update manually XXX this is disgusting
2563 playlist->RegionAdded (new_region); /* EMIT SIGNAL */
2567 Editor::track_height_step_timeout ()
2569 if (get_microseconds() - last_track_height_step_timestamp < 250000) {
2570 current_stepping_trackview = 0;
2577 Editor::add_region_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2579 assert (region_view);
2581 if (!region_view->region()->playlist()) {
2585 _region_motion_group->raise_to_top ();
2587 if (Config->get_edit_mode() == Splice) {
2588 _drags->add (new RegionSpliceDrag (this, item, region_view, selection->regions.by_layer()));
2590 RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id);
2591 _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), false, false));
2594 /* sync the canvas to what we think is its current state */
2595 update_canvas_now();
2599 Editor::add_region_copy_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2601 assert (region_view);
2603 if (!region_view->region()->playlist()) {
2607 _region_motion_group->raise_to_top ();
2609 RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id);
2610 _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), false, true));
2614 Editor::add_region_brush_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2616 assert (region_view);
2618 if (!region_view->region()->playlist()) {
2622 if (Config->get_edit_mode() == Splice) {
2626 RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id);
2627 _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), true, false));
2629 begin_reversible_command (Operations::drag_region_brush);
2632 /** Start a grab where a time range is selected, track(s) are selected, and the
2633 * user clicks and drags a region with a modifier in order to create a new region containing
2634 * the section of the clicked region that lies within the time range.
2637 Editor::start_selection_grab (ArdourCanvas::Item* /*item*/, GdkEvent* event)
2639 if (clicked_regionview == 0) {
2643 /* lets try to create new Region for the selection */
2645 vector<boost::shared_ptr<Region> > new_regions;
2646 create_region_from_selection (new_regions);
2648 if (new_regions.empty()) {
2652 /* XXX fix me one day to use all new regions */
2654 boost::shared_ptr<Region> region (new_regions.front());
2656 /* add it to the current stream/playlist.
2658 tricky: the streamview for the track will add a new regionview. we will
2659 catch the signal it sends when it creates the regionview to
2660 set the regionview we want to then drag.
2663 latest_regionviews.clear();
2664 sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
2666 /* A selection grab currently creates two undo/redo operations, one for
2667 creating the new region and another for moving it.
2670 begin_reversible_command (Operations::selection_grab);
2672 boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
2674 playlist->clear_changes ();
2675 clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
2676 _session->add_command(new StatefulDiffCommand (playlist));
2678 commit_reversible_command ();
2682 if (latest_regionviews.empty()) {
2683 /* something went wrong */
2687 /* we need to deselect all other regionviews, and select this one
2688 i'm ignoring undo stuff, because the region creation will take care of it
2690 selection->set (latest_regionviews);
2692 _drags->set (new RegionMoveDrag (this, latest_regionviews.front()->get_canvas_group(), latest_regionviews.front(), latest_regionviews, false, false), event);
2698 if (_drags->active ()) {
2701 selection->clear ();
2706 Editor::set_internal_edit (bool yn)
2708 if (_internal_editing == yn) {
2712 _internal_editing = yn;
2715 pre_internal_mouse_mode = mouse_mode;
2716 pre_internal_snap_type = _snap_type;
2717 pre_internal_snap_mode = _snap_mode;
2719 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2720 (*i)->enter_internal_edit_mode ();
2723 set_snap_to (internal_snap_type);
2724 set_snap_mode (internal_snap_mode);
2728 internal_snap_mode = _snap_mode;
2729 internal_snap_type = _snap_type;
2731 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2732 (*i)->leave_internal_edit_mode ();
2735 if (mouse_mode == MouseDraw && pre_internal_mouse_mode != MouseDraw) {
2736 /* we were drawing .. flip back to something sensible */
2737 set_mouse_mode (pre_internal_mouse_mode);
2740 set_snap_to (pre_internal_snap_type);
2741 set_snap_mode (pre_internal_snap_mode);
2744 set_canvas_cursor ();
2747 /** Update _join_object_range_state which indicate whether we are over the top or bottom half of a region view,
2748 * used by the `join object/range' tool mode.
2751 Editor::update_join_object_range_location (double /*x*/, double y)
2753 /* XXX: actually, this decides based on whether the mouse is in the top
2754 or bottom half of a the waveform part RouteTimeAxisView;
2756 Note that entered_{track,regionview} is not always setup (e.g. if
2757 the mouse is over a TimeSelection), and to get a Region
2758 that we're over requires searching the playlist.
2761 if (!smart_mode_action->get_active() || (mouse_mode != MouseRange && mouse_mode != MouseObject)) {
2762 _join_object_range_state = JOIN_OBJECT_RANGE_NONE;
2766 if (mouse_mode == MouseObject) {
2767 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2768 } else if (mouse_mode == MouseRange) {
2769 _join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
2772 /* XXX: maybe we should make entered_track work in all cases, rather than resorting to this */
2773 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y + vertical_adjustment.get_value() - canvas_timebars_vsize);
2777 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
2782 rtv->canvas_display()->w2i (cx, cy);
2784 double const c = cy / (rtv->view()->child_height() - TimeAxisViewItem::NAME_HIGHLIGHT_SIZE);
2786 double const f = modf (c, &d);
2788 _join_object_range_state = f < 0.5 ? JOIN_OBJECT_RANGE_RANGE : JOIN_OBJECT_RANGE_OBJECT;
2794 Editor::effective_mouse_mode () const
2796 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
2798 } else if (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE) {
2806 Editor::remove_midi_note (ArdourCanvas::Item* item, GdkEvent *)
2808 ArdourCanvas::CanvasNoteEvent* e = dynamic_cast<ArdourCanvas::CanvasNoteEvent*> (item);
2811 e->region_view().delete_note (e->note ());
2815 Editor::set_canvas_cursor_for_region_view (double x, RegionView* rv)
2819 ArdourCanvas::Group* g = rv->get_canvas_group ();
2820 ArdourCanvas::Group* p = g->get_parent_group ();
2822 /* Compute x in region view parent coordinates */
2826 double x1, x2, y1, y2;
2827 g->get_bounds (x1, y1, x2, y2);
2829 /* Halfway across the region */
2830 double const h = (x1 + x2) / 2;
2832 Trimmable::CanTrim ct = rv->region()->can_trim ();
2834 if (ct & Trimmable::FrontTrimEarlier) {
2835 set_canvas_cursor (_cursors->left_side_trim);
2837 set_canvas_cursor (_cursors->left_side_trim_right_only);
2840 if (ct & Trimmable::EndTrimLater) {
2841 set_canvas_cursor (_cursors->right_side_trim);
2843 set_canvas_cursor (_cursors->right_side_trim_left_only);
2848 /** Obtain the pointer position in world coordinates */
2850 Editor::get_pointer_position (double& x, double& y) const
2853 track_canvas->get_pointer (px, py);
2854 track_canvas->window_to_world (px, py, x, y);