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.
29 #include "pbd/error.h"
30 #include "pbd/enumwriter.h"
31 #include "pbd/memento_command.h"
32 #include "pbd/basename.h"
33 #include "pbd/stateful_diff_command.h"
35 #include "gtkmm2ext/bindings.h"
36 #include "gtkmm2ext/utils.h"
37 #include "gtkmm2ext/tearoff.h"
39 #include "canvas/canvas.h"
41 #include "ardour/audioregion.h"
42 #include "ardour/operations.h"
43 #include "ardour/playlist.h"
44 #include "ardour/profile.h"
45 #include "ardour/region_factory.h"
46 #include "ardour/route.h"
47 #include "ardour/session.h"
48 #include "ardour/types.h"
50 #include "ardour_ui.h"
53 #include "time_axis_view.h"
54 #include "audio_time_axis.h"
55 #include "audio_region_view.h"
56 #include "midi_region_view.h"
58 #include "streamview.h"
59 #include "region_gain_line.h"
60 #include "automation_time_axis.h"
61 #include "control_point.h"
63 #include "selection.h"
66 #include "rgb_macros.h"
67 #include "control_point_dialog.h"
68 #include "editor_drag.h"
69 #include "automation_region_view.h"
70 #include "edit_note_dialog.h"
71 #include "mouse_cursors.h"
72 #include "editor_cursors.h"
73 #include "verbose_cursor.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) {
104 Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->_track_canvas->get_window();
106 if (!canvas_window) {
110 Glib::RefPtr<const Gdk::Window> pointer_window = Gdk::Display::get_default()->get_window_at_pointer (x, y);
112 if (!pointer_window) {
116 if (pointer_window != canvas_window) {
117 in_track_canvas = false;
121 in_track_canvas = true;
124 event.type = GDK_BUTTON_RELEASE;
128 where = window_event_sample (&event, 0, 0);
134 Editor::window_event_sample (GdkEvent const * event, double* pcx, double* pcy) const
136 ArdourCanvas::Duple d;
138 if (!gdk_event_get_coords (event, &d.x, &d.y)) {
142 /* event coordinates are in window units, so convert to canvas
145 d = _track_canvas->window_to_canvas (d);
155 return pixel_to_sample (d.x);
159 Editor::canvas_event_sample (GdkEvent const * event, double* pcx, double* pcy) const
164 /* event coordinates are already in canvas units */
166 if (!gdk_event_get_coords (event, &x, &y)) {
167 cerr << "!NO c COORDS for event type " << event->type << endl;
179 /* note that pixel_to_sample_from_event() never returns less than zero, so even if the pixel
180 position is negative (as can be the case with motion events in particular),
181 the frame location is always positive.
184 return pixel_to_sample_from_event (x);
188 Editor::set_current_trimmable (boost::shared_ptr<Trimmable> t)
190 boost::shared_ptr<Trimmable> st = _trimmable.lock();
192 if (!st || st == t) {
198 Editor::set_current_movable (boost::shared_ptr<Movable> m)
200 boost::shared_ptr<Movable> sm = _movable.lock();
202 if (!sm || sm != m) {
208 Editor::mouse_mode_object_range_toggled()
210 MouseMode m = mouse_mode;
212 Glib::RefPtr<Action> act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-range"));
214 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
217 if (tact->get_active()) {
218 m = MouseObject; //Smart mode turned to ON, force editing to Object mode
221 set_mouse_mode(m, true); //call this so the button styles can get updated
225 Editor::set_mouse_mode (MouseMode m, bool force)
227 if (_drags->active ()) {
231 if (!force && m == mouse_mode) {
235 if (ARDOUR::Profile->get_mixbus()) {
236 if ( m == MouseZoom) m = MouseObject;
237 if ( m == MouseCut) m = MouseObject;
240 Glib::RefPtr<Action> act;
244 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-range"));
248 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-cut"));
252 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-object"));
256 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-draw"));
260 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-gain"));
264 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-zoom"));
268 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-timefx"));
272 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-audition"));
278 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
281 /* go there and back to ensure that the toggled handler is called to set up mouse_mode */
282 tact->set_active (false);
283 tact->set_active (true);
285 //NOTE: this will result in a call to mouse_mode_toggled which does the heavy lifting
289 Editor::mouse_mode_toggled (MouseMode m)
291 Glib::RefPtr<Action> act;
292 Glib::RefPtr<ToggleAction> tact;
294 if (ARDOUR::Profile->get_mixbus()) {
295 if ( m == MouseZoom) m = MouseObject;
296 if ( m == MouseCut) m = MouseObject;
301 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-range"));
305 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-object"));
309 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-cut"));
313 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-draw"));
317 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-gain"));
321 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-zoom"));
325 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-timefx"));
329 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-audition"));
335 tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
338 if (!tact->get_active()) {
339 /* this was just the notification that the old mode has been
340 * left. we'll get called again with the new mode active in a
348 act = ActionManager::get_action (X_("MouseMode"), X_("toggle-internal-edit"));
349 tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
350 tact->set_active (true);
356 if (_session && mouse_mode == MouseAudition) {
357 /* stop transport and reset default speed to avoid oddness with
359 _session->request_transport_speed (0.0, true);
366 /* this should generate a new enter event which will
367 trigger the appropiate cursor.
371 _track_canvas->re_enter ();
374 set_gain_envelope_visibility ();
376 update_time_selection_display ();
378 MouseModeChanged (); /* EMIT SIGNAL */
382 Editor::update_time_selection_display ()
384 if (smart_mode_action->get_active()) {
385 /* not sure what to do here */
386 if (mouse_mode == MouseObject) {
390 switch (mouse_mode) {
392 selection->clear_objects ();
395 selection->clear_time ();
402 Editor::step_mouse_mode (bool next)
404 switch (current_mouse_mode()) {
407 if (Profile->get_sae()) {
408 set_mouse_mode (MouseZoom);
410 set_mouse_mode (MouseRange);
413 set_mouse_mode (MouseTimeFX);
418 if (next) set_mouse_mode (MouseDraw);
419 else set_mouse_mode (MouseCut);
423 if (next) set_mouse_mode (MouseRange);
424 else set_mouse_mode (MouseDraw);
428 if (next) set_mouse_mode (MouseCut);
429 else set_mouse_mode (MouseRange);
434 if (Profile->get_sae()) {
435 set_mouse_mode (MouseTimeFX);
437 set_mouse_mode (MouseGain);
440 if (Profile->get_sae()) {
441 set_mouse_mode (MouseObject);
443 set_mouse_mode (MouseDraw);
449 if (next) set_mouse_mode (MouseTimeFX);
450 else set_mouse_mode (MouseZoom);
455 set_mouse_mode (MouseAudition);
457 if (Profile->get_sae()) {
458 set_mouse_mode (MouseZoom);
460 set_mouse_mode (MouseGain);
466 if (next) set_mouse_mode (MouseObject);
467 else set_mouse_mode (MouseTimeFX);
473 Editor::toggle_internal_editing_from_double_click (GdkEvent* event)
475 if (_drags->active()) {
476 _drags->end_grab (event);
479 ActionManager::do_action ("MouseMode", "toggle-internal-edit");
481 /* prevent reversion of edit cursor on button release */
483 pre_press_cursor = 0;
489 Editor::button_selection (ArdourCanvas::Item* /*item*/, GdkEvent* event, ItemType item_type)
491 /* in object/audition/timefx/gain-automation mode,
492 any button press sets the selection if the object
493 can be selected. this is a bit of hack, because
494 we want to avoid this if the mouse operation is a
497 note: not dbl-click or triple-click
499 Also note that there is no region selection in internal edit mode, otherwise
500 for operations operating on the selection (e.g. cut) it is not obvious whether
501 to cut notes or regions.
504 MouseMode eff_mouse_mode = effective_mouse_mode ();
506 if (eff_mouse_mode == MouseCut) {
507 /* never change selection in cut mode */
511 if (get_smart_mode() && eff_mouse_mode == MouseRange && event->button.button == 3 && item_type == RegionItem) {
512 /* context clicks are always about object properties, even if
513 we're in range mode within smart mode.
515 eff_mouse_mode = MouseObject;
518 /* special case: allow drag of region fade in/out in object mode with join object/range enabled */
519 if (get_smart_mode()) {
521 case FadeInHandleItem:
522 case FadeInTrimHandleItem:
523 case FadeOutHandleItem:
524 case FadeOutTrimHandleItem:
525 eff_mouse_mode = MouseObject;
532 if (((mouse_mode != MouseObject) &&
533 (mouse_mode != MouseAudition || item_type != RegionItem) &&
534 (mouse_mode != MouseTimeFX || item_type != RegionItem) &&
535 (mouse_mode != MouseGain) &&
536 (mouse_mode != MouseDraw)) ||
537 ((event->type != GDK_BUTTON_PRESS && event->type != GDK_BUTTON_RELEASE) || event->button.button > 3) ||
538 (internal_editing() && mouse_mode != MouseTimeFX)) {
543 if (event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) {
545 if ((event->button.state & Keyboard::RelevantModifierKeyMask) && event->button.button != 1) {
547 /* almost no selection action on modified button-2 or button-3 events */
549 if (item_type != RegionItem && event->button.button != 2) {
555 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
556 bool press = (event->type == GDK_BUTTON_PRESS);
561 if (eff_mouse_mode != MouseRange) {
562 set_selected_regionview_from_click (press, op);
564 /* don't change the selection unless the
565 clicked track is not currently selected. if
566 so, "collapse" the selection to just this
569 if (!selection->selected (clicked_axisview)) {
570 set_selected_track_as_side_effect (Selection::Set);
574 if (eff_mouse_mode != MouseRange) {
575 set_selected_regionview_from_click (press, op);
580 case RegionViewNameHighlight:
582 case LeftFrameHandle:
583 case RightFrameHandle:
584 case FadeInHandleItem:
585 case FadeInTrimHandleItem:
587 case FadeOutHandleItem:
588 case FadeOutTrimHandleItem:
590 case StartCrossFadeItem:
591 case EndCrossFadeItem:
592 if (get_smart_mode() || eff_mouse_mode != MouseRange) {
593 set_selected_regionview_from_click (press, op);
594 } else if (event->type == GDK_BUTTON_PRESS) {
595 set_selected_track_as_side_effect (op);
599 case ControlPointItem:
600 set_selected_track_as_side_effect (op);
601 if (eff_mouse_mode != MouseRange) {
602 set_selected_control_point_from_click (press, op);
607 /* for context click, select track */
608 if (event->button.button == 3) {
609 selection->clear_tracks ();
610 set_selected_track_as_side_effect (op);
614 case AutomationTrackItem:
615 set_selected_track_as_side_effect (op);
624 Editor::button_press_handler_1 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
626 /* single mouse clicks on any of these item types operate
627 independent of mouse mode, mostly because they are
628 not on the main track canvas or because we want
633 case PlayheadCursorItem:
634 _drags->set (new CursorDrag (this, *playhead_cursor, true), event);
638 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
639 hide_marker (item, event);
641 _drags->set (new MarkerDrag (this, item), event);
645 case TempoMarkerItem:
647 TempoMarker* m = reinterpret_cast<TempoMarker*> (item->get_data ("marker"));
650 new TempoMarkerDrag (
653 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
660 case MeterMarkerItem:
662 MeterMarker* m = reinterpret_cast<MeterMarker*> (item->get_data ("marker"));
665 new MeterMarkerDrag (
668 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
676 _drags->set (new VideoTimeLineDrag (this, item), event);
683 case TimecodeRulerItem:
684 case SamplesRulerItem:
685 case MinsecRulerItem:
687 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
688 _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
694 case RangeMarkerBarItem:
695 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
696 _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
698 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateRangeMarker), event);
703 case CdMarkerBarItem:
704 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
705 _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
707 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateCDMarker), event);
712 case TransportMarkerBarItem:
713 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
714 _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
716 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateTransportMarker), event);
725 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
726 /* special case: allow trim of range selections in joined object mode;
727 in theory eff should equal MouseRange in this case, but it doesn't
728 because entering the range selection canvas item results in entered_regionview
729 being set to 0, so update_join_object_range_location acts as if we aren't
732 if (item_type == StartSelectionTrimItem) {
733 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
734 } else if (item_type == EndSelectionTrimItem) {
735 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
739 Editing::MouseMode eff = effective_mouse_mode ();
741 /* special case: allow drag of region fade in/out in object mode with join object/range enabled */
742 if (get_smart_mode()) {
744 case FadeInHandleItem:
745 case FadeInTrimHandleItem:
746 case FadeOutHandleItem:
747 case FadeOutTrimHandleItem:
755 /* there is no Range mode when in internal edit mode */
756 if (eff == MouseRange && internal_editing()) {
763 case StartSelectionTrimItem:
764 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
767 case EndSelectionTrimItem:
768 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
772 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
773 start_selection_grab (item, event);
775 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
776 /* grab selection for moving */
777 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionMove), event);
779 /* this was debated, but decided the more common action was to
780 make a new selection */
781 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
786 if (internal_editing()) {
787 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
788 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
792 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::RangeSelectModifier)) {
793 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionExtend), event);
795 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
801 case RegionViewNameHighlight:
802 if (!clicked_regionview->region()->locked()) {
803 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
809 if (!internal_editing()) {
810 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::RangeSelectModifier)) {
811 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionExtend), event);
813 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
823 /* Existing note: allow trimming/motion */
824 if (internal_editing()) {
825 /* trim notes if we're in internal edit mode and near the ends of the note */
826 NoteBase* cn = reinterpret_cast<NoteBase*>(item->get_data ("notebase"));
828 if (cn->big_enough_to_trim() && cn->mouse_near_ends()) {
829 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
831 _drags->set (new NoteDrag (this, item), event);
837 if (internal_editing()) {
838 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
839 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
853 case FadeInHandleItem:
854 case FadeOutHandleItem:
855 case LeftFrameHandle:
856 case RightFrameHandle:
857 case FeatureLineItem:
858 case RegionViewNameHighlight:
861 case AutomationTrackItem:
862 _drags->set (new RegionCutDrag (this, item, canvas_event_sample (event)), event, current_canvas_cursor);
873 /* Existing note: allow trimming/motion */
874 if (internal_editing()) {
875 NoteBase* cn = reinterpret_cast<NoteBase*> (item->get_data ("notebase"));
877 if (cn->big_enough_to_trim() && cn->mouse_near_ends()) {
878 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
880 _drags->set (new NoteDrag (this, item), event);
890 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)) &&
891 event->type == GDK_BUTTON_PRESS) {
893 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
895 } else if (event->type == GDK_BUTTON_PRESS) {
898 case FadeInHandleItem:
900 _drags->set (new FadeInDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), selection->regions), event, _cursors->fade_in);
904 case FadeOutHandleItem:
906 _drags->set (new FadeOutDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), selection->regions), event, _cursors->fade_out);
910 case StartCrossFadeItem:
911 case EndCrossFadeItem:
912 /* we might allow user to grab inside the fade to trim a region with preserve_fade_anchor. for not this is not fully implemented */
913 // if (!clicked_regionview->region()->locked()) {
914 // _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer(), true), event);
919 case FeatureLineItem:
921 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier)) {
922 remove_transient(item);
926 _drags->set (new FeatureLineDrag (this, item), event);
932 if (dynamic_cast<AutomationRegionView*> (clicked_regionview)) {
933 /* click on an automation region view; do nothing here and let the ARV's signal handler
939 if (internal_editing ()) {
943 /* click on a normal region view */
944 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
945 add_region_copy_drag (item, event, clicked_regionview);
946 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
947 add_region_brush_drag (item, event, clicked_regionview);
949 add_region_drag (item, event, clicked_regionview);
953 // if (!internal_editing() && (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE && !selection->regions.empty())) {
954 // _drags->add (new SelectionDrag (this, clicked_axisview->get_selection_rect (clicked_selection)->rect, SelectionDrag::SelectionMove));
957 _drags->start_grab (event);
961 case RegionViewNameHighlight:
962 case LeftFrameHandle:
963 case RightFrameHandle:
964 if (!clicked_regionview->region()->locked()) {
965 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
970 case FadeInTrimHandleItem:
971 case FadeOutTrimHandleItem:
972 if (!clicked_regionview->region()->locked()) {
973 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer(), true), event);
980 /* rename happens on edit clicks */
981 if (clicked_regionview->get_name_highlight()) {
982 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer()), event);
988 case ControlPointItem:
989 _drags->set (new ControlPointDrag (this, item), event);
993 case AutomationLineItem:
994 _drags->set (new LineDrag (this, item), event);
999 if (internal_editing()) {
1000 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
1001 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
1005 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
1009 case AutomationTrackItem:
1011 TimeAxisView* parent = clicked_axisview->get_parent ();
1012 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (clicked_axisview);
1014 if (parent && dynamic_cast<MidiTimeAxisView*> (parent) && atv->show_regions ()) {
1016 RouteTimeAxisView* p = dynamic_cast<RouteTimeAxisView*> (parent);
1018 boost::shared_ptr<Playlist> pl = p->track()->playlist ();
1019 if (pl->n_regions() == 0) {
1020 /* Parent has no regions; create one so that we have somewhere to put automation */
1021 _drags->set (new RegionCreateDrag (this, item, parent), event);
1023 /* See if there's a region before the click that we can extend, and extend it if so */
1024 framepos_t const t = canvas_event_sample (event);
1025 boost::shared_ptr<Region> prev = pl->find_next_region (t, End, -1);
1027 _drags->set (new RegionCreateDrag (this, item, parent), event);
1029 prev->set_length (t - prev->position ());
1033 /* rubberband drag to select automation points */
1034 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
1056 switch (item_type) {
1058 _drags->set (new LineDrag (this, item), event);
1061 case ControlPointItem:
1062 _drags->set (new ControlPointDrag (this, item), event);
1068 AudioRegionView* arv = dynamic_cast<AudioRegionView *> (clicked_regionview);
1070 _drags->set (new AutomationRangeDrag (this, arv, selection->time), event, _cursors->up_down);
1071 _drags->start_grab (event);
1073 double const y = event->button.y;
1074 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
1076 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
1078 /* smart "join" mode: drag automation */
1079 _drags->set (new AutomationRangeDrag (this, atv, selection->time), event, _cursors->up_down);
1080 _drags->start_grab (event);
1088 case AutomationLineItem:
1089 _drags->set (new LineDrag (this, item), event);
1099 if (event->type == GDK_BUTTON_PRESS) {
1100 _drags->set (new MouseZoomDrag (this, item), event);
1107 if (internal_editing() && item_type == NoteItem ) {
1108 /* drag notes if we're in internal edit mode */
1109 NoteBase* cn = reinterpret_cast<NoteBase*>(item->get_data ("notebase"));
1111 if (cn->big_enough_to_trim()) {
1112 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
1115 } else if (clicked_regionview) {
1117 _drags->set (new TimeFXDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1123 _drags->set (new ScrubDrag (this, item), event);
1124 scrub_reversals = 0;
1125 scrub_reverse_distance = 0;
1126 last_scrub_x = event->button.x;
1127 scrubbing_direction = 0;
1128 push_canvas_cursor (_cursors->transparent);
1140 Editor::button_press_handler_2 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1142 Editing::MouseMode const eff = effective_mouse_mode ();
1145 switch (item_type) {
1147 if (internal_editing ()) {
1148 /* no region drags in internal edit mode */
1152 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
1153 add_region_copy_drag (item, event, clicked_regionview);
1155 add_region_drag (item, event, clicked_regionview);
1157 _drags->start_grab (event);
1160 case ControlPointItem:
1161 _drags->set (new ControlPointDrag (this, item), event);
1169 switch (item_type) {
1170 case RegionViewNameHighlight:
1171 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1175 case LeftFrameHandle:
1176 case RightFrameHandle:
1177 if (!internal_editing ()) {
1178 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1183 case RegionViewName:
1184 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer()), event);
1198 /* relax till release */
1204 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1205 temporal_zoom_to_frame (false, canvas_event_sample (event));
1207 temporal_zoom_to_frame (true, canvas_event_sample(event));
1220 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1222 if (event->type == GDK_2BUTTON_PRESS) {
1223 _drags->mark_double_click ();
1224 gdk_pointer_ungrab (GDK_CURRENT_TIME);
1228 if (event->type != GDK_BUTTON_PRESS) {
1232 pre_press_cursor = current_canvas_cursor;
1234 _track_canvas->grab_focus();
1236 if (_session && _session->actively_recording()) {
1240 if (internal_editing()) {
1241 bool leave_internal_edit_mode = false;
1243 switch (item_type) {
1248 if (!dynamic_cast<MidiRegionView*> (clicked_regionview) && !dynamic_cast<AutomationRegionView*> (clicked_regionview)) {
1249 leave_internal_edit_mode = true;
1253 case PlayheadCursorItem:
1255 case TempoMarkerItem:
1256 case MeterMarkerItem:
1260 case RangeMarkerBarItem:
1261 case CdMarkerBarItem:
1262 case TransportMarkerBarItem:
1264 case TimecodeRulerItem:
1265 case SamplesRulerItem:
1266 case MinsecRulerItem:
1268 /* button press on these items never does anything to
1269 change the editing mode.
1277 if (leave_internal_edit_mode) {
1278 ActionManager::do_action ("MouseMode", "toggle-internal-edit");
1282 button_selection (item, event, item_type);
1284 if (!_drags->active () &&
1285 (Keyboard::is_delete_event (&event->button) ||
1286 Keyboard::is_context_menu_event (&event->button) ||
1287 Keyboard::is_edit_event (&event->button))) {
1289 /* handled by button release */
1293 //not rolling, range mode click + join_play_range : locate the PH here
1294 if ( !_drags->active () && !_session->transport_rolling() && ( effective_mouse_mode() == MouseRange ) && Config->get_follow_edits() ) {
1295 framepos_t where = canvas_event_sample (event);
1297 _session->request_locate (where, false);
1300 switch (event->button.button) {
1302 return button_press_handler_1 (item, event, item_type);
1306 return button_press_handler_2 (item, event, item_type);
1313 return button_press_dispatch (&event->button);
1322 Editor::button_press_dispatch (GdkEventButton* ev)
1324 /* this function is intended only for buttons 4 and above.
1327 Gtkmm2ext::MouseButton b (ev->state, ev->button);
1328 return button_bindings->activate (b, Gtkmm2ext::Bindings::Press);
1332 Editor::button_release_dispatch (GdkEventButton* ev)
1334 /* this function is intended only for buttons 4 and above.
1337 Gtkmm2ext::MouseButton b (ev->state, ev->button);
1338 return button_bindings->activate (b, Gtkmm2ext::Bindings::Release);
1342 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1344 framepos_t where = canvas_event_sample (event);
1345 AutomationTimeAxisView* atv = 0;
1347 if (pre_press_cursor) {
1348 set_canvas_cursor (pre_press_cursor);
1349 pre_press_cursor = 0;
1352 /* no action if we're recording */
1354 if (_session && _session->actively_recording()) {
1358 /* see if we're finishing a drag */
1360 bool were_dragging = false;
1361 if (_drags->active ()) {
1362 bool const r = _drags->end_grab (event);
1364 /* grab dragged, so do nothing else */
1368 were_dragging = true;
1371 update_region_layering_order_editor ();
1373 /* edit events get handled here */
1375 if (!_drags->active () && Keyboard::is_edit_event (&event->button)) {
1376 switch (item_type) {
1378 show_region_properties ();
1381 case TempoMarkerItem: {
1383 TempoMarker* tempo_marker;
1385 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
1386 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
1390 if ((tempo_marker = dynamic_cast<TempoMarker*> (marker)) == 0) {
1391 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
1395 edit_tempo_marker (*tempo_marker);
1399 case MeterMarkerItem: {
1401 MeterMarker* meter_marker;
1403 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
1404 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
1408 if ((meter_marker = dynamic_cast<MeterMarker*> (marker)) == 0) {
1409 fatal << _("programming error: marker for meter is not a meter marker!") << endmsg;
1412 edit_meter_marker (*meter_marker);
1416 case RegionViewName:
1417 if (clicked_regionview->name_active()) {
1418 return mouse_rename_region (item, event);
1422 case ControlPointItem:
1423 edit_control_point (item);
1432 /* context menu events get handled here */
1433 if (Keyboard::is_context_menu_event (&event->button)) {
1435 context_click_event = *event;
1437 if (!_drags->active ()) {
1439 /* no matter which button pops up the context menu, tell the menu
1440 widget to use button 1 to drive menu selection.
1443 switch (item_type) {
1445 case FadeInHandleItem:
1446 case FadeInTrimHandleItem:
1447 case StartCrossFadeItem:
1448 case LeftFrameHandle:
1449 popup_xfade_in_context_menu (1, event->button.time, item, item_type);
1453 case FadeOutHandleItem:
1454 case FadeOutTrimHandleItem:
1455 case EndCrossFadeItem:
1456 case RightFrameHandle:
1457 popup_xfade_out_context_menu (1, event->button.time, item, item_type);
1461 popup_track_context_menu (1, event->button.time, item_type, false);
1465 case RegionViewNameHighlight:
1466 case RegionViewName:
1467 popup_track_context_menu (1, event->button.time, item_type, false);
1471 popup_track_context_menu (1, event->button.time, item_type, true);
1474 case AutomationTrackItem:
1475 popup_track_context_menu (1, event->button.time, item_type, false);
1479 case RangeMarkerBarItem:
1480 case TransportMarkerBarItem:
1481 case CdMarkerBarItem:
1485 case TimecodeRulerItem:
1486 case SamplesRulerItem:
1487 case MinsecRulerItem:
1489 popup_ruler_menu (where, item_type);
1493 marker_context_menu (&event->button, item);
1496 case TempoMarkerItem:
1497 tempo_or_meter_marker_context_menu (&event->button, item);
1500 case MeterMarkerItem:
1501 tempo_or_meter_marker_context_menu (&event->button, item);
1504 case CrossfadeViewItem:
1505 popup_track_context_menu (1, event->button.time, item_type, false);
1508 case ControlPointItem:
1509 popup_control_point_context_menu (item, event);
1520 /* delete events get handled here */
1522 Editing::MouseMode const eff = effective_mouse_mode ();
1524 if (!_drags->active () && Keyboard::is_delete_event (&event->button)) {
1526 switch (item_type) {
1527 case TempoMarkerItem:
1528 remove_tempo_marker (item);
1531 case MeterMarkerItem:
1532 remove_meter_marker (item);
1536 remove_marker (*item, event);
1540 if (eff == MouseObject) {
1541 remove_clicked_region ();
1545 case ControlPointItem:
1546 remove_control_point (item);
1550 remove_midi_note (item, event);
1559 switch (event->button.button) {
1562 switch (item_type) {
1563 /* see comments in button_press_handler */
1564 case PlayheadCursorItem:
1567 case AutomationLineItem:
1568 case StartSelectionTrimItem:
1569 case EndSelectionTrimItem:
1573 if (!_dragging_playhead) {
1574 snap_to_with_modifier (where, event, 0, true);
1575 mouse_add_new_marker (where);
1579 case CdMarkerBarItem:
1580 if (!_dragging_playhead) {
1581 // if we get here then a dragged range wasn't done
1582 snap_to_with_modifier (where, event, 0, true);
1583 mouse_add_new_marker (where, true);
1588 if (!_dragging_playhead) {
1589 snap_to_with_modifier (where, event);
1590 mouse_add_new_tempo_event (where);
1595 if (!_dragging_playhead) {
1596 mouse_add_new_meter_event (pixel_to_sample (event->button.x));
1601 case TimecodeRulerItem:
1602 case SamplesRulerItem:
1603 case MinsecRulerItem:
1614 switch (item_type) {
1615 case AutomationTrackItem:
1616 atv = dynamic_cast<AutomationTimeAxisView*>(clicked_axisview);
1618 bool with_guard_points = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
1619 atv->add_automation_event (event, where, event->button.y, with_guard_points);
1629 switch (item_type) {
1632 /* check that we didn't drag before releasing, since
1633 its really annoying to create new control
1634 points when doing this.
1636 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (clicked_regionview);
1637 if (!were_dragging && arv) {
1638 bool with_guard_points = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
1639 arv->add_gain_point_event (item, event, with_guard_points);
1645 case AutomationTrackItem: {
1646 bool with_guard_points = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
1647 dynamic_cast<AutomationTimeAxisView*>(clicked_axisview)->
1648 add_automation_event (event, where, event->button.y, with_guard_points);
1658 pop_canvas_cursor ();
1659 if (scrubbing_direction == 0) {
1660 /* no drag, just a click */
1661 switch (item_type) {
1663 play_selected_region ();
1669 /* make sure we stop */
1670 _session->request_transport_speed (0.0);
1679 /* do any (de)selection operations that should occur on button release */
1680 button_selection (item, event, item_type);
1689 switch (item_type) {
1691 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1693 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier|Keyboard::SecondaryModifier))) {
1696 // Button2 click is unused
1711 // x_style_paste (where, 1.0);
1732 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1739 /* by the time we reach here, entered_regionview and entered trackview
1740 * will have already been set as appropriate. Things are done this
1741 * way because this method isn't passed a pointer to a variable type of
1742 * thing that is entered (which may or may not be canvas item).
1743 * (e.g. the actual entered regionview)
1746 choose_canvas_cursor_on_entry (&event->crossing, item_type);
1748 switch (item_type) {
1749 case ControlPointItem:
1750 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1751 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1754 fraction = 1.0 - (cp->get_y() / cp->line().height());
1756 _verbose_cursor->set (cp->line().get_verbose_cursor_string (fraction));
1757 _verbose_cursor->show ();
1762 if (mouse_mode == MouseGain) {
1763 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1765 line->set_outline_color (ARDOUR_UI::config()->get_canvasvar_EnteredGainLine());
1770 case AutomationLineItem:
1771 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1772 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1774 line->set_outline_color (ARDOUR_UI::config()->get_canvasvar_EnteredAutomationLine());
1779 case AutomationTrackItem:
1780 AutomationTimeAxisView* atv;
1781 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1782 clear_entered_track = false;
1783 set_entered_track (atv);
1788 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1791 entered_marker = marker;
1792 marker->set_color_rgba (ARDOUR_UI::config()->get_canvasvar_EnteredMarker());
1794 case MeterMarkerItem:
1795 case TempoMarkerItem:
1798 case FadeInHandleItem:
1799 case FadeInTrimHandleItem:
1800 if (mouse_mode == MouseObject && !internal_editing()) {
1801 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
1803 RegionView* rv = static_cast<RegionView*>(item->get_data ("regionview"));
1804 rect->set_fill_color (rv->get_fill_color());
1809 case FadeOutHandleItem:
1810 case FadeOutTrimHandleItem:
1811 if (mouse_mode == MouseObject && !internal_editing()) {
1812 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
1814 RegionView* rv = static_cast<RegionView*>(item->get_data ("regionview"));
1815 rect->set_fill_color (rv->get_fill_color ());
1820 case FeatureLineItem:
1822 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1823 line->set_outline_color (0xFF0000FF);
1834 /* third pass to handle entered track status in a comprehensible way.
1837 switch (item_type) {
1839 case AutomationLineItem:
1840 case ControlPointItem:
1841 /* these do not affect the current entered track state */
1842 clear_entered_track = false;
1845 case AutomationTrackItem:
1846 /* handled above already */
1858 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent*, ItemType item_type)
1866 switch (item_type) {
1867 case ControlPointItem:
1868 _verbose_cursor->hide ();
1872 case AutomationLineItem:
1873 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1875 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1877 line->set_outline_color (al->get_line_color());
1883 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1887 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
1888 location_flags_changed (loc, this);
1891 case MeterMarkerItem:
1892 case TempoMarkerItem:
1895 case FadeInTrimHandleItem:
1896 case FadeOutTrimHandleItem:
1897 case FadeInHandleItem:
1898 case FadeOutHandleItem:
1900 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
1902 rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_InactiveFadeHandle());
1907 case AutomationTrackItem:
1910 case FeatureLineItem:
1912 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1913 line->set_outline_color (ARDOUR_UI::config()->get_canvasvar_ZeroLine());
1925 Editor::scrub (framepos_t frame, double current_x)
1929 if (scrubbing_direction == 0) {
1931 _session->request_locate (frame, false);
1932 _session->request_transport_speed (0.1);
1933 scrubbing_direction = 1;
1937 if (last_scrub_x > current_x) {
1939 /* pointer moved to the left */
1941 if (scrubbing_direction > 0) {
1943 /* we reversed direction to go backwards */
1946 scrub_reverse_distance += (int) (last_scrub_x - current_x);
1950 /* still moving to the left (backwards) */
1952 scrub_reversals = 0;
1953 scrub_reverse_distance = 0;
1955 delta = 0.01 * (last_scrub_x - current_x);
1956 _session->request_transport_speed_nonzero (_session->transport_speed() - delta);
1960 /* pointer moved to the right */
1962 if (scrubbing_direction < 0) {
1963 /* we reversed direction to go forward */
1966 scrub_reverse_distance += (int) (current_x - last_scrub_x);
1969 /* still moving to the right */
1971 scrub_reversals = 0;
1972 scrub_reverse_distance = 0;
1974 delta = 0.01 * (current_x - last_scrub_x);
1975 _session->request_transport_speed_nonzero (_session->transport_speed() + delta);
1979 /* if there have been more than 2 opposite motion moves detected, or one that moves
1980 back more than 10 pixels, reverse direction
1983 if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
1985 if (scrubbing_direction > 0) {
1986 /* was forwards, go backwards */
1987 _session->request_transport_speed (-0.1);
1988 scrubbing_direction = -1;
1990 /* was backwards, go forwards */
1991 _session->request_transport_speed (0.1);
1992 scrubbing_direction = 1;
1995 scrub_reverse_distance = 0;
1996 scrub_reversals = 0;
2000 last_scrub_x = current_x;
2004 Editor::motion_handler (ArdourCanvas::Item* /*item*/, GdkEvent* event, bool from_autoscroll)
2006 _last_motion_y = event->motion.y;
2008 if (event->motion.is_hint) {
2011 /* We call this so that MOTION_NOTIFY events continue to be
2012 delivered to the canvas. We need to do this because we set
2013 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
2014 the density of the events, at the expense of a round-trip
2015 to the server. Given that this will mostly occur on cases
2016 where DISPLAY = :0.0, and given the cost of what the motion
2017 event might do, its a good tradeoff.
2020 _track_canvas->get_pointer (x, y);
2023 if (current_stepping_trackview) {
2024 /* don't keep the persistent stepped trackview if the mouse moves */
2025 current_stepping_trackview = 0;
2026 step_timeout.disconnect ();
2029 if (_session && _session->actively_recording()) {
2030 /* Sorry. no dragging stuff around while we record */
2034 update_join_object_range_location (event->motion.y);
2036 if (_drags->active ()) {
2037 return _drags->motion_handler (event, from_autoscroll);
2044 Editor::can_remove_control_point (ArdourCanvas::Item* item)
2046 ControlPoint* control_point;
2048 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2049 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2053 AutomationLine& line = control_point->line ();
2054 if (dynamic_cast<AudioRegionGainLine*> (&line)) {
2055 /* we shouldn't remove the first or last gain point in region gain lines */
2056 if (line.is_last_point(*control_point) || line.is_first_point(*control_point)) {
2065 Editor::remove_control_point (ArdourCanvas::Item* item)
2067 if (!can_remove_control_point (item)) {
2071 ControlPoint* control_point;
2073 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2074 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2078 control_point->line().remove_point (*control_point);
2082 Editor::edit_control_point (ArdourCanvas::Item* item)
2084 ControlPoint* p = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"));
2087 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2091 ControlPointDialog d (p);
2094 if (d.run () != RESPONSE_ACCEPT) {
2098 p->line().modify_point_y (*p, d.get_y_fraction ());
2102 Editor::edit_notes (TimeAxisViewItem& tavi)
2104 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(&tavi);
2110 MidiRegionView::Selection const & s = mrv->selection();
2116 EditNoteDialog* d = new EditNoteDialog (&(*s.begin())->region_view(), s);
2120 d->signal_response().connect (sigc::bind (sigc::mem_fun (*this, &Editor::note_edit_done), d));
2124 Editor::note_edit_done (int r, EditNoteDialog* d)
2131 Editor::visible_order_range (int* low, int* high) const
2133 *low = TimeAxisView::max_order ();
2136 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
2138 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
2140 if (!rtv->hidden()) {
2142 if (*high < rtv->order()) {
2143 *high = rtv->order ();
2146 if (*low > rtv->order()) {
2147 *low = rtv->order ();
2154 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
2156 /* Either add to or set the set the region selection, unless
2157 this is an alignment click (control used)
2160 if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
2161 TimeAxisView* tv = &rv.get_time_axis_view();
2162 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
2164 if (rtv && rtv->is_track()) {
2165 speed = rtv->track()->speed();
2168 framepos_t where = get_preferred_edit_position();
2172 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
2174 align_region (rv.region(), SyncPoint, (framepos_t) (where * speed));
2176 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
2178 align_region (rv.region(), End, (framepos_t) (where * speed));
2182 align_region (rv.region(), Start, (framepos_t) (where * speed));
2189 Editor::collect_new_region_view (RegionView* rv)
2191 latest_regionviews.push_back (rv);
2195 Editor::collect_and_select_new_region_view (RegionView* rv)
2198 latest_regionviews.push_back (rv);
2202 Editor::cancel_selection ()
2204 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2205 (*i)->hide_selection ();
2208 selection->clear ();
2209 clicked_selection = 0;
2213 Editor::cancel_time_selection ()
2215 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2216 (*i)->hide_selection ();
2218 selection->time.clear ();
2219 clicked_selection = 0;
2223 Editor::point_trim (GdkEvent* event, framepos_t new_bound)
2225 RegionView* rv = clicked_regionview;
2227 /* Choose action dependant on which button was pressed */
2228 switch (event->button.button) {
2230 begin_reversible_command (_("start point trim"));
2232 if (selection->selected (rv)) {
2233 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
2234 i != selection->regions.by_layer().end(); ++i)
2236 if (!(*i)->region()->locked()) {
2237 (*i)->region()->clear_changes ();
2238 (*i)->region()->trim_front (new_bound);
2239 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2244 if (!rv->region()->locked()) {
2245 rv->region()->clear_changes ();
2246 rv->region()->trim_front (new_bound);
2247 _session->add_command(new StatefulDiffCommand (rv->region()));
2251 commit_reversible_command();
2255 begin_reversible_command (_("End point trim"));
2257 if (selection->selected (rv)) {
2259 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
2261 if (!(*i)->region()->locked()) {
2262 (*i)->region()->clear_changes();
2263 (*i)->region()->trim_end (new_bound);
2264 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2270 if (!rv->region()->locked()) {
2271 rv->region()->clear_changes ();
2272 rv->region()->trim_end (new_bound);
2273 _session->add_command (new StatefulDiffCommand (rv->region()));
2277 commit_reversible_command();
2286 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* /*event*/)
2291 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2292 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2296 Location* location = find_location_from_marker (marker, is_start);
2297 location->set_hidden (true, this);
2302 Editor::reposition_zoom_rect (framepos_t start, framepos_t end)
2304 double x1 = sample_to_pixel (start);
2305 double x2 = sample_to_pixel (end);
2306 double y2 = _full_canvas_height - 1.0;
2308 zoom_rect->set (ArdourCanvas::Rect (x1, 1.0, x2, y2));
2313 Editor::mouse_rename_region (ArdourCanvas::Item* /*item*/, GdkEvent* /*event*/)
2315 using namespace Gtkmm2ext;
2317 ArdourPrompter prompter (false);
2319 prompter.set_prompt (_("Name for region:"));
2320 prompter.set_initial_text (clicked_regionview->region()->name());
2321 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
2322 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
2323 prompter.show_all ();
2324 switch (prompter.run ()) {
2325 case Gtk::RESPONSE_ACCEPT:
2327 prompter.get_result(str);
2329 clicked_regionview->region()->set_name (str);
2338 Editor::mouse_brush_insert_region (RegionView* rv, framepos_t pos)
2340 /* no brushing without a useful snap setting */
2342 switch (_snap_mode) {
2344 return; /* can't work because it allows region to be placed anywhere */
2349 switch (_snap_type) {
2357 /* don't brush a copy over the original */
2359 if (pos == rv->region()->position()) {
2363 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
2365 if (rtv == 0 || !rtv->is_track()) {
2369 boost::shared_ptr<Playlist> playlist = rtv->playlist();
2370 double speed = rtv->track()->speed();
2372 playlist->clear_changes ();
2373 boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region(), true));
2374 playlist->add_region (new_region, (framepos_t) (pos * speed));
2375 _session->add_command (new StatefulDiffCommand (playlist));
2377 // playlist is frozen, so we have to update manually XXX this is disgusting
2379 playlist->RegionAdded (new_region); /* EMIT SIGNAL */
2383 Editor::track_height_step_timeout ()
2385 if (get_microseconds() - last_track_height_step_timestamp < 250000) {
2386 current_stepping_trackview = 0;
2393 Editor::add_region_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2395 assert (region_view);
2397 if (!region_view->region()->playlist()) {
2401 switch (Config->get_edit_mode()) {
2403 _drags->add (new RegionSpliceDrag (this, item, region_view, selection->regions.by_layer()));
2406 _drags->add (new RegionRippleDrag (this, item, region_view, selection->regions.by_layer()));
2409 _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), false, false));
2416 Editor::add_region_copy_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2418 assert (region_view);
2420 if (!region_view->region()->playlist()) {
2424 _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), false, true));
2428 Editor::add_region_brush_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2430 assert (region_view);
2432 if (!region_view->region()->playlist()) {
2436 if (Config->get_edit_mode() == Splice || Config->get_edit_mode() == Ripple) {
2440 _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), true, false));
2442 begin_reversible_command (Operations::drag_region_brush);
2445 /** Start a grab where a time range is selected, track(s) are selected, and the
2446 * user clicks and drags a region with a modifier in order to create a new region containing
2447 * the section of the clicked region that lies within the time range.
2450 Editor::start_selection_grab (ArdourCanvas::Item* /*item*/, GdkEvent* event)
2452 if (clicked_regionview == 0) {
2456 /* lets try to create new Region for the selection */
2458 vector<boost::shared_ptr<Region> > new_regions;
2459 create_region_from_selection (new_regions);
2461 if (new_regions.empty()) {
2465 /* XXX fix me one day to use all new regions */
2467 boost::shared_ptr<Region> region (new_regions.front());
2469 /* add it to the current stream/playlist.
2471 tricky: the streamview for the track will add a new regionview. we will
2472 catch the signal it sends when it creates the regionview to
2473 set the regionview we want to then drag.
2476 latest_regionviews.clear();
2477 sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
2479 /* A selection grab currently creates two undo/redo operations, one for
2480 creating the new region and another for moving it.
2483 begin_reversible_command (Operations::selection_grab);
2485 boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
2487 playlist->clear_changes ();
2488 clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
2489 _session->add_command(new StatefulDiffCommand (playlist));
2491 commit_reversible_command ();
2495 if (latest_regionviews.empty()) {
2496 /* something went wrong */
2500 /* we need to deselect all other regionviews, and select this one
2501 i'm ignoring undo stuff, because the region creation will take care of it
2503 selection->set (latest_regionviews);
2505 _drags->set (new RegionMoveDrag (this, latest_regionviews.front()->get_canvas_group(), latest_regionviews.front(), latest_regionviews, false, false), event);
2511 if (_drags->active ()) {
2514 selection->clear ();
2519 Editor::set_internal_edit (bool yn)
2521 if (_internal_editing == yn) {
2525 _internal_editing = yn;
2528 pre_internal_mouse_mode = mouse_mode;
2529 pre_internal_snap_type = _snap_type;
2530 pre_internal_snap_mode = _snap_mode;
2532 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2533 (*i)->enter_internal_edit_mode ();
2536 set_snap_to (internal_snap_type);
2537 set_snap_mode (internal_snap_mode);
2541 internal_snap_mode = _snap_mode;
2542 internal_snap_type = _snap_type;
2544 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2545 (*i)->leave_internal_edit_mode ();
2548 if (mouse_mode == MouseDraw && pre_internal_mouse_mode != MouseDraw) {
2549 /* we were drawing .. flip back to something sensible */
2550 set_mouse_mode (pre_internal_mouse_mode);
2553 set_snap_to (pre_internal_snap_type);
2554 set_snap_mode (pre_internal_snap_mode);
2557 reset_canvas_cursor ();
2560 /** Update _join_object_range_state which indicate whether we are over the top
2561 * or bottom half of a route view, used by the `join object/range' tool
2562 * mode. Coordinates in canvas space.
2565 Editor::update_join_object_range_location (double y)
2567 if (_internal_editing || !get_smart_mode()) {
2568 _join_object_range_state = JOIN_OBJECT_RANGE_NONE;
2572 JoinObjectRangeState const old = _join_object_range_state;
2574 if (mouse_mode == MouseObject) {
2575 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2576 } else if (mouse_mode == MouseRange) {
2577 _join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
2580 if (entered_regionview) {
2582 ArdourCanvas::Duple const item_space = entered_regionview->get_canvas_group()->canvas_to_item (ArdourCanvas::Duple (0, y));
2583 double const c = item_space.y / entered_regionview->height();
2585 _join_object_range_state = c <= 0.5 ? JOIN_OBJECT_RANGE_RANGE : JOIN_OBJECT_RANGE_OBJECT;
2587 if (_join_object_range_state != old) {
2588 set_canvas_cursor (which_track_cursor ());
2591 } else if (entered_track) {
2593 RouteTimeAxisView* entered_route_view = dynamic_cast<RouteTimeAxisView*> (entered_track);
2595 if (entered_route_view) {
2600 entered_route_view->canvas_display()->canvas_to_item (cx, cy);
2602 double track_height = entered_route_view->view()->child_height();
2603 if (Config->get_show_name_highlight()) {
2604 track_height -= TimeAxisViewItem::NAME_HIGHLIGHT_SIZE;
2606 double const c = cy / track_height;
2610 _join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
2612 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2616 /* Other kinds of tracks use object mode */
2617 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2620 if (_join_object_range_state != old) {
2621 set_canvas_cursor (which_track_cursor ());
2627 Editor::effective_mouse_mode () const
2629 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
2631 } else if (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE) {
2639 Editor::remove_midi_note (ArdourCanvas::Item* item, GdkEvent *)
2641 NoteBase* e = reinterpret_cast<NoteBase*> (item->get_data ("notebase"));
2644 e->region_view().delete_note (e->note ());
2647 /** Obtain the pointer position in canvas coordinates */
2649 Editor::get_pointer_position (double& x, double& y) const
2652 _track_canvas->get_pointer (px, py);
2653 _track_canvas->window_to_canvas (px, py, x, y);