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/audioregion.h"
66 #include "ardour/operations.h"
67 #include "ardour/playlist.h"
68 #include "ardour/profile.h"
69 #include "ardour/region_factory.h"
70 #include "ardour/route.h"
71 #include "ardour/session.h"
72 #include "ardour/types.h"
79 using namespace ARDOUR;
82 using namespace Editing;
83 using Gtkmm2ext::Keyboard;
86 Editor::mouse_frame (framepos_t& where, bool& in_track_canvas) const
88 /* gdk_window_get_pointer() has X11's XQueryPointer semantics in that it only
89 pays attentions to subwindows. this means that menu windows are ignored, and
90 if the pointer is in a menu, the return window from the call will be the
91 the regular subwindow *under* the menu.
93 this matters quite a lot if the pointer is moving around in a menu that overlaps
94 the track canvas because we will believe that we are within the track canvas
95 when we are not. therefore, we track enter/leave events for the track canvas
96 and allow that to override the result of gdk_window_get_pointer().
99 if (!within_track_canvas) {
105 Gdk::ModifierType mask;
106 Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->track_canvas->get_window();
107 Glib::RefPtr<const Gdk::Window> pointer_window;
109 if (!canvas_window) {
113 pointer_window = canvas_window->get_pointer (x, y, mask);
115 if (pointer_window == track_canvas->get_bin_window()) {
118 in_track_canvas = true;
121 in_track_canvas = false;
126 event.type = GDK_BUTTON_RELEASE;
130 where = event_frame (&event, 0, 0);
135 Editor::event_frame (GdkEvent const * event, double* pcx, double* pcy) const
149 switch (event->type) {
150 case GDK_BUTTON_RELEASE:
151 case GDK_BUTTON_PRESS:
152 case GDK_2BUTTON_PRESS:
153 case GDK_3BUTTON_PRESS:
154 *pcx = event->button.x;
155 *pcy = event->button.y;
156 _trackview_group->w2i(*pcx, *pcy);
158 case GDK_MOTION_NOTIFY:
159 *pcx = event->motion.x;
160 *pcy = event->motion.y;
161 _trackview_group->w2i(*pcx, *pcy);
163 case GDK_ENTER_NOTIFY:
164 case GDK_LEAVE_NOTIFY:
165 track_canvas->w2c(event->crossing.x, event->crossing.y, *pcx, *pcy);
168 case GDK_KEY_RELEASE:
169 // track_canvas->w2c(event->key.x, event->key.y, *pcx, *pcy);
172 warning << string_compose (_("Editor::event_frame() used on unhandled event type %1"), event->type) << endmsg;
176 /* note that pixel_to_frame() never returns less than zero, so even if the pixel
177 position is negative (as can be the case with motion events in particular),
178 the frame location is always positive.
181 return pixel_to_frame (*pcx);
185 Editor::which_grabber_cursor ()
187 Gdk::Cursor* c = _cursors->grabber;
189 if (_internal_editing) {
190 switch (mouse_mode) {
192 c = _cursors->midi_pencil;
196 c = _cursors->grabber_note;
200 c = _cursors->midi_resize;
209 switch (_edit_point) {
211 c = _cursors->grabber_edit_point;
214 boost::shared_ptr<Movable> m = _movable.lock();
215 if (m && m->locked()) {
216 c = _cursors->speaker;
226 Editor::set_current_trimmable (boost::shared_ptr<Trimmable> t)
228 boost::shared_ptr<Trimmable> st = _trimmable.lock();
230 if (!st || st == t) {
232 set_canvas_cursor ();
237 Editor::set_current_movable (boost::shared_ptr<Movable> m)
239 boost::shared_ptr<Movable> sm = _movable.lock();
241 if (!sm || sm != m) {
243 set_canvas_cursor ();
248 Editor::set_canvas_cursor ()
250 switch (mouse_mode) {
252 current_canvas_cursor = _cursors->selector;
256 current_canvas_cursor = which_grabber_cursor();
260 current_canvas_cursor = _cursors->midi_pencil;
264 current_canvas_cursor = _cursors->cross_hair;
268 if (Keyboard::the_keyboard().key_is_down (GDK_Control_L)) {
269 current_canvas_cursor = _cursors->zoom_out;
271 current_canvas_cursor = _cursors->zoom_in;
276 current_canvas_cursor = _cursors->time_fx; // just use playhead
280 current_canvas_cursor = _cursors->speaker;
284 switch (_join_object_range_state) {
285 case JOIN_OBJECT_RANGE_NONE:
287 case JOIN_OBJECT_RANGE_OBJECT:
288 current_canvas_cursor = which_grabber_cursor ();
290 case JOIN_OBJECT_RANGE_RANGE:
291 current_canvas_cursor = _cursors->selector;
295 /* up-down cursor as a cue that automation can be dragged up and down when in join object/range mode */
296 if (smart_mode_action->get_active()) {
298 get_pointer_position (x, y);
299 ArdourCanvas::Item* i = track_canvas->get_item_at (x, y);
300 if (i && i->property_parent() && (*i->property_parent()).get_data (X_("timeselection"))) {
301 pair<TimeAxisView*, int> tvp = trackview_by_y_position (_last_motion_y + vertical_adjustment.get_value() - canvas_timebars_vsize);
302 if (dynamic_cast<AutomationTimeAxisView*> (tvp.first)) {
303 current_canvas_cursor = _cursors->up_down;
308 set_canvas_cursor (current_canvas_cursor, true);
312 Editor::set_mouse_mode (MouseMode m, bool force)
314 if (_drags->active ()) {
318 if (!force && m == mouse_mode) {
322 Glib::RefPtr<Action> act;
326 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-range"));
330 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-object"));
334 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-draw"));
338 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-gain"));
342 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-zoom"));
346 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-timefx"));
350 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-audition"));
356 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
359 /* go there and back to ensure that the toggled handler is called to set up mouse_mode */
360 tact->set_active (false);
361 tact->set_active (true);
363 MouseModeChanged (); /* EMIT SIGNAL */
367 Editor::mouse_mode_toggled (MouseMode m)
369 Glib::RefPtr<Action> act;
370 Glib::RefPtr<ToggleAction> tact;
374 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-range"));
378 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-object"));
382 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-draw"));
386 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-gain"));
390 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-zoom"));
394 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-timefx"));
398 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-audition"));
404 tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
407 if (!tact->get_active()) {
408 /* this was just the notification that the old mode has been
409 * left. we'll get called again with the new mode active in a
417 act = ActionManager::get_action (X_("MouseMode"), X_("toggle-internal-edit"));
418 tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
419 tact->set_active (true);
425 if (_session && mouse_mode == MouseAudition) {
426 /* stop transport and reset default speed to avoid oddness with
428 _session->request_transport_speed (0.0, true);
435 if (!internal_editing()) {
436 if (mouse_mode != MouseRange && mouse_mode != MouseGain && _join_object_range_state == JOIN_OBJECT_RANGE_NONE) {
438 /* in all modes except range, gain and joined object/range, hide the range selection,
439 show the object (region) selection.
442 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
443 (*i)->hide_selection ();
449 in range or object/range mode, show the range selection.
452 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
453 (*i)->show_selection (selection->time);
458 set_canvas_cursor ();
459 set_gain_envelope_visibility ();
461 MouseModeChanged (); /* EMIT SIGNAL */
465 Editor::step_mouse_mode (bool next)
467 switch (current_mouse_mode()) {
470 if (Profile->get_sae()) {
471 set_mouse_mode (MouseZoom);
473 set_mouse_mode (MouseRange);
476 set_mouse_mode (MouseTimeFX);
481 if (next) set_mouse_mode (MouseDraw);
482 else set_mouse_mode (MouseObject);
486 if (next) set_mouse_mode (MouseZoom);
487 else set_mouse_mode (MouseRange);
492 if (Profile->get_sae()) {
493 set_mouse_mode (MouseTimeFX);
495 set_mouse_mode (MouseGain);
498 if (Profile->get_sae()) {
499 set_mouse_mode (MouseObject);
501 set_mouse_mode (MouseDraw);
507 if (next) set_mouse_mode (MouseTimeFX);
508 else set_mouse_mode (MouseZoom);
513 set_mouse_mode (MouseAudition);
515 if (Profile->get_sae()) {
516 set_mouse_mode (MouseZoom);
518 set_mouse_mode (MouseGain);
524 if (next) set_mouse_mode (MouseObject);
525 else set_mouse_mode (MouseTimeFX);
531 Editor::toggle_internal_editing_from_double_click (GdkEvent* event)
533 if (_drags->active()) {
534 _drags->end_grab (event);
537 ActionManager::do_action ("MouseMode", "toggle-internal-edit");
539 /* prevent reversion of edit cursor on button release */
541 pre_press_cursor = 0;
547 Editor::button_selection (ArdourCanvas::Item* /*item*/, GdkEvent* event, ItemType item_type)
549 /* in object/audition/timefx/gain-automation mode,
550 any button press sets the selection if the object
551 can be selected. this is a bit of hack, because
552 we want to avoid this if the mouse operation is a
555 note: not dbl-click or triple-click
557 Also note that there is no region selection in internal edit mode, otherwise
558 for operations operating on the selection (e.g. cut) it is not obvious whether
559 to cut notes or regions.
562 if (((mouse_mode != MouseObject) &&
563 (_join_object_range_state != JOIN_OBJECT_RANGE_OBJECT) &&
564 (mouse_mode != MouseAudition || item_type != RegionItem) &&
565 (mouse_mode != MouseTimeFX || item_type != RegionItem) &&
566 (mouse_mode != MouseGain) &&
567 (mouse_mode != MouseRange) &&
568 (mouse_mode != MouseDraw)) ||
569 ((event->type != GDK_BUTTON_PRESS && event->type != GDK_BUTTON_RELEASE) || event->button.button > 3) ||
570 (internal_editing() && mouse_mode != MouseTimeFX)) {
575 if (event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) {
577 if ((event->button.state & Keyboard::RelevantModifierKeyMask) && event->button.button != 1) {
579 /* almost no selection action on modified button-2 or button-3 events */
581 if (item_type != RegionItem && event->button.button != 2) {
587 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
588 bool press = (event->type == GDK_BUTTON_PRESS);
592 if (!doing_range_stuff()) {
593 set_selected_regionview_from_click (press, op);
597 if (doing_range_stuff()) {
598 /* don't change the selection unless the
599 clicked track is not currently selected. if
600 so, "collapse" the selection to just this
603 if (!selection->selected (clicked_axisview)) {
604 set_selected_track_as_side_effect (Selection::Set);
610 case RegionViewNameHighlight:
612 case LeftFrameHandle:
613 case RightFrameHandle:
614 if (doing_object_stuff() || (mouse_mode != MouseRange && mouse_mode != MouseObject)) {
615 set_selected_regionview_from_click (press, op);
616 } else if (event->type == GDK_BUTTON_PRESS) {
617 set_selected_track_as_side_effect (op);
621 case FadeInHandleItem:
623 case FadeOutHandleItem:
625 case StartCrossFadeItem:
626 case EndCrossFadeItem:
627 if (doing_object_stuff() || (mouse_mode != MouseRange && mouse_mode != MouseObject)) {
628 set_selected_regionview_from_click (press, op);
629 } else if (event->type == GDK_BUTTON_PRESS) {
630 set_selected_track_as_side_effect (op);
634 case ControlPointItem:
635 set_selected_track_as_side_effect (op);
636 if (doing_object_stuff() || (mouse_mode != MouseRange && mouse_mode != MouseObject)) {
637 set_selected_control_point_from_click (press, op);
642 /* for context click, select track */
643 if (event->button.button == 3) {
644 selection->clear_tracks ();
645 set_selected_track_as_side_effect (op);
649 case AutomationTrackItem:
650 set_selected_track_as_side_effect (op);
659 Editor::button_press_handler_1 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
661 /* single mouse clicks on any of these item types operate
662 independent of mouse mode, mostly because they are
663 not on the main track canvas or because we want
668 case PlayheadCursorItem:
669 _drags->set (new CursorDrag (this, item, true), event);
673 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
674 hide_marker (item, event);
676 _drags->set (new MarkerDrag (this, item), event);
680 case TempoMarkerItem:
682 TempoMarker* m = reinterpret_cast<TempoMarker*> (item->get_data ("marker"));
684 if (m->tempo().movable ()) {
686 new TempoMarkerDrag (
689 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
699 case MeterMarkerItem:
701 MeterMarker* m = reinterpret_cast<MeterMarker*> (item->get_data ("marker"));
703 if (m->meter().movable ()) {
705 new MeterMarkerDrag (
708 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
721 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
722 _drags->set (new CursorDrag (this, &playhead_cursor->canvas_item, false), event);
728 case RangeMarkerBarItem:
729 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
730 _drags->set (new CursorDrag (this, &playhead_cursor->canvas_item, false), event);
732 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateRangeMarker), event);
737 case CdMarkerBarItem:
738 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
739 _drags->set (new CursorDrag (this, &playhead_cursor->canvas_item, false), event);
741 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateCDMarker), event);
746 case TransportMarkerBarItem:
747 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
748 _drags->set (new CursorDrag (this, &playhead_cursor->canvas_item, false), event);
750 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateTransportMarker), event);
759 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
760 /* special case: allow trim of range selections in joined object mode;
761 in theory eff should equal MouseRange in this case, but it doesn't
762 because entering the range selection canvas item results in entered_regionview
763 being set to 0, so update_join_object_range_location acts as if we aren't
766 if (item_type == StartSelectionTrimItem) {
767 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
768 } else if (item_type == EndSelectionTrimItem) {
769 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
773 Editing::MouseMode eff = effective_mouse_mode ();
775 /* special case: allow drag of region fade in/out in object mode with join object/range enabled */
776 if (item_type == FadeInHandleItem || item_type == FadeOutHandleItem) {
783 case StartSelectionTrimItem:
784 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
787 case EndSelectionTrimItem:
788 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
792 if (Keyboard::modifier_state_contains
793 (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier))) {
794 // contains and not equals because I can't use alt as a modifier alone.
795 start_selection_grab (item, event);
796 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
797 /* grab selection for moving */
798 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionMove), event);
800 double const y = event->button.y + vertical_adjustment.get_value() - canvas_timebars_vsize;
801 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
803 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
804 if (smart_mode_action->get_active() && atv) {
805 /* smart "join" mode: drag automation */
806 _drags->set (new AutomationRangeDrag (this, atv, selection->time), event, _cursors->up_down);
808 /* this was debated, but decided the more common action was to
809 make a new selection */
810 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
817 if (internal_editing()) {
818 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
819 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
823 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
828 case RegionViewNameHighlight:
829 if (!clicked_regionview->region()->locked()) {
830 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
831 _drags->set (new TrimDrag (this, item, clicked_regionview, s.by_layer()), event);
836 case LeftFrameHandle:
837 case RightFrameHandle:
838 if (!internal_editing() && doing_object_stuff() && !clicked_regionview->region()->locked()) {
839 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
840 _drags->set (new TrimDrag (this, item, clicked_regionview, s.by_layer()), event);
846 if (!internal_editing()) {
847 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
856 if (internal_editing()) {
857 /* trim notes if we're in internal edit mode and near the ends of the note */
858 ArdourCanvas::CanvasNote* cn = dynamic_cast<ArdourCanvas::CanvasNote*> (item);
859 if (cn && cn->big_enough_to_trim() && cn->mouse_near_ends()) {
860 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
862 _drags->set (new NoteDrag (this, item), event);
876 if (internal_editing()) {
877 ArdourCanvas::CanvasNoteEvent* cn = dynamic_cast<ArdourCanvas::CanvasNoteEvent*> (item);
878 if (cn->mouse_near_ends()) {
879 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
881 _drags->set (new NoteDrag (this, item), event);
891 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)) &&
892 event->type == GDK_BUTTON_PRESS) {
894 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
896 } else if (event->type == GDK_BUTTON_PRESS) {
899 case FadeInHandleItem:
901 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
902 _drags->set (new FadeInDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), s), event, _cursors->fade_in);
906 case FadeOutHandleItem:
908 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
909 _drags->set (new FadeOutDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), s), event, _cursors->fade_out);
913 case StartCrossFadeItem:
914 _drags->set (new CrossfadeEdgeDrag (this, reinterpret_cast<AudioRegionView*>(item->get_data("regionview")), item, true), event, 0);
917 case EndCrossFadeItem:
918 _drags->set (new CrossfadeEdgeDrag (this, reinterpret_cast<AudioRegionView*>(item->get_data("regionview")), item, false), event, 0);
921 case FeatureLineItem:
923 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier)) {
924 remove_transient(item);
928 _drags->set (new FeatureLineDrag (this, item), event);
934 if (dynamic_cast<AutomationRegionView*> (clicked_regionview)) {
935 /* click on an automation region view; do nothing here and let the ARV's signal handler
941 if (internal_editing ()) {
942 if (event->type == GDK_2BUTTON_PRESS && event->button.button == 1) {
943 Glib::RefPtr<Action> act = ActionManager::get_action (X_("MouseMode"), X_("toggle-internal-edit"));
949 /* click on a normal region view */
950 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
951 add_region_copy_drag (item, event, clicked_regionview);
952 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
953 add_region_brush_drag (item, event, clicked_regionview);
955 add_region_drag (item, event, clicked_regionview);
959 if (!internal_editing() && (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE && !selection->regions.empty())) {
960 _drags->add (new SelectionDrag (this, clicked_axisview->get_selection_rect (clicked_selection)->rect, SelectionDrag::SelectionMove));
963 _drags->start_grab (event);
966 case RegionViewNameHighlight:
967 case LeftFrameHandle:
968 case RightFrameHandle:
969 if (!clicked_regionview->region()->locked()) {
970 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
971 _drags->set (new TrimDrag (this, item, clicked_regionview, s.by_layer()), event);
978 /* rename happens on edit clicks */
979 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
980 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, s.by_layer()), event);
985 case ControlPointItem:
986 _drags->set (new ControlPointDrag (this, item), event);
990 case AutomationLineItem:
991 _drags->set (new LineDrag (this, item), event);
996 if (internal_editing()) {
997 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
998 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
1002 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
1006 case AutomationTrackItem:
1008 TimeAxisView* parent = clicked_axisview->get_parent ();
1009 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (clicked_axisview);
1011 if (parent && dynamic_cast<MidiTimeAxisView*> (parent) && atv->show_regions ()) {
1013 RouteTimeAxisView* p = dynamic_cast<RouteTimeAxisView*> (parent);
1015 boost::shared_ptr<Playlist> pl = p->track()->playlist ();
1016 if (pl->n_regions() == 0) {
1017 /* Parent has no regions; create one so that we have somewhere to put automation */
1018 _drags->set (new RegionCreateDrag (this, item, parent), event);
1020 /* See if there's a region before the click that we can extend, and extend it if so */
1021 framepos_t const t = event_frame (event);
1022 boost::shared_ptr<Region> prev = pl->find_next_region (t, End, -1);
1024 _drags->set (new RegionCreateDrag (this, item, parent), event);
1026 prev->set_length (t - prev->position ());
1030 /* rubberband drag to select automation points */
1031 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
1038 if (smart_mode_action->get_active()) {
1039 /* we're in "smart" joined mode, and we've clicked on a Selection */
1040 double const y = event->button.y + vertical_adjustment.get_value() - canvas_timebars_vsize;
1041 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
1043 /* if we're over an automation track, start a drag of its data */
1044 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
1046 _drags->set (new AutomationRangeDrag (this, atv, selection->time), event, _cursors->up_down);
1049 /* if we're over a track and a region, and in the `object' part of a region,
1050 put a selection around the region and drag both
1052 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1053 if (rtv && _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
1054 boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track> (rtv->route ());
1056 boost::shared_ptr<Playlist> pl = t->playlist ();
1059 boost::shared_ptr<Region> r = pl->top_region_at (event_frame (event));
1061 RegionView* rv = rtv->view()->find_view (r);
1062 clicked_selection = select_range (rv->region()->position(),
1063 rv->region()->last_frame()+1);
1064 _drags->add (new SelectionDrag (this, item, SelectionDrag::SelectionMove));
1065 list<RegionView*> rvs;
1067 _drags->add (new RegionMoveDrag (this, item, rv, rvs, false, false));
1068 _drags->start_grab (event);
1079 case ImageFrameHandleStartItem:
1080 imageframe_start_handle_op(item, event) ;
1083 case ImageFrameHandleEndItem:
1084 imageframe_end_handle_op(item, event) ;
1087 case MarkerViewHandleStartItem:
1088 markerview_item_start_handle_op(item, event) ;
1091 case MarkerViewHandleEndItem:
1092 markerview_item_end_handle_op(item, event) ;
1095 case MarkerViewItem:
1096 start_markerview_grab(item, event) ;
1098 case ImageFrameItem:
1099 start_imageframe_grab(item, event) ;
1115 switch (item_type) {
1117 _drags->set (new LineDrag (this, item), event);
1120 case ControlPointItem:
1121 _drags->set (new ControlPointDrag (this, item), event);
1127 AudioRegionView* arv = dynamic_cast<AudioRegionView *> (clicked_regionview);
1129 _drags->set (new AutomationRangeDrag (this, arv, selection->time), event, _cursors->up_down);
1130 _drags->start_grab (event);
1136 case AutomationLineItem:
1137 _drags->set (new LineDrag (this, item), event);
1147 if (event->type == GDK_BUTTON_PRESS) {
1148 _drags->set (new MouseZoomDrag (this, item), event);
1155 if (internal_editing() && item_type == NoteItem) {
1156 /* drag notes if we're in internal edit mode */
1157 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
1159 } else if (clicked_regionview) {
1161 _drags->set (new TimeFXDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1167 _drags->set (new ScrubDrag (this, item), event);
1168 scrub_reversals = 0;
1169 scrub_reverse_distance = 0;
1170 last_scrub_x = event->button.x;
1171 scrubbing_direction = 0;
1172 set_canvas_cursor (_cursors->transparent);
1184 Editor::button_press_handler_2 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1186 Editing::MouseMode const eff = effective_mouse_mode ();
1189 switch (item_type) {
1191 if (internal_editing ()) {
1192 /* no region drags in internal edit mode */
1196 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
1197 add_region_copy_drag (item, event, clicked_regionview);
1199 add_region_drag (item, event, clicked_regionview);
1201 _drags->start_grab (event);
1204 case ControlPointItem:
1205 _drags->set (new ControlPointDrag (this, item), event);
1213 switch (item_type) {
1214 case RegionViewNameHighlight:
1215 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1219 case LeftFrameHandle:
1220 case RightFrameHandle:
1221 if (!internal_editing ()) {
1222 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1227 case RegionViewName:
1228 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer()), event);
1242 /* relax till release */
1248 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1249 temporal_zoom_to_frame (false, event_frame (event));
1251 temporal_zoom_to_frame (true, event_frame(event));
1264 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1266 if (event->type != GDK_BUTTON_PRESS) {
1270 Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->track_canvas->get_window();
1272 if (canvas_window) {
1273 Glib::RefPtr<const Gdk::Window> pointer_window;
1276 Gdk::ModifierType mask;
1278 pointer_window = canvas_window->get_pointer (x, y, mask);
1280 if (pointer_window == track_canvas->get_bin_window()) {
1281 track_canvas->window_to_world (x, y, wx, wy);
1285 pre_press_cursor = current_canvas_cursor;
1287 track_canvas->grab_focus();
1289 if (_session && _session->actively_recording()) {
1295 if (internal_editing()) {
1296 bool leave_internal_edit_mode = false;
1298 switch (item_type) {
1303 if (!dynamic_cast<MidiRegionView*> (clicked_regionview) && !dynamic_cast<AutomationRegionView*> (clicked_regionview)) {
1304 leave_internal_edit_mode = true;
1308 case PlayheadCursorItem:
1310 case TempoMarkerItem:
1311 case MeterMarkerItem:
1315 case RangeMarkerBarItem:
1316 case CdMarkerBarItem:
1317 case TransportMarkerBarItem:
1318 /* button press on these events never does anything to
1319 change the editing mode.
1324 leave_internal_edit_mode = true;
1331 if (leave_internal_edit_mode) {
1332 ActionManager::do_action ("MouseMode", "toggle-internal-edit");
1336 button_selection (item, event, item_type);
1338 if (!_drags->active () &&
1339 (Keyboard::is_delete_event (&event->button) ||
1340 Keyboard::is_context_menu_event (&event->button) ||
1341 Keyboard::is_edit_event (&event->button))) {
1343 /* handled by button release */
1347 switch (event->button.button) {
1349 return button_press_handler_1 (item, event, item_type);
1353 return button_press_handler_2 (item, event, item_type);
1360 return button_press_dispatch (&event->button);
1369 Editor::button_press_dispatch (GdkEventButton* ev)
1371 /* this function is intended only for buttons 4 and above.
1374 Gtkmm2ext::MouseButton b (ev->state, ev->button);
1375 return button_bindings->activate (b, Gtkmm2ext::Bindings::Press);
1379 Editor::button_release_dispatch (GdkEventButton* ev)
1381 /* this function is intended only for buttons 4 and above.
1384 Gtkmm2ext::MouseButton b (ev->state, ev->button);
1385 return button_bindings->activate (b, Gtkmm2ext::Bindings::Release);
1389 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1391 framepos_t where = event_frame (event, 0, 0);
1392 AutomationTimeAxisView* atv = 0;
1394 if (pre_press_cursor) {
1395 set_canvas_cursor (pre_press_cursor);
1396 pre_press_cursor = 0;
1399 /* no action if we're recording */
1401 if (_session && _session->actively_recording()) {
1405 /* see if we're finishing a drag */
1407 bool were_dragging = false;
1408 if (_drags->active ()) {
1409 bool const r = _drags->end_grab (event);
1411 /* grab dragged, so do nothing else */
1415 were_dragging = true;
1418 update_region_layering_order_editor ();
1420 /* edit events get handled here */
1422 if (!_drags->active () && Keyboard::is_edit_event (&event->button)) {
1423 switch (item_type) {
1425 show_region_properties ();
1428 case TempoMarkerItem:
1429 edit_tempo_marker (item);
1432 case MeterMarkerItem:
1433 edit_meter_marker (item);
1436 case RegionViewName:
1437 if (clicked_regionview->name_active()) {
1438 return mouse_rename_region (item, event);
1442 case ControlPointItem:
1443 edit_control_point (item);
1448 ArdourCanvas::CanvasNoteEvent* e = dynamic_cast<ArdourCanvas::CanvasNoteEvent*> (item);
1450 edit_notes (e->region_view().selection ());
1460 /* context menu events get handled here */
1462 if (Keyboard::is_context_menu_event (&event->button)) {
1464 context_click_event = *event;
1466 if (!_drags->active ()) {
1468 /* no matter which button pops up the context menu, tell the menu
1469 widget to use button 1 to drive menu selection.
1472 switch (item_type) {
1474 case FadeInHandleItem:
1476 case FadeOutHandleItem:
1477 popup_fade_context_menu (1, event->button.time, item, item_type);
1480 case StartCrossFadeItem:
1481 popup_xfade_in_context_menu (1, event->button.time, item, item_type);
1484 case EndCrossFadeItem:
1485 popup_xfade_out_context_menu (1, event->button.time, item, item_type);
1489 popup_track_context_menu (1, event->button.time, item_type, false);
1493 case RegionViewNameHighlight:
1494 case LeftFrameHandle:
1495 case RightFrameHandle:
1496 case RegionViewName:
1497 popup_track_context_menu (1, event->button.time, item_type, false);
1501 popup_track_context_menu (1, event->button.time, item_type, true);
1504 case AutomationTrackItem:
1505 popup_track_context_menu (1, event->button.time, item_type, false);
1509 case RangeMarkerBarItem:
1510 case TransportMarkerBarItem:
1511 case CdMarkerBarItem:
1514 popup_ruler_menu (where, item_type);
1518 marker_context_menu (&event->button, item);
1521 case TempoMarkerItem:
1522 tempo_or_meter_marker_context_menu (&event->button, item);
1525 case MeterMarkerItem:
1526 tempo_or_meter_marker_context_menu (&event->button, item);
1529 case CrossfadeViewItem:
1530 popup_track_context_menu (1, event->button.time, item_type, false);
1533 case ControlPointItem:
1534 popup_control_point_context_menu (item, event);
1538 case ImageFrameItem:
1539 popup_imageframe_edit_menu(1, event->button.time, item, true) ;
1541 case ImageFrameTimeAxisItem:
1542 popup_imageframe_edit_menu(1, event->button.time, item, false) ;
1544 case MarkerViewItem:
1545 popup_marker_time_axis_edit_menu(1, event->button.time, item, true) ;
1547 case MarkerTimeAxisItem:
1548 popup_marker_time_axis_edit_menu(1, event->button.time, item, false) ;
1560 /* delete events get handled here */
1562 Editing::MouseMode const eff = effective_mouse_mode ();
1564 if (!_drags->active () && Keyboard::is_delete_event (&event->button)) {
1566 switch (item_type) {
1567 case TempoMarkerItem:
1568 remove_tempo_marker (item);
1571 case MeterMarkerItem:
1572 remove_meter_marker (item);
1576 remove_marker (*item, event);
1580 if (eff == MouseObject) {
1581 remove_clicked_region ();
1585 case ControlPointItem:
1586 remove_control_point (item);
1590 remove_midi_note (item, event);
1599 switch (event->button.button) {
1602 switch (item_type) {
1603 /* see comments in button_press_handler */
1604 case PlayheadCursorItem:
1607 case AutomationLineItem:
1608 case StartSelectionTrimItem:
1609 case EndSelectionTrimItem:
1613 if (!_dragging_playhead) {
1614 snap_to_with_modifier (where, event, 0, true);
1615 mouse_add_new_marker (where);
1619 case CdMarkerBarItem:
1620 if (!_dragging_playhead) {
1621 // if we get here then a dragged range wasn't done
1622 snap_to_with_modifier (where, event, 0, true);
1623 mouse_add_new_marker (where, true);
1628 if (!_dragging_playhead) {
1629 snap_to_with_modifier (where, event);
1630 mouse_add_new_tempo_event (where);
1635 if (!_dragging_playhead) {
1636 mouse_add_new_meter_event (pixel_to_frame (event->button.x));
1647 switch (item_type) {
1648 case AutomationTrackItem:
1649 atv = dynamic_cast<AutomationTimeAxisView*>(clicked_axisview);
1651 atv->add_automation_event (event, where, event->button.y);
1661 switch (item_type) {
1664 /* check that we didn't drag before releasing, since
1665 its really annoying to create new control
1666 points when doing this.
1668 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (clicked_regionview);
1669 if (!were_dragging && arv) {
1670 arv->add_gain_point_event (item, event);
1676 case AutomationTrackItem:
1677 dynamic_cast<AutomationTimeAxisView*>(clicked_axisview)->
1678 add_automation_event (event, where, event->button.y);
1687 set_canvas_cursor (current_canvas_cursor);
1688 if (scrubbing_direction == 0) {
1689 /* no drag, just a click */
1690 switch (item_type) {
1692 play_selected_region ();
1698 /* make sure we stop */
1699 _session->request_transport_speed (0.0);
1708 /* do any (de)selection operations that should occur on button release */
1709 button_selection (item, event, item_type);
1718 switch (item_type) {
1720 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1722 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier|Keyboard::SecondaryModifier))) {
1725 // Button2 click is unused
1740 // x_style_paste (where, 1.0);
1761 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1768 switch (item_type) {
1769 case ControlPointItem:
1770 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1771 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1772 cp->set_visible (true);
1776 at_y = cp->get_y ();
1777 cp->i2w (at_x, at_y);
1781 fraction = 1.0 - (cp->get_y() / cp->line().height());
1783 if (is_drawable() && !_drags->active ()) {
1784 set_canvas_cursor (_cursors->fader);
1787 _verbose_cursor->set (cp->line().get_verbose_cursor_string (fraction), at_x, at_y);
1788 _verbose_cursor->show ();
1793 if (mouse_mode == MouseGain) {
1794 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1796 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredGainLine.get();
1797 if (is_drawable()) {
1798 set_canvas_cursor (_cursors->fader);
1803 case AutomationLineItem:
1804 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1805 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1807 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredAutomationLine.get();
1809 if (is_drawable()) {
1810 set_canvas_cursor (_cursors->fader);
1815 case RegionViewNameHighlight:
1816 if (is_drawable() && doing_object_stuff() && entered_regionview) {
1817 set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1818 _over_region_trim_target = true;
1822 case LeftFrameHandle:
1823 case RightFrameHandle:
1824 if (is_drawable() && doing_object_stuff() && !internal_editing() && entered_regionview) {
1825 set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1829 case StartSelectionTrimItem:
1831 case ImageFrameHandleStartItem:
1832 case MarkerViewHandleStartItem:
1834 if (is_drawable()) {
1835 set_canvas_cursor (_cursors->left_side_trim);
1838 case EndSelectionTrimItem:
1840 case ImageFrameHandleEndItem:
1841 case MarkerViewHandleEndItem:
1843 if (is_drawable()) {
1844 set_canvas_cursor (_cursors->right_side_trim);
1848 case PlayheadCursorItem:
1849 if (is_drawable()) {
1850 switch (_edit_point) {
1852 set_canvas_cursor (_cursors->grabber_edit_point);
1855 set_canvas_cursor (_cursors->grabber);
1861 case RegionViewName:
1863 /* when the name is not an active item, the entire name highlight is for trimming */
1865 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1866 if (mouse_mode == MouseObject && is_drawable()) {
1867 set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1868 _over_region_trim_target = true;
1874 case AutomationTrackItem:
1875 if (is_drawable()) {
1876 Gdk::Cursor *cursor;
1877 switch (mouse_mode) {
1879 cursor = _cursors->selector;
1882 cursor = _cursors->zoom_in;
1885 cursor = _cursors->cross_hair;
1889 set_canvas_cursor (cursor);
1891 AutomationTimeAxisView* atv;
1892 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1893 clear_entered_track = false;
1894 set_entered_track (atv);
1900 case RangeMarkerBarItem:
1901 case TransportMarkerBarItem:
1902 case CdMarkerBarItem:
1905 if (is_drawable()) {
1906 set_canvas_cursor (_cursors->timebar);
1911 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1914 entered_marker = marker;
1915 marker->set_color_rgba (ARDOUR_UI::config()->canvasvar_EnteredMarker.get());
1917 case MeterMarkerItem:
1918 case TempoMarkerItem:
1919 if (is_drawable()) {
1920 set_canvas_cursor (_cursors->timebar);
1924 case FadeInHandleItem:
1925 if (mouse_mode == MouseObject && !internal_editing()) {
1926 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1928 rect->property_fill_color_rgba() = 0xBBBBBBAA;
1930 set_canvas_cursor (_cursors->fade_in);
1934 case FadeOutHandleItem:
1935 if (mouse_mode == MouseObject && !internal_editing()) {
1936 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1938 rect->property_fill_color_rgba() = 0xBBBBBBAA;
1940 set_canvas_cursor (_cursors->fade_out);
1943 case FeatureLineItem:
1945 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1946 line->property_fill_color_rgba() = 0xFF0000FF;
1950 if (smart_mode_action->get_active()) {
1951 set_canvas_cursor ();
1959 /* second pass to handle entered track status in a comprehensible way.
1962 switch (item_type) {
1964 case AutomationLineItem:
1965 case ControlPointItem:
1966 /* these do not affect the current entered track state */
1967 clear_entered_track = false;
1970 case AutomationTrackItem:
1971 /* handled above already */
1975 set_entered_track (0);
1983 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent*, ItemType item_type)
1993 switch (item_type) {
1994 case ControlPointItem:
1995 cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
1996 if (cp->line().the_list()->interpolation() != AutomationList::Discrete) {
1997 if (cp->line().npoints() > 1 && !cp->get_selected()) {
1998 cp->set_visible (false);
2002 if (is_drawable()) {
2003 set_canvas_cursor (current_canvas_cursor);
2006 _verbose_cursor->hide ();
2009 case RegionViewNameHighlight:
2010 case LeftFrameHandle:
2011 case RightFrameHandle:
2012 case StartSelectionTrimItem:
2013 case EndSelectionTrimItem:
2014 case PlayheadCursorItem:
2017 case ImageFrameHandleStartItem:
2018 case ImageFrameHandleEndItem:
2019 case MarkerViewHandleStartItem:
2020 case MarkerViewHandleEndItem:
2023 _over_region_trim_target = false;
2025 if (is_drawable()) {
2026 set_canvas_cursor (current_canvas_cursor);
2031 case AutomationLineItem:
2032 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
2034 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
2036 line->property_fill_color_rgba() = al->get_line_color();
2038 if (is_drawable()) {
2039 set_canvas_cursor (current_canvas_cursor);
2043 case RegionViewName:
2044 /* see enter_handler() for notes */
2045 _over_region_trim_target = false;
2047 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
2048 if (is_drawable() && mouse_mode == MouseObject) {
2049 set_canvas_cursor (current_canvas_cursor);
2054 case RangeMarkerBarItem:
2055 case TransportMarkerBarItem:
2056 case CdMarkerBarItem:
2060 if (is_drawable()) {
2061 set_canvas_cursor (current_canvas_cursor);
2066 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2070 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
2071 location_flags_changed (loc, this);
2074 case MeterMarkerItem:
2075 case TempoMarkerItem:
2077 if (is_drawable()) {
2078 set_canvas_cursor (current_canvas_cursor);
2083 case FadeInHandleItem:
2084 case FadeOutHandleItem:
2085 rv = static_cast<RegionView*>(item->get_data ("regionview"));
2087 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
2089 rect->property_fill_color_rgba() = rv->get_fill_color();
2092 set_canvas_cursor (current_canvas_cursor);
2095 case AutomationTrackItem:
2096 if (is_drawable()) {
2097 set_canvas_cursor (current_canvas_cursor);
2098 clear_entered_track = true;
2099 Glib::signal_idle().connect (sigc::mem_fun(*this, &Editor::left_automation_track));
2102 case FeatureLineItem:
2104 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
2105 line->property_fill_color_rgba() = (guint) ARDOUR_UI::config()->canvasvar_ZeroLine.get();;
2117 Editor::left_automation_track ()
2119 if (clear_entered_track) {
2120 set_entered_track (0);
2121 clear_entered_track = false;
2127 Editor::scrub (framepos_t frame, double current_x)
2131 if (scrubbing_direction == 0) {
2133 _session->request_locate (frame, false);
2134 _session->request_transport_speed (0.1);
2135 scrubbing_direction = 1;
2139 if (last_scrub_x > current_x) {
2141 /* pointer moved to the left */
2143 if (scrubbing_direction > 0) {
2145 /* we reversed direction to go backwards */
2148 scrub_reverse_distance += (int) (last_scrub_x - current_x);
2152 /* still moving to the left (backwards) */
2154 scrub_reversals = 0;
2155 scrub_reverse_distance = 0;
2157 delta = 0.01 * (last_scrub_x - current_x);
2158 _session->request_transport_speed_nonzero (_session->transport_speed() - delta);
2162 /* pointer moved to the right */
2164 if (scrubbing_direction < 0) {
2165 /* we reversed direction to go forward */
2168 scrub_reverse_distance += (int) (current_x - last_scrub_x);
2171 /* still moving to the right */
2173 scrub_reversals = 0;
2174 scrub_reverse_distance = 0;
2176 delta = 0.01 * (current_x - last_scrub_x);
2177 _session->request_transport_speed_nonzero (_session->transport_speed() + delta);
2181 /* if there have been more than 2 opposite motion moves detected, or one that moves
2182 back more than 10 pixels, reverse direction
2185 if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
2187 if (scrubbing_direction > 0) {
2188 /* was forwards, go backwards */
2189 _session->request_transport_speed (-0.1);
2190 scrubbing_direction = -1;
2192 /* was backwards, go forwards */
2193 _session->request_transport_speed (0.1);
2194 scrubbing_direction = 1;
2197 scrub_reverse_distance = 0;
2198 scrub_reversals = 0;
2202 last_scrub_x = current_x;
2206 Editor::motion_handler (ArdourCanvas::Item* /*item*/, GdkEvent* event, bool from_autoscroll)
2208 _last_motion_y = event->motion.y;
2210 if (event->motion.is_hint) {
2213 /* We call this so that MOTION_NOTIFY events continue to be
2214 delivered to the canvas. We need to do this because we set
2215 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
2216 the density of the events, at the expense of a round-trip
2217 to the server. Given that this will mostly occur on cases
2218 where DISPLAY = :0.0, and given the cost of what the motion
2219 event might do, its a good tradeoff.
2222 track_canvas->get_pointer (x, y);
2225 if (current_stepping_trackview) {
2226 /* don't keep the persistent stepped trackview if the mouse moves */
2227 current_stepping_trackview = 0;
2228 step_timeout.disconnect ();
2231 if (_session && _session->actively_recording()) {
2232 /* Sorry. no dragging stuff around while we record */
2236 JoinObjectRangeState const old = _join_object_range_state;
2237 update_join_object_range_location (event->motion.x, event->motion.y);
2238 if (_join_object_range_state != old) {
2239 set_canvas_cursor ();
2242 if (_over_region_trim_target) {
2243 set_canvas_cursor_for_region_view (event->motion.x, entered_regionview);
2246 bool handled = false;
2247 if (_drags->active ()) {
2248 handled = _drags->motion_handler (event, from_autoscroll);
2255 track_canvas_motion (event);
2260 Editor::can_remove_control_point (ArdourCanvas::Item* item)
2262 ControlPoint* control_point;
2264 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2265 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2269 AutomationLine& line = control_point->line ();
2270 if (dynamic_cast<AudioRegionGainLine*> (&line)) {
2271 /* we shouldn't remove the first or last gain point in region gain lines */
2272 if (line.is_last_point(*control_point) || line.is_first_point(*control_point)) {
2281 Editor::remove_control_point (ArdourCanvas::Item* item)
2283 if (!can_remove_control_point (item)) {
2287 ControlPoint* control_point;
2289 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2290 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2294 control_point->line().remove_point (*control_point);
2298 Editor::edit_control_point (ArdourCanvas::Item* item)
2300 ControlPoint* p = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"));
2303 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2307 ControlPointDialog d (p);
2308 d.set_position (Gtk::WIN_POS_MOUSE);
2311 if (d.run () != RESPONSE_ACCEPT) {
2315 p->line().modify_point_y (*p, d.get_y_fraction ());
2319 Editor::edit_notes (MidiRegionView::Selection const & s)
2325 EditNoteDialog d (&(*s.begin())->region_view(), s);
2326 d.set_position (Gtk::WIN_POS_MOUSE);
2334 Editor::visible_order_range (int* low, int* high) const
2336 *low = TimeAxisView::max_order ();
2339 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
2341 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
2343 if (!rtv->hidden()) {
2345 if (*high < rtv->order()) {
2346 *high = rtv->order ();
2349 if (*low > rtv->order()) {
2350 *low = rtv->order ();
2357 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
2359 /* Either add to or set the set the region selection, unless
2360 this is an alignment click (control used)
2363 if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
2364 TimeAxisView* tv = &rv.get_time_axis_view();
2365 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
2367 if (rtv && rtv->is_track()) {
2368 speed = rtv->track()->speed();
2371 framepos_t where = get_preferred_edit_position();
2375 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
2377 align_region (rv.region(), SyncPoint, (framepos_t) (where * speed));
2379 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
2381 align_region (rv.region(), End, (framepos_t) (where * speed));
2385 align_region (rv.region(), Start, (framepos_t) (where * speed));
2392 Editor::collect_new_region_view (RegionView* rv)
2394 latest_regionviews.push_back (rv);
2398 Editor::collect_and_select_new_region_view (RegionView* rv)
2401 latest_regionviews.push_back (rv);
2405 Editor::cancel_selection ()
2407 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2408 (*i)->hide_selection ();
2411 selection->clear ();
2412 clicked_selection = 0;
2417 Editor::point_trim (GdkEvent* event, framepos_t new_bound)
2419 RegionView* rv = clicked_regionview;
2421 /* Choose action dependant on which button was pressed */
2422 switch (event->button.button) {
2424 begin_reversible_command (_("start point trim"));
2426 if (selection->selected (rv)) {
2427 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
2428 i != selection->regions.by_layer().end(); ++i)
2430 if (!(*i)->region()->locked()) {
2431 (*i)->region()->clear_changes ();
2432 (*i)->region()->trim_front (new_bound);
2433 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2438 if (!rv->region()->locked()) {
2439 rv->region()->clear_changes ();
2440 rv->region()->trim_front (new_bound);
2441 _session->add_command(new StatefulDiffCommand (rv->region()));
2445 commit_reversible_command();
2449 begin_reversible_command (_("End point trim"));
2451 if (selection->selected (rv)) {
2453 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
2455 if (!(*i)->region()->locked()) {
2456 (*i)->region()->clear_changes();
2457 (*i)->region()->trim_end (new_bound);
2458 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2464 if (!rv->region()->locked()) {
2465 rv->region()->clear_changes ();
2466 rv->region()->trim_end (new_bound);
2467 _session->add_command (new StatefulDiffCommand (rv->region()));
2471 commit_reversible_command();
2480 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* /*event*/)
2485 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2486 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2490 Location* location = find_location_from_marker (marker, is_start);
2491 location->set_hidden (true, this);
2496 Editor::reposition_zoom_rect (framepos_t start, framepos_t end)
2498 double x1 = frame_to_pixel (start);
2499 double x2 = frame_to_pixel (end);
2500 double y2 = full_canvas_height - 1.0;
2502 zoom_rect->property_x1() = x1;
2503 zoom_rect->property_y1() = 1.0;
2504 zoom_rect->property_x2() = x2;
2505 zoom_rect->property_y2() = y2;
2510 Editor::mouse_rename_region (ArdourCanvas::Item* /*item*/, GdkEvent* /*event*/)
2512 using namespace Gtkmm2ext;
2514 ArdourPrompter prompter (false);
2516 prompter.set_prompt (_("Name for region:"));
2517 prompter.set_initial_text (clicked_regionview->region()->name());
2518 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
2519 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
2520 prompter.show_all ();
2521 switch (prompter.run ()) {
2522 case Gtk::RESPONSE_ACCEPT:
2524 prompter.get_result(str);
2526 clicked_regionview->region()->set_name (str);
2535 Editor::mouse_brush_insert_region (RegionView* rv, framepos_t pos)
2537 /* no brushing without a useful snap setting */
2539 switch (_snap_mode) {
2541 return; /* can't work because it allows region to be placed anywhere */
2546 switch (_snap_type) {
2554 /* don't brush a copy over the original */
2556 if (pos == rv->region()->position()) {
2560 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
2562 if (rtv == 0 || !rtv->is_track()) {
2566 boost::shared_ptr<Playlist> playlist = rtv->playlist();
2567 double speed = rtv->track()->speed();
2569 playlist->clear_changes ();
2570 boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region(), true));
2571 playlist->add_region (new_region, (framepos_t) (pos * speed));
2572 _session->add_command (new StatefulDiffCommand (playlist));
2574 // playlist is frozen, so we have to update manually XXX this is disgusting
2576 playlist->RegionAdded (new_region); /* EMIT SIGNAL */
2580 Editor::track_height_step_timeout ()
2582 if (get_microseconds() - last_track_height_step_timestamp < 250000) {
2583 current_stepping_trackview = 0;
2590 Editor::add_region_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2592 assert (region_view);
2594 if (!region_view->region()->playlist()) {
2598 _region_motion_group->raise_to_top ();
2600 if (Config->get_edit_mode() == Splice) {
2601 _drags->add (new RegionSpliceDrag (this, item, region_view, selection->regions.by_layer()));
2603 RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id);
2604 _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), false, false));
2607 /* sync the canvas to what we think is its current state */
2608 update_canvas_now();
2612 Editor::add_region_copy_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2614 assert (region_view);
2616 if (!region_view->region()->playlist()) {
2620 _region_motion_group->raise_to_top ();
2622 RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id);
2623 _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), false, true));
2627 Editor::add_region_brush_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2629 assert (region_view);
2631 if (!region_view->region()->playlist()) {
2635 if (Config->get_edit_mode() == Splice) {
2639 RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id);
2640 _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), true, false));
2642 begin_reversible_command (Operations::drag_region_brush);
2645 /** Start a grab where a time range is selected, track(s) are selected, and the
2646 * user clicks and drags a region with a modifier in order to create a new region containing
2647 * the section of the clicked region that lies within the time range.
2650 Editor::start_selection_grab (ArdourCanvas::Item* /*item*/, GdkEvent* event)
2652 if (clicked_regionview == 0) {
2656 /* lets try to create new Region for the selection */
2658 vector<boost::shared_ptr<Region> > new_regions;
2659 create_region_from_selection (new_regions);
2661 if (new_regions.empty()) {
2665 /* XXX fix me one day to use all new regions */
2667 boost::shared_ptr<Region> region (new_regions.front());
2669 /* add it to the current stream/playlist.
2671 tricky: the streamview for the track will add a new regionview. we will
2672 catch the signal it sends when it creates the regionview to
2673 set the regionview we want to then drag.
2676 latest_regionviews.clear();
2677 sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
2679 /* A selection grab currently creates two undo/redo operations, one for
2680 creating the new region and another for moving it.
2683 begin_reversible_command (Operations::selection_grab);
2685 boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
2687 playlist->clear_changes ();
2688 clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
2689 _session->add_command(new StatefulDiffCommand (playlist));
2691 commit_reversible_command ();
2695 if (latest_regionviews.empty()) {
2696 /* something went wrong */
2700 /* we need to deselect all other regionviews, and select this one
2701 i'm ignoring undo stuff, because the region creation will take care of it
2703 selection->set (latest_regionviews);
2705 _drags->set (new RegionMoveDrag (this, latest_regionviews.front()->get_canvas_group(), latest_regionviews.front(), latest_regionviews, false, false), event);
2711 if (_drags->active ()) {
2714 selection->clear ();
2719 Editor::set_internal_edit (bool yn)
2721 if (_internal_editing == yn) {
2725 _internal_editing = yn;
2728 pre_internal_mouse_mode = mouse_mode;
2729 pre_internal_snap_type = _snap_type;
2730 pre_internal_snap_mode = _snap_mode;
2732 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2733 (*i)->enter_internal_edit_mode ();
2736 set_snap_to (internal_snap_type);
2737 set_snap_mode (internal_snap_mode);
2741 internal_snap_mode = _snap_mode;
2742 internal_snap_type = _snap_type;
2744 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2745 (*i)->leave_internal_edit_mode ();
2748 if (mouse_mode == MouseDraw && pre_internal_mouse_mode != MouseDraw) {
2749 /* we were drawing .. flip back to something sensible */
2750 set_mouse_mode (pre_internal_mouse_mode);
2753 set_snap_to (pre_internal_snap_type);
2754 set_snap_mode (pre_internal_snap_mode);
2757 set_canvas_cursor ();
2760 /** Update _join_object_range_state which indicate whether we are over the top or bottom half of a region view,
2761 * used by the `join object/range' tool mode.
2764 Editor::update_join_object_range_location (double /*x*/, double y)
2766 /* XXX: actually, this decides based on whether the mouse is in the top
2767 or bottom half of a the waveform part RouteTimeAxisView;
2769 Note that entered_{track,regionview} is not always setup (e.g. if
2770 the mouse is over a TimeSelection), and to get a Region
2771 that we're over requires searching the playlist.
2774 if (!smart_mode_action->get_active() || (mouse_mode != MouseRange && mouse_mode != MouseObject)) {
2775 _join_object_range_state = JOIN_OBJECT_RANGE_NONE;
2779 if (mouse_mode == MouseObject) {
2780 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2781 } else if (mouse_mode == MouseRange) {
2782 _join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
2785 /* XXX: maybe we should make entered_track work in all cases, rather than resorting to this */
2786 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y + vertical_adjustment.get_value() - canvas_timebars_vsize);
2790 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
2795 rtv->canvas_display()->w2i (cx, cy);
2797 double const c = cy / (rtv->view()->child_height() - TimeAxisViewItem::NAME_HIGHLIGHT_SIZE);
2799 double const f = modf (c, &d);
2801 _join_object_range_state = f < 0.5 ? JOIN_OBJECT_RANGE_RANGE : JOIN_OBJECT_RANGE_OBJECT;
2807 Editor::effective_mouse_mode () const
2809 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
2811 } else if (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE) {
2819 Editor::remove_midi_note (ArdourCanvas::Item* item, GdkEvent *)
2821 ArdourCanvas::CanvasNoteEvent* e = dynamic_cast<ArdourCanvas::CanvasNoteEvent*> (item);
2824 e->region_view().delete_note (e->note ());
2828 Editor::set_canvas_cursor_for_region_view (double x, RegionView* rv)
2832 ArdourCanvas::Group* g = rv->get_canvas_group ();
2833 ArdourCanvas::Group* p = g->get_parent_group ();
2835 /* Compute x in region view parent coordinates */
2839 double x1, x2, y1, y2;
2840 g->get_bounds (x1, y1, x2, y2);
2842 /* Halfway across the region */
2843 double const h = (x1 + x2) / 2;
2845 Trimmable::CanTrim ct = rv->region()->can_trim ();
2847 if (ct & Trimmable::FrontTrimEarlier) {
2848 set_canvas_cursor (_cursors->left_side_trim);
2850 set_canvas_cursor (_cursors->left_side_trim_right_only);
2853 if (ct & Trimmable::EndTrimLater) {
2854 set_canvas_cursor (_cursors->right_side_trim);
2856 set_canvas_cursor (_cursors->right_side_trim_left_only);
2861 /** Obtain the pointer position in world coordinates */
2863 Editor::get_pointer_position (double& x, double& y) const
2866 track_canvas->get_pointer (px, py);
2867 track_canvas->window_to_world (px, py, x, y);