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);
1072 double const y = event->button.y;
1073 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
1075 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
1077 /* smart "join" mode: drag automation */
1078 _drags->set (new AutomationRangeDrag (this, atv, selection->time), event, _cursors->up_down);
1086 case AutomationLineItem:
1087 _drags->set (new LineDrag (this, item), event);
1097 if (event->type == GDK_BUTTON_PRESS) {
1098 _drags->set (new MouseZoomDrag (this, item), event);
1105 if (internal_editing() && item_type == NoteItem ) {
1106 /* drag notes if we're in internal edit mode */
1107 NoteBase* cn = reinterpret_cast<NoteBase*>(item->get_data ("notebase"));
1109 if (cn->big_enough_to_trim()) {
1110 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
1113 } else if (clicked_regionview) {
1115 _drags->set (new TimeFXDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1121 _drags->set (new ScrubDrag (this, item), event);
1122 scrub_reversals = 0;
1123 scrub_reverse_distance = 0;
1124 last_scrub_x = event->button.x;
1125 scrubbing_direction = 0;
1126 push_canvas_cursor (_cursors->transparent);
1138 Editor::button_press_handler_2 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1140 Editing::MouseMode const eff = effective_mouse_mode ();
1143 switch (item_type) {
1145 if (internal_editing ()) {
1146 /* no region drags in internal edit mode */
1150 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
1151 add_region_copy_drag (item, event, clicked_regionview);
1153 add_region_drag (item, event, clicked_regionview);
1155 _drags->start_grab (event);
1158 case ControlPointItem:
1159 _drags->set (new ControlPointDrag (this, item), event);
1167 switch (item_type) {
1168 case RegionViewNameHighlight:
1169 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1173 case LeftFrameHandle:
1174 case RightFrameHandle:
1175 if (!internal_editing ()) {
1176 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1181 case RegionViewName:
1182 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer()), event);
1196 /* relax till release */
1202 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1203 temporal_zoom_to_frame (false, canvas_event_sample (event));
1205 temporal_zoom_to_frame (true, canvas_event_sample(event));
1218 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1220 if (event->type == GDK_2BUTTON_PRESS) {
1221 _drags->mark_double_click ();
1222 gdk_pointer_ungrab (GDK_CURRENT_TIME);
1226 if (event->type != GDK_BUTTON_PRESS) {
1230 pre_press_cursor = current_canvas_cursor;
1232 _track_canvas->grab_focus();
1234 if (_session && _session->actively_recording()) {
1238 if (internal_editing()) {
1239 bool leave_internal_edit_mode = false;
1241 switch (item_type) {
1246 if (!dynamic_cast<MidiRegionView*> (clicked_regionview) && !dynamic_cast<AutomationRegionView*> (clicked_regionview)) {
1247 leave_internal_edit_mode = true;
1251 case PlayheadCursorItem:
1253 case TempoMarkerItem:
1254 case MeterMarkerItem:
1258 case RangeMarkerBarItem:
1259 case CdMarkerBarItem:
1260 case TransportMarkerBarItem:
1262 case TimecodeRulerItem:
1263 case SamplesRulerItem:
1264 case MinsecRulerItem:
1266 /* button press on these items never does anything to
1267 change the editing mode.
1275 if (leave_internal_edit_mode) {
1276 ActionManager::do_action ("MouseMode", "toggle-internal-edit");
1280 button_selection (item, event, item_type);
1282 if (!_drags->active () &&
1283 (Keyboard::is_delete_event (&event->button) ||
1284 Keyboard::is_context_menu_event (&event->button) ||
1285 Keyboard::is_edit_event (&event->button))) {
1287 /* handled by button release */
1291 //not rolling, range mode click + join_play_range : locate the PH here
1292 if ( !_drags->active () && !_session->transport_rolling() && ( effective_mouse_mode() == MouseRange ) && Config->get_follow_edits() ) {
1293 framepos_t where = canvas_event_sample (event);
1295 _session->request_locate (where, false);
1298 switch (event->button.button) {
1300 return button_press_handler_1 (item, event, item_type);
1304 return button_press_handler_2 (item, event, item_type);
1311 return button_press_dispatch (&event->button);
1320 Editor::button_press_dispatch (GdkEventButton* ev)
1322 /* this function is intended only for buttons 4 and above.
1325 Gtkmm2ext::MouseButton b (ev->state, ev->button);
1326 return button_bindings->activate (b, Gtkmm2ext::Bindings::Press);
1330 Editor::button_release_dispatch (GdkEventButton* ev)
1332 /* this function is intended only for buttons 4 and above.
1335 Gtkmm2ext::MouseButton b (ev->state, ev->button);
1336 return button_bindings->activate (b, Gtkmm2ext::Bindings::Release);
1340 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1342 framepos_t where = canvas_event_sample (event);
1343 AutomationTimeAxisView* atv = 0;
1345 if (pre_press_cursor) {
1346 set_canvas_cursor (pre_press_cursor);
1347 pre_press_cursor = 0;
1350 /* no action if we're recording */
1352 if (_session && _session->actively_recording()) {
1356 /* see if we're finishing a drag */
1358 bool were_dragging = false;
1359 if (_drags->active ()) {
1360 bool const r = _drags->end_grab (event);
1362 /* grab dragged, so do nothing else */
1366 were_dragging = true;
1369 update_region_layering_order_editor ();
1371 /* edit events get handled here */
1373 if (!_drags->active () && Keyboard::is_edit_event (&event->button)) {
1374 switch (item_type) {
1376 show_region_properties ();
1379 case TempoMarkerItem: {
1381 TempoMarker* tempo_marker;
1383 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
1384 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
1388 if ((tempo_marker = dynamic_cast<TempoMarker*> (marker)) == 0) {
1389 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
1393 edit_tempo_marker (*tempo_marker);
1397 case MeterMarkerItem: {
1399 MeterMarker* meter_marker;
1401 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
1402 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
1406 if ((meter_marker = dynamic_cast<MeterMarker*> (marker)) == 0) {
1407 fatal << _("programming error: marker for meter is not a meter marker!") << endmsg;
1410 edit_meter_marker (*meter_marker);
1414 case RegionViewName:
1415 if (clicked_regionview->name_active()) {
1416 return mouse_rename_region (item, event);
1420 case ControlPointItem:
1421 edit_control_point (item);
1430 /* context menu events get handled here */
1431 if (Keyboard::is_context_menu_event (&event->button)) {
1433 context_click_event = *event;
1435 if (!_drags->active ()) {
1437 /* no matter which button pops up the context menu, tell the menu
1438 widget to use button 1 to drive menu selection.
1441 switch (item_type) {
1443 case FadeInHandleItem:
1444 case FadeInTrimHandleItem:
1445 case StartCrossFadeItem:
1446 case LeftFrameHandle:
1447 popup_xfade_in_context_menu (1, event->button.time, item, item_type);
1451 case FadeOutHandleItem:
1452 case FadeOutTrimHandleItem:
1453 case EndCrossFadeItem:
1454 case RightFrameHandle:
1455 popup_xfade_out_context_menu (1, event->button.time, item, item_type);
1459 popup_track_context_menu (1, event->button.time, item_type, false);
1463 case RegionViewNameHighlight:
1464 case RegionViewName:
1465 popup_track_context_menu (1, event->button.time, item_type, false);
1469 popup_track_context_menu (1, event->button.time, item_type, true);
1472 case AutomationTrackItem:
1473 popup_track_context_menu (1, event->button.time, item_type, false);
1477 case RangeMarkerBarItem:
1478 case TransportMarkerBarItem:
1479 case CdMarkerBarItem:
1483 case TimecodeRulerItem:
1484 case SamplesRulerItem:
1485 case MinsecRulerItem:
1487 popup_ruler_menu (where, item_type);
1491 marker_context_menu (&event->button, item);
1494 case TempoMarkerItem:
1495 tempo_or_meter_marker_context_menu (&event->button, item);
1498 case MeterMarkerItem:
1499 tempo_or_meter_marker_context_menu (&event->button, item);
1502 case CrossfadeViewItem:
1503 popup_track_context_menu (1, event->button.time, item_type, false);
1506 case ControlPointItem:
1507 popup_control_point_context_menu (item, event);
1518 /* delete events get handled here */
1520 Editing::MouseMode const eff = effective_mouse_mode ();
1522 if (!_drags->active () && Keyboard::is_delete_event (&event->button)) {
1524 switch (item_type) {
1525 case TempoMarkerItem:
1526 remove_tempo_marker (item);
1529 case MeterMarkerItem:
1530 remove_meter_marker (item);
1534 remove_marker (*item, event);
1538 if (eff == MouseObject) {
1539 remove_clicked_region ();
1543 case ControlPointItem:
1544 remove_control_point (item);
1548 remove_midi_note (item, event);
1557 switch (event->button.button) {
1560 switch (item_type) {
1561 /* see comments in button_press_handler */
1562 case PlayheadCursorItem:
1565 case AutomationLineItem:
1566 case StartSelectionTrimItem:
1567 case EndSelectionTrimItem:
1571 if (!_dragging_playhead) {
1572 snap_to_with_modifier (where, event, 0, true);
1573 mouse_add_new_marker (where);
1577 case CdMarkerBarItem:
1578 if (!_dragging_playhead) {
1579 // if we get here then a dragged range wasn't done
1580 snap_to_with_modifier (where, event, 0, true);
1581 mouse_add_new_marker (where, true);
1586 if (!_dragging_playhead) {
1587 snap_to_with_modifier (where, event);
1588 mouse_add_new_tempo_event (where);
1593 if (!_dragging_playhead) {
1594 mouse_add_new_meter_event (pixel_to_sample (event->button.x));
1599 case TimecodeRulerItem:
1600 case SamplesRulerItem:
1601 case MinsecRulerItem:
1612 switch (item_type) {
1613 case AutomationTrackItem:
1614 atv = dynamic_cast<AutomationTimeAxisView*>(clicked_axisview);
1616 bool with_guard_points = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
1617 atv->add_automation_event (event, where, event->button.y, with_guard_points);
1627 switch (item_type) {
1630 /* check that we didn't drag before releasing, since
1631 its really annoying to create new control
1632 points when doing this.
1634 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (clicked_regionview);
1635 if (!were_dragging && arv) {
1636 bool with_guard_points = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
1637 arv->add_gain_point_event (item, event, with_guard_points);
1643 case AutomationTrackItem: {
1644 bool with_guard_points = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
1645 dynamic_cast<AutomationTimeAxisView*>(clicked_axisview)->
1646 add_automation_event (event, where, event->button.y, with_guard_points);
1656 pop_canvas_cursor ();
1657 if (scrubbing_direction == 0) {
1658 /* no drag, just a click */
1659 switch (item_type) {
1661 play_selected_region ();
1667 /* make sure we stop */
1668 _session->request_transport_speed (0.0);
1677 /* do any (de)selection operations that should occur on button release */
1678 button_selection (item, event, item_type);
1687 switch (item_type) {
1689 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1691 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier|Keyboard::SecondaryModifier))) {
1694 // Button2 click is unused
1709 // x_style_paste (where, 1.0);
1730 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1737 /* by the time we reach here, entered_regionview and entered trackview
1738 * will have already been set as appropriate. Things are done this
1739 * way because this method isn't passed a pointer to a variable type of
1740 * thing that is entered (which may or may not be canvas item).
1741 * (e.g. the actual entered regionview)
1744 choose_canvas_cursor_on_entry (&event->crossing, item_type);
1746 switch (item_type) {
1747 case ControlPointItem:
1748 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1749 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1752 fraction = 1.0 - (cp->get_y() / cp->line().height());
1754 _verbose_cursor->set (cp->line().get_verbose_cursor_string (fraction));
1755 _verbose_cursor->show ();
1760 if (mouse_mode == MouseGain) {
1761 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1763 line->set_outline_color (ARDOUR_UI::config()->get_canvasvar_EnteredGainLine());
1768 case AutomationLineItem:
1769 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1770 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1772 line->set_outline_color (ARDOUR_UI::config()->get_canvasvar_EnteredAutomationLine());
1777 case AutomationTrackItem:
1778 AutomationTimeAxisView* atv;
1779 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1780 clear_entered_track = false;
1781 set_entered_track (atv);
1786 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1789 entered_marker = marker;
1790 marker->set_color_rgba (ARDOUR_UI::config()->get_canvasvar_EnteredMarker());
1792 case MeterMarkerItem:
1793 case TempoMarkerItem:
1796 case FadeInHandleItem:
1797 case FadeInTrimHandleItem:
1798 if (mouse_mode == MouseObject && !internal_editing()) {
1799 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
1801 RegionView* rv = static_cast<RegionView*>(item->get_data ("regionview"));
1802 rect->set_fill_color (rv->get_fill_color());
1807 case FadeOutHandleItem:
1808 case FadeOutTrimHandleItem:
1809 if (mouse_mode == MouseObject && !internal_editing()) {
1810 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
1812 RegionView* rv = static_cast<RegionView*>(item->get_data ("regionview"));
1813 rect->set_fill_color (rv->get_fill_color ());
1818 case FeatureLineItem:
1820 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1821 line->set_outline_color (0xFF0000FF);
1832 /* third pass to handle entered track status in a comprehensible way.
1835 switch (item_type) {
1837 case AutomationLineItem:
1838 case ControlPointItem:
1839 /* these do not affect the current entered track state */
1840 clear_entered_track = false;
1843 case AutomationTrackItem:
1844 /* handled above already */
1856 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent*, ItemType item_type)
1864 switch (item_type) {
1865 case ControlPointItem:
1866 _verbose_cursor->hide ();
1870 case AutomationLineItem:
1871 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1873 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1875 line->set_outline_color (al->get_line_color());
1881 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1885 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
1886 location_flags_changed (loc, this);
1889 case MeterMarkerItem:
1890 case TempoMarkerItem:
1893 case FadeInTrimHandleItem:
1894 case FadeOutTrimHandleItem:
1895 case FadeInHandleItem:
1896 case FadeOutHandleItem:
1898 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
1900 rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_InactiveFadeHandle());
1905 case AutomationTrackItem:
1908 case FeatureLineItem:
1910 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1911 line->set_outline_color (ARDOUR_UI::config()->get_canvasvar_ZeroLine());
1923 Editor::scrub (framepos_t frame, double current_x)
1927 if (scrubbing_direction == 0) {
1929 _session->request_locate (frame, false);
1930 _session->request_transport_speed (0.1);
1931 scrubbing_direction = 1;
1935 if (last_scrub_x > current_x) {
1937 /* pointer moved to the left */
1939 if (scrubbing_direction > 0) {
1941 /* we reversed direction to go backwards */
1944 scrub_reverse_distance += (int) (last_scrub_x - current_x);
1948 /* still moving to the left (backwards) */
1950 scrub_reversals = 0;
1951 scrub_reverse_distance = 0;
1953 delta = 0.01 * (last_scrub_x - current_x);
1954 _session->request_transport_speed_nonzero (_session->transport_speed() - delta);
1958 /* pointer moved to the right */
1960 if (scrubbing_direction < 0) {
1961 /* we reversed direction to go forward */
1964 scrub_reverse_distance += (int) (current_x - last_scrub_x);
1967 /* still moving to the right */
1969 scrub_reversals = 0;
1970 scrub_reverse_distance = 0;
1972 delta = 0.01 * (current_x - last_scrub_x);
1973 _session->request_transport_speed_nonzero (_session->transport_speed() + delta);
1977 /* if there have been more than 2 opposite motion moves detected, or one that moves
1978 back more than 10 pixels, reverse direction
1981 if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
1983 if (scrubbing_direction > 0) {
1984 /* was forwards, go backwards */
1985 _session->request_transport_speed (-0.1);
1986 scrubbing_direction = -1;
1988 /* was backwards, go forwards */
1989 _session->request_transport_speed (0.1);
1990 scrubbing_direction = 1;
1993 scrub_reverse_distance = 0;
1994 scrub_reversals = 0;
1998 last_scrub_x = current_x;
2002 Editor::motion_handler (ArdourCanvas::Item* /*item*/, GdkEvent* event, bool from_autoscroll)
2004 _last_motion_y = event->motion.y;
2006 if (event->motion.is_hint) {
2009 /* We call this so that MOTION_NOTIFY events continue to be
2010 delivered to the canvas. We need to do this because we set
2011 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
2012 the density of the events, at the expense of a round-trip
2013 to the server. Given that this will mostly occur on cases
2014 where DISPLAY = :0.0, and given the cost of what the motion
2015 event might do, its a good tradeoff.
2018 _track_canvas->get_pointer (x, y);
2021 if (current_stepping_trackview) {
2022 /* don't keep the persistent stepped trackview if the mouse moves */
2023 current_stepping_trackview = 0;
2024 step_timeout.disconnect ();
2027 if (_session && _session->actively_recording()) {
2028 /* Sorry. no dragging stuff around while we record */
2032 update_join_object_range_location (event->motion.y);
2034 if (_drags->active ()) {
2035 return _drags->motion_handler (event, from_autoscroll);
2042 Editor::can_remove_control_point (ArdourCanvas::Item* item)
2044 ControlPoint* control_point;
2046 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2047 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2051 AutomationLine& line = control_point->line ();
2052 if (dynamic_cast<AudioRegionGainLine*> (&line)) {
2053 /* we shouldn't remove the first or last gain point in region gain lines */
2054 if (line.is_last_point(*control_point) || line.is_first_point(*control_point)) {
2063 Editor::remove_control_point (ArdourCanvas::Item* item)
2065 if (!can_remove_control_point (item)) {
2069 ControlPoint* control_point;
2071 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2072 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2076 control_point->line().remove_point (*control_point);
2080 Editor::edit_control_point (ArdourCanvas::Item* item)
2082 ControlPoint* p = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"));
2085 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2089 ControlPointDialog d (p);
2092 if (d.run () != RESPONSE_ACCEPT) {
2096 p->line().modify_point_y (*p, d.get_y_fraction ());
2100 Editor::edit_notes (TimeAxisViewItem& tavi)
2102 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(&tavi);
2108 MidiRegionView::Selection const & s = mrv->selection();
2114 EditNoteDialog* d = new EditNoteDialog (&(*s.begin())->region_view(), s);
2118 d->signal_response().connect (sigc::bind (sigc::mem_fun (*this, &Editor::note_edit_done), d));
2122 Editor::note_edit_done (int r, EditNoteDialog* d)
2129 Editor::visible_order_range (int* low, int* high) const
2131 *low = TimeAxisView::max_order ();
2134 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
2136 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
2138 if (!rtv->hidden()) {
2140 if (*high < rtv->order()) {
2141 *high = rtv->order ();
2144 if (*low > rtv->order()) {
2145 *low = rtv->order ();
2152 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
2154 /* Either add to or set the set the region selection, unless
2155 this is an alignment click (control used)
2158 if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
2159 TimeAxisView* tv = &rv.get_time_axis_view();
2160 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
2162 if (rtv && rtv->is_track()) {
2163 speed = rtv->track()->speed();
2166 framepos_t where = get_preferred_edit_position();
2170 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
2172 align_region (rv.region(), SyncPoint, (framepos_t) (where * speed));
2174 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
2176 align_region (rv.region(), End, (framepos_t) (where * speed));
2180 align_region (rv.region(), Start, (framepos_t) (where * speed));
2187 Editor::collect_new_region_view (RegionView* rv)
2189 latest_regionviews.push_back (rv);
2193 Editor::collect_and_select_new_region_view (RegionView* rv)
2196 latest_regionviews.push_back (rv);
2200 Editor::cancel_selection ()
2202 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2203 (*i)->hide_selection ();
2206 selection->clear ();
2207 clicked_selection = 0;
2211 Editor::cancel_time_selection ()
2213 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2214 (*i)->hide_selection ();
2216 selection->time.clear ();
2217 clicked_selection = 0;
2221 Editor::point_trim (GdkEvent* event, framepos_t new_bound)
2223 RegionView* rv = clicked_regionview;
2225 /* Choose action dependant on which button was pressed */
2226 switch (event->button.button) {
2228 begin_reversible_command (_("start point trim"));
2230 if (selection->selected (rv)) {
2231 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
2232 i != selection->regions.by_layer().end(); ++i)
2234 if (!(*i)->region()->locked()) {
2235 (*i)->region()->clear_changes ();
2236 (*i)->region()->trim_front (new_bound);
2237 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2242 if (!rv->region()->locked()) {
2243 rv->region()->clear_changes ();
2244 rv->region()->trim_front (new_bound);
2245 _session->add_command(new StatefulDiffCommand (rv->region()));
2249 commit_reversible_command();
2253 begin_reversible_command (_("End point trim"));
2255 if (selection->selected (rv)) {
2257 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
2259 if (!(*i)->region()->locked()) {
2260 (*i)->region()->clear_changes();
2261 (*i)->region()->trim_end (new_bound);
2262 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2268 if (!rv->region()->locked()) {
2269 rv->region()->clear_changes ();
2270 rv->region()->trim_end (new_bound);
2271 _session->add_command (new StatefulDiffCommand (rv->region()));
2275 commit_reversible_command();
2284 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* /*event*/)
2289 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2290 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2294 Location* location = find_location_from_marker (marker, is_start);
2295 location->set_hidden (true, this);
2300 Editor::reposition_zoom_rect (framepos_t start, framepos_t end)
2302 double x1 = sample_to_pixel (start);
2303 double x2 = sample_to_pixel (end);
2304 double y2 = _full_canvas_height - 1.0;
2306 zoom_rect->set (ArdourCanvas::Rect (x1, 1.0, x2, y2));
2311 Editor::mouse_rename_region (ArdourCanvas::Item* /*item*/, GdkEvent* /*event*/)
2313 using namespace Gtkmm2ext;
2315 ArdourPrompter prompter (false);
2317 prompter.set_prompt (_("Name for region:"));
2318 prompter.set_initial_text (clicked_regionview->region()->name());
2319 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
2320 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
2321 prompter.show_all ();
2322 switch (prompter.run ()) {
2323 case Gtk::RESPONSE_ACCEPT:
2325 prompter.get_result(str);
2327 clicked_regionview->region()->set_name (str);
2336 Editor::mouse_brush_insert_region (RegionView* rv, framepos_t pos)
2338 /* no brushing without a useful snap setting */
2340 switch (_snap_mode) {
2342 return; /* can't work because it allows region to be placed anywhere */
2347 switch (_snap_type) {
2355 /* don't brush a copy over the original */
2357 if (pos == rv->region()->position()) {
2361 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
2363 if (rtv == 0 || !rtv->is_track()) {
2367 boost::shared_ptr<Playlist> playlist = rtv->playlist();
2368 double speed = rtv->track()->speed();
2370 playlist->clear_changes ();
2371 boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region(), true));
2372 playlist->add_region (new_region, (framepos_t) (pos * speed));
2373 _session->add_command (new StatefulDiffCommand (playlist));
2375 // playlist is frozen, so we have to update manually XXX this is disgusting
2377 playlist->RegionAdded (new_region); /* EMIT SIGNAL */
2381 Editor::track_height_step_timeout ()
2383 if (get_microseconds() - last_track_height_step_timestamp < 250000) {
2384 current_stepping_trackview = 0;
2391 Editor::add_region_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2393 assert (region_view);
2395 if (!region_view->region()->playlist()) {
2399 switch (Config->get_edit_mode()) {
2401 _drags->add (new RegionSpliceDrag (this, item, region_view, selection->regions.by_layer()));
2404 _drags->add (new RegionRippleDrag (this, item, region_view, selection->regions.by_layer()));
2407 _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), false, false));
2414 Editor::add_region_copy_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2416 assert (region_view);
2418 if (!region_view->region()->playlist()) {
2422 _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), false, true));
2426 Editor::add_region_brush_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2428 assert (region_view);
2430 if (!region_view->region()->playlist()) {
2434 if (Config->get_edit_mode() == Splice || Config->get_edit_mode() == Ripple) {
2438 _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), true, false));
2440 begin_reversible_command (Operations::drag_region_brush);
2443 /** Start a grab where a time range is selected, track(s) are selected, and the
2444 * user clicks and drags a region with a modifier in order to create a new region containing
2445 * the section of the clicked region that lies within the time range.
2448 Editor::start_selection_grab (ArdourCanvas::Item* /*item*/, GdkEvent* event)
2450 if (clicked_regionview == 0) {
2454 /* lets try to create new Region for the selection */
2456 vector<boost::shared_ptr<Region> > new_regions;
2457 create_region_from_selection (new_regions);
2459 if (new_regions.empty()) {
2463 /* XXX fix me one day to use all new regions */
2465 boost::shared_ptr<Region> region (new_regions.front());
2467 /* add it to the current stream/playlist.
2469 tricky: the streamview for the track will add a new regionview. we will
2470 catch the signal it sends when it creates the regionview to
2471 set the regionview we want to then drag.
2474 latest_regionviews.clear();
2475 sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
2477 /* A selection grab currently creates two undo/redo operations, one for
2478 creating the new region and another for moving it.
2481 begin_reversible_command (Operations::selection_grab);
2483 boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
2485 playlist->clear_changes ();
2486 clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
2487 _session->add_command(new StatefulDiffCommand (playlist));
2489 commit_reversible_command ();
2493 if (latest_regionviews.empty()) {
2494 /* something went wrong */
2498 /* we need to deselect all other regionviews, and select this one
2499 i'm ignoring undo stuff, because the region creation will take care of it
2501 selection->set (latest_regionviews);
2503 _drags->set (new RegionMoveDrag (this, latest_regionviews.front()->get_canvas_group(), latest_regionviews.front(), latest_regionviews, false, false), event);
2509 if (_drags->active ()) {
2512 selection->clear ();
2517 Editor::set_internal_edit (bool yn)
2519 if (_internal_editing == yn) {
2523 _internal_editing = yn;
2526 pre_internal_mouse_mode = mouse_mode;
2527 pre_internal_snap_type = _snap_type;
2528 pre_internal_snap_mode = _snap_mode;
2530 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2531 (*i)->enter_internal_edit_mode ();
2534 set_snap_to (internal_snap_type);
2535 set_snap_mode (internal_snap_mode);
2539 internal_snap_mode = _snap_mode;
2540 internal_snap_type = _snap_type;
2542 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2543 (*i)->leave_internal_edit_mode ();
2546 if (mouse_mode == MouseDraw && pre_internal_mouse_mode != MouseDraw) {
2547 /* we were drawing .. flip back to something sensible */
2548 set_mouse_mode (pre_internal_mouse_mode);
2551 set_snap_to (pre_internal_snap_type);
2552 set_snap_mode (pre_internal_snap_mode);
2555 reset_canvas_cursor ();
2558 /** Update _join_object_range_state which indicate whether we are over the top
2559 * or bottom half of a route view, used by the `join object/range' tool
2560 * mode. Coordinates in canvas space.
2563 Editor::update_join_object_range_location (double y)
2565 if (_internal_editing || !get_smart_mode()) {
2566 _join_object_range_state = JOIN_OBJECT_RANGE_NONE;
2570 JoinObjectRangeState const old = _join_object_range_state;
2572 if (mouse_mode == MouseObject) {
2573 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2574 } else if (mouse_mode == MouseRange) {
2575 _join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
2578 if (entered_regionview) {
2580 ArdourCanvas::Duple const item_space = entered_regionview->get_canvas_group()->canvas_to_item (ArdourCanvas::Duple (0, y));
2581 double const c = item_space.y / entered_regionview->height();
2583 _join_object_range_state = c <= 0.5 ? JOIN_OBJECT_RANGE_RANGE : JOIN_OBJECT_RANGE_OBJECT;
2585 if (_join_object_range_state != old) {
2586 set_canvas_cursor (which_track_cursor ());
2589 } else if (entered_track) {
2591 RouteTimeAxisView* entered_route_view = dynamic_cast<RouteTimeAxisView*> (entered_track);
2593 if (entered_route_view) {
2598 entered_route_view->canvas_display()->canvas_to_item (cx, cy);
2600 double track_height = entered_route_view->view()->child_height();
2601 if (Config->get_show_name_highlight()) {
2602 track_height -= TimeAxisViewItem::NAME_HIGHLIGHT_SIZE;
2604 double const c = cy / track_height;
2608 _join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
2610 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2614 /* Other kinds of tracks use object mode */
2615 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2618 if (_join_object_range_state != old) {
2619 set_canvas_cursor (which_track_cursor ());
2625 Editor::effective_mouse_mode () const
2627 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
2629 } else if (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE) {
2637 Editor::remove_midi_note (ArdourCanvas::Item* item, GdkEvent *)
2639 NoteBase* e = reinterpret_cast<NoteBase*> (item->get_data ("notebase"));
2642 e->region_view().delete_note (e->note ());
2645 /** Obtain the pointer position in canvas coordinates */
2647 Editor::get_pointer_position (double& x, double& y) const
2650 _track_canvas->get_pointer (px, py);
2651 _track_canvas->window_to_canvas (px, py, x, y);