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 Glib::RefPtr<Action> act;
239 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-range"));
243 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-cut"));
247 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-object"));
251 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-draw"));
255 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-gain"));
259 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-zoom"));
263 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-timefx"));
267 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-audition"));
273 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
276 /* go there and back to ensure that the toggled handler is called to set up mouse_mode */
277 tact->set_active (false);
278 tact->set_active (true);
280 //NOTE: this will result in a call to mouse_mode_toggled which does the heavy lifting
284 Editor::mouse_mode_toggled (MouseMode m)
286 Glib::RefPtr<Action> act;
287 Glib::RefPtr<ToggleAction> tact;
291 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-range"));
295 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-object"));
299 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-cut"));
303 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-draw"));
307 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-gain"));
311 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-zoom"));
315 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-timefx"));
319 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-audition"));
325 tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
328 if (!tact->get_active()) {
329 /* this was just the notification that the old mode has been
330 * left. we'll get called again with the new mode active in a
338 act = ActionManager::get_action (X_("MouseMode"), X_("toggle-internal-edit"));
339 tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
340 tact->set_active (true);
346 if (_session && mouse_mode == MouseAudition) {
347 /* stop transport and reset default speed to avoid oddness with
349 _session->request_transport_speed (0.0, true);
356 /* this should generate a new enter event which will
357 trigger the appropiate cursor.
361 _track_canvas->re_enter ();
364 set_gain_envelope_visibility ();
366 update_time_selection_display ();
368 MouseModeChanged (); /* EMIT SIGNAL */
372 Editor::update_time_selection_display ()
374 if (smart_mode_action->get_active()) {
375 /* not sure what to do here */
376 if (mouse_mode == MouseObject) {
380 switch (mouse_mode) {
382 selection->clear_objects ();
385 selection->clear_time ();
392 Editor::step_mouse_mode (bool next)
394 switch (current_mouse_mode()) {
397 if (Profile->get_sae()) {
398 set_mouse_mode (MouseZoom);
400 set_mouse_mode (MouseRange);
403 set_mouse_mode (MouseTimeFX);
408 if (next) set_mouse_mode (MouseDraw);
409 else set_mouse_mode (MouseCut);
413 if (next) set_mouse_mode (MouseRange);
414 else set_mouse_mode (MouseDraw);
418 if (next) set_mouse_mode (MouseCut);
419 else set_mouse_mode (MouseRange);
424 if (Profile->get_sae()) {
425 set_mouse_mode (MouseTimeFX);
427 set_mouse_mode (MouseGain);
430 if (Profile->get_sae()) {
431 set_mouse_mode (MouseObject);
433 set_mouse_mode (MouseDraw);
439 if (next) set_mouse_mode (MouseTimeFX);
440 else set_mouse_mode (MouseZoom);
445 set_mouse_mode (MouseAudition);
447 if (Profile->get_sae()) {
448 set_mouse_mode (MouseZoom);
450 set_mouse_mode (MouseGain);
456 if (next) set_mouse_mode (MouseObject);
457 else set_mouse_mode (MouseTimeFX);
463 Editor::toggle_internal_editing_from_double_click (GdkEvent* event)
465 if (_drags->active()) {
466 _drags->end_grab (event);
469 ActionManager::do_action ("MouseMode", "toggle-internal-edit");
471 /* prevent reversion of edit cursor on button release */
473 pre_press_cursor = 0;
479 Editor::button_selection (ArdourCanvas::Item* /*item*/, GdkEvent* event, ItemType item_type)
481 /* in object/audition/timefx/gain-automation mode,
482 any button press sets the selection if the object
483 can be selected. this is a bit of hack, because
484 we want to avoid this if the mouse operation is a
487 note: not dbl-click or triple-click
489 Also note that there is no region selection in internal edit mode, otherwise
490 for operations operating on the selection (e.g. cut) it is not obvious whether
491 to cut notes or regions.
494 MouseMode eff_mouse_mode = effective_mouse_mode ();
496 if (eff_mouse_mode == MouseCut) {
497 /* never change selection in cut mode */
501 if (get_smart_mode() && eff_mouse_mode == MouseRange && event->button.button == 3 && item_type == RegionItem) {
502 /* context clicks are always about object properties, even if
503 we're in range mode within smart mode.
505 eff_mouse_mode = MouseObject;
508 /* special case: allow drag of region fade in/out in object mode with join object/range enabled */
509 if (get_smart_mode()) {
511 case FadeInHandleItem:
512 case FadeInTrimHandleItem:
513 case FadeOutHandleItem:
514 case FadeOutTrimHandleItem:
515 eff_mouse_mode = MouseObject;
522 if (((mouse_mode != MouseObject) &&
523 (mouse_mode != MouseAudition || item_type != RegionItem) &&
524 (mouse_mode != MouseTimeFX || item_type != RegionItem) &&
525 (mouse_mode != MouseGain) &&
526 (mouse_mode != MouseDraw)) ||
527 ((event->type != GDK_BUTTON_PRESS && event->type != GDK_BUTTON_RELEASE) || event->button.button > 3) ||
528 (internal_editing() && mouse_mode != MouseTimeFX)) {
533 if (event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) {
535 if ((event->button.state & Keyboard::RelevantModifierKeyMask) && event->button.button != 1) {
537 /* almost no selection action on modified button-2 or button-3 events */
539 if (item_type != RegionItem && event->button.button != 2) {
545 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
546 bool press = (event->type == GDK_BUTTON_PRESS);
551 if (eff_mouse_mode != MouseRange) {
552 set_selected_regionview_from_click (press, op);
554 /* don't change the selection unless the
555 clicked track is not currently selected. if
556 so, "collapse" the selection to just this
559 if (!selection->selected (clicked_axisview)) {
560 set_selected_track_as_side_effect (Selection::Set);
564 if (eff_mouse_mode != MouseRange) {
565 set_selected_regionview_from_click (press, op);
570 case RegionViewNameHighlight:
572 case LeftFrameHandle:
573 case RightFrameHandle:
574 case FadeInHandleItem:
575 case FadeInTrimHandleItem:
577 case FadeOutHandleItem:
578 case FadeOutTrimHandleItem:
580 case StartCrossFadeItem:
581 case EndCrossFadeItem:
582 if (get_smart_mode() || eff_mouse_mode != MouseRange) {
583 set_selected_regionview_from_click (press, op);
584 } else if (event->type == GDK_BUTTON_PRESS) {
585 set_selected_track_as_side_effect (op);
589 case ControlPointItem:
590 set_selected_track_as_side_effect (op);
591 if (eff_mouse_mode != MouseRange) {
592 set_selected_control_point_from_click (press, op);
597 /* for context click, select track */
598 if (event->button.button == 3) {
599 selection->clear_tracks ();
600 set_selected_track_as_side_effect (op);
604 case AutomationTrackItem:
605 set_selected_track_as_side_effect (op);
614 Editor::button_press_handler_1 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
616 /* single mouse clicks on any of these item types operate
617 independent of mouse mode, mostly because they are
618 not on the main track canvas or because we want
623 case PlayheadCursorItem:
624 _drags->set (new CursorDrag (this, *playhead_cursor, true), event);
628 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
629 hide_marker (item, event);
631 _drags->set (new MarkerDrag (this, item), event);
635 case TempoMarkerItem:
637 TempoMarker* m = reinterpret_cast<TempoMarker*> (item->get_data ("marker"));
640 new TempoMarkerDrag (
643 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
650 case MeterMarkerItem:
652 MeterMarker* m = reinterpret_cast<MeterMarker*> (item->get_data ("marker"));
655 new MeterMarkerDrag (
658 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
666 _drags->set (new VideoTimeLineDrag (this, item), event);
673 case TimecodeRulerItem:
674 case SamplesRulerItem:
675 case MinsecRulerItem:
677 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
678 _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
684 case RangeMarkerBarItem:
685 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
686 _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
688 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateRangeMarker), event);
693 case CdMarkerBarItem:
694 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
695 _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
697 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateCDMarker), event);
702 case TransportMarkerBarItem:
703 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
704 _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
706 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateTransportMarker), event);
715 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
716 /* special case: allow trim of range selections in joined object mode;
717 in theory eff should equal MouseRange in this case, but it doesn't
718 because entering the range selection canvas item results in entered_regionview
719 being set to 0, so update_join_object_range_location acts as if we aren't
722 if (item_type == StartSelectionTrimItem) {
723 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
724 } else if (item_type == EndSelectionTrimItem) {
725 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
729 Editing::MouseMode eff = effective_mouse_mode ();
731 /* special case: allow drag of region fade in/out in object mode with join object/range enabled */
732 if (get_smart_mode()) {
734 case FadeInHandleItem:
735 case FadeInTrimHandleItem:
736 case FadeOutHandleItem:
737 case FadeOutTrimHandleItem:
745 /* there is no Range mode when in internal edit mode */
746 if (eff == MouseRange && internal_editing()) {
753 case StartSelectionTrimItem:
754 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
757 case EndSelectionTrimItem:
758 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
762 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
763 start_selection_grab (item, event);
765 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
766 /* grab selection for moving */
767 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionMove), event);
769 double const y = event->button.y;
770 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
772 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
773 if ( get_smart_mode() && atv) {
774 /* smart "join" mode: drag automation */
775 _drags->set (new AutomationRangeDrag (this, atv, selection->time), event, _cursors->up_down);
777 /* this was debated, but decided the more common action was to
778 make a new selection */
779 _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:
860 _drags->set (new RegionCutDrag (this, item), event, current_canvas_cursor);
871 /* Existing note: allow trimming/motion */
872 if (internal_editing()) {
873 NoteBase* cn = reinterpret_cast<NoteBase*> (item->get_data ("notebase"));
875 if (cn->big_enough_to_trim() && cn->mouse_near_ends()) {
876 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
878 _drags->set (new NoteDrag (this, item), event);
888 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)) &&
889 event->type == GDK_BUTTON_PRESS) {
891 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
893 } else if (event->type == GDK_BUTTON_PRESS) {
896 case FadeInHandleItem:
898 _drags->set (new FadeInDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), selection->regions), event, _cursors->fade_in);
902 case FadeOutHandleItem:
904 _drags->set (new FadeOutDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), selection->regions), event, _cursors->fade_out);
908 case StartCrossFadeItem:
909 case EndCrossFadeItem:
910 /* we might allow user to grab inside the fade to trim a region with preserve_fade_anchor. for not this is not fully implemented */
911 // if (!clicked_regionview->region()->locked()) {
912 // _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer(), true), event);
917 case FeatureLineItem:
919 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier)) {
920 remove_transient(item);
924 _drags->set (new FeatureLineDrag (this, item), event);
930 if (dynamic_cast<AutomationRegionView*> (clicked_regionview)) {
931 /* click on an automation region view; do nothing here and let the ARV's signal handler
937 if (internal_editing ()) {
941 /* click on a normal region view */
942 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
943 add_region_copy_drag (item, event, clicked_regionview);
944 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
945 add_region_brush_drag (item, event, clicked_regionview);
947 add_region_drag (item, event, clicked_regionview);
951 // if (!internal_editing() && (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE && !selection->regions.empty())) {
952 // _drags->add (new SelectionDrag (this, clicked_axisview->get_selection_rect (clicked_selection)->rect, SelectionDrag::SelectionMove));
955 _drags->start_grab (event);
959 case RegionViewNameHighlight:
960 case LeftFrameHandle:
961 case RightFrameHandle:
962 if (!clicked_regionview->region()->locked()) {
963 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
968 case FadeInTrimHandleItem:
969 case FadeOutTrimHandleItem:
970 if (!clicked_regionview->region()->locked()) {
971 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer(), true), event);
978 /* rename happens on edit clicks */
979 if (clicked_regionview->get_name_highlight()) {
980 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer()), event);
986 case ControlPointItem:
987 _drags->set (new ControlPointDrag (this, item), event);
991 case AutomationLineItem:
992 _drags->set (new LineDrag (this, item), event);
997 if (internal_editing()) {
998 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
999 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
1003 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
1007 case AutomationTrackItem:
1009 TimeAxisView* parent = clicked_axisview->get_parent ();
1010 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (clicked_axisview);
1012 if (parent && dynamic_cast<MidiTimeAxisView*> (parent) && atv->show_regions ()) {
1014 RouteTimeAxisView* p = dynamic_cast<RouteTimeAxisView*> (parent);
1016 boost::shared_ptr<Playlist> pl = p->track()->playlist ();
1017 if (pl->n_regions() == 0) {
1018 /* Parent has no regions; create one so that we have somewhere to put automation */
1019 _drags->set (new RegionCreateDrag (this, item, parent), event);
1021 /* See if there's a region before the click that we can extend, and extend it if so */
1022 framepos_t const t = canvas_event_sample (event);
1023 boost::shared_ptr<Region> prev = pl->find_next_region (t, End, -1);
1025 _drags->set (new RegionCreateDrag (this, item, parent), event);
1027 prev->set_length (t - prev->position ());
1031 /* rubberband drag to select automation points */
1032 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
1039 if ( get_smart_mode() ) {
1040 /* we're in "smart" joined mode, and we've clicked on a Selection */
1041 double const y = event->button.y;
1042 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
1044 /* if we're over an automation track, start a drag of its data */
1045 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
1047 _drags->set (new AutomationRangeDrag (this, atv, selection->time), event, _cursors->up_down);
1050 /* if we're over a track and a region, and in the `object' part of a region,
1051 put a selection around the region and drag both
1053 /* RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1054 if (rtv && _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
1055 boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track> (rtv->route ());
1057 boost::shared_ptr<Playlist> pl = t->playlist ();
1060 boost::shared_ptr<Region> r = pl->top_region_at (canvas_event_sample (event));
1062 RegionView* rv = rtv->view()->find_view (r);
1063 clicked_selection = select_range (rv->region()->position(),
1064 rv->region()->last_frame()+1);
1065 _drags->add (new SelectionDrag (this, item, SelectionDrag::SelectionMove));
1066 list<RegionView*> rvs;
1068 _drags->add (new RegionMoveDrag (this, item, rv, rvs, false, false));
1069 _drags->start_grab (event);
1093 switch (item_type) {
1095 _drags->set (new LineDrag (this, item), event);
1098 case ControlPointItem:
1099 _drags->set (new ControlPointDrag (this, item), event);
1105 AudioRegionView* arv = dynamic_cast<AudioRegionView *> (clicked_regionview);
1107 _drags->set (new AutomationRangeDrag (this, arv, selection->time), event, _cursors->up_down);
1108 _drags->start_grab (event);
1114 case AutomationLineItem:
1115 _drags->set (new LineDrag (this, item), event);
1125 if (event->type == GDK_BUTTON_PRESS) {
1126 _drags->set (new MouseZoomDrag (this, item), event);
1133 if (internal_editing() && item_type == NoteItem ) {
1134 /* drag notes if we're in internal edit mode */
1135 NoteBase* cn = reinterpret_cast<NoteBase*>(item->get_data ("notebase"));
1137 if (cn->big_enough_to_trim()) {
1138 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
1141 } else if (clicked_regionview) {
1143 _drags->set (new TimeFXDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1149 _drags->set (new ScrubDrag (this, item), event);
1150 scrub_reversals = 0;
1151 scrub_reverse_distance = 0;
1152 last_scrub_x = event->button.x;
1153 scrubbing_direction = 0;
1154 push_canvas_cursor (_cursors->transparent);
1166 Editor::button_press_handler_2 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1168 Editing::MouseMode const eff = effective_mouse_mode ();
1171 switch (item_type) {
1173 if (internal_editing ()) {
1174 /* no region drags in internal edit mode */
1178 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
1179 add_region_copy_drag (item, event, clicked_regionview);
1181 add_region_drag (item, event, clicked_regionview);
1183 _drags->start_grab (event);
1186 case ControlPointItem:
1187 _drags->set (new ControlPointDrag (this, item), event);
1195 switch (item_type) {
1196 case RegionViewNameHighlight:
1197 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1201 case LeftFrameHandle:
1202 case RightFrameHandle:
1203 if (!internal_editing ()) {
1204 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1209 case RegionViewName:
1210 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer()), event);
1224 /* relax till release */
1230 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1231 temporal_zoom_to_frame (false, canvas_event_sample (event));
1233 temporal_zoom_to_frame (true, canvas_event_sample(event));
1246 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1248 if (event->type == GDK_2BUTTON_PRESS) {
1249 _drags->mark_double_click ();
1250 gdk_pointer_ungrab (GDK_CURRENT_TIME);
1254 if (event->type != GDK_BUTTON_PRESS) {
1258 pre_press_cursor = current_canvas_cursor;
1260 _track_canvas->grab_focus();
1262 if (_session && _session->actively_recording()) {
1266 if (internal_editing()) {
1267 bool leave_internal_edit_mode = false;
1269 switch (item_type) {
1274 if (!dynamic_cast<MidiRegionView*> (clicked_regionview) && !dynamic_cast<AutomationRegionView*> (clicked_regionview)) {
1275 leave_internal_edit_mode = true;
1279 case PlayheadCursorItem:
1281 case TempoMarkerItem:
1282 case MeterMarkerItem:
1286 case RangeMarkerBarItem:
1287 case CdMarkerBarItem:
1288 case TransportMarkerBarItem:
1290 case TimecodeRulerItem:
1291 case SamplesRulerItem:
1292 case MinsecRulerItem:
1294 /* button press on these items never does anything to
1295 change the editing mode.
1303 if (leave_internal_edit_mode) {
1304 ActionManager::do_action ("MouseMode", "toggle-internal-edit");
1308 button_selection (item, event, item_type);
1310 if (!_drags->active () &&
1311 (Keyboard::is_delete_event (&event->button) ||
1312 Keyboard::is_context_menu_event (&event->button) ||
1313 Keyboard::is_edit_event (&event->button))) {
1315 /* handled by button release */
1319 //not rolling, range mode click + join_play_range : locate the PH here
1320 if ( !_drags->active () && !_session->transport_rolling() && ( effective_mouse_mode() == MouseRange ) && Config->get_follow_edits() ) {
1321 framepos_t where = canvas_event_sample (event);
1323 _session->request_locate (where, false);
1326 switch (event->button.button) {
1328 return button_press_handler_1 (item, event, item_type);
1332 return button_press_handler_2 (item, event, item_type);
1339 return button_press_dispatch (&event->button);
1348 Editor::button_press_dispatch (GdkEventButton* ev)
1350 /* this function is intended only for buttons 4 and above.
1353 Gtkmm2ext::MouseButton b (ev->state, ev->button);
1354 return button_bindings->activate (b, Gtkmm2ext::Bindings::Press);
1358 Editor::button_release_dispatch (GdkEventButton* ev)
1360 /* this function is intended only for buttons 4 and above.
1363 Gtkmm2ext::MouseButton b (ev->state, ev->button);
1364 return button_bindings->activate (b, Gtkmm2ext::Bindings::Release);
1368 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1370 framepos_t where = canvas_event_sample (event);
1371 AutomationTimeAxisView* atv = 0;
1373 if (pre_press_cursor) {
1374 set_canvas_cursor (pre_press_cursor);
1375 pre_press_cursor = 0;
1378 /* no action if we're recording */
1380 if (_session && _session->actively_recording()) {
1384 /* see if we're finishing a drag */
1386 bool were_dragging = false;
1387 if (_drags->active ()) {
1388 bool const r = _drags->end_grab (event);
1390 /* grab dragged, so do nothing else */
1394 were_dragging = true;
1397 update_region_layering_order_editor ();
1399 /* edit events get handled here */
1401 if (!_drags->active () && Keyboard::is_edit_event (&event->button)) {
1402 switch (item_type) {
1404 show_region_properties ();
1407 case TempoMarkerItem: {
1409 TempoMarker* tempo_marker;
1411 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
1412 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
1416 if ((tempo_marker = dynamic_cast<TempoMarker*> (marker)) == 0) {
1417 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
1421 edit_tempo_marker (*tempo_marker);
1425 case MeterMarkerItem: {
1427 MeterMarker* meter_marker;
1429 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
1430 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
1434 if ((meter_marker = dynamic_cast<MeterMarker*> (marker)) == 0) {
1435 fatal << _("programming error: marker for meter is not a meter marker!") << endmsg;
1438 edit_meter_marker (*meter_marker);
1442 case RegionViewName:
1443 if (clicked_regionview->name_active()) {
1444 return mouse_rename_region (item, event);
1448 case ControlPointItem:
1449 edit_control_point (item);
1458 /* context menu events get handled here */
1459 if (Keyboard::is_context_menu_event (&event->button)) {
1461 context_click_event = *event;
1463 if (!_drags->active ()) {
1465 /* no matter which button pops up the context menu, tell the menu
1466 widget to use button 1 to drive menu selection.
1469 switch (item_type) {
1471 case FadeInHandleItem:
1472 case FadeInTrimHandleItem:
1473 case StartCrossFadeItem:
1474 case LeftFrameHandle:
1475 popup_xfade_in_context_menu (1, event->button.time, item, item_type);
1479 case FadeOutHandleItem:
1480 case FadeOutTrimHandleItem:
1481 case EndCrossFadeItem:
1482 case RightFrameHandle:
1483 popup_xfade_out_context_menu (1, event->button.time, item, item_type);
1487 popup_track_context_menu (1, event->button.time, item_type, false);
1491 case RegionViewNameHighlight:
1492 case RegionViewName:
1493 popup_track_context_menu (1, event->button.time, item_type, false);
1497 popup_track_context_menu (1, event->button.time, item_type, true);
1500 case AutomationTrackItem:
1501 popup_track_context_menu (1, event->button.time, item_type, false);
1505 case RangeMarkerBarItem:
1506 case TransportMarkerBarItem:
1507 case CdMarkerBarItem:
1511 case TimecodeRulerItem:
1512 case SamplesRulerItem:
1513 case MinsecRulerItem:
1515 popup_ruler_menu (where, item_type);
1519 marker_context_menu (&event->button, item);
1522 case TempoMarkerItem:
1523 tempo_or_meter_marker_context_menu (&event->button, item);
1526 case MeterMarkerItem:
1527 tempo_or_meter_marker_context_menu (&event->button, item);
1530 case CrossfadeViewItem:
1531 popup_track_context_menu (1, event->button.time, item_type, false);
1534 case ControlPointItem:
1535 popup_control_point_context_menu (item, event);
1546 /* delete events get handled here */
1548 Editing::MouseMode const eff = effective_mouse_mode ();
1550 if (!_drags->active () && Keyboard::is_delete_event (&event->button)) {
1552 switch (item_type) {
1553 case TempoMarkerItem:
1554 remove_tempo_marker (item);
1557 case MeterMarkerItem:
1558 remove_meter_marker (item);
1562 remove_marker (*item, event);
1566 if (eff == MouseObject) {
1567 remove_clicked_region ();
1571 case ControlPointItem:
1572 remove_control_point (item);
1576 remove_midi_note (item, event);
1585 switch (event->button.button) {
1588 switch (item_type) {
1589 /* see comments in button_press_handler */
1590 case PlayheadCursorItem:
1593 case AutomationLineItem:
1594 case StartSelectionTrimItem:
1595 case EndSelectionTrimItem:
1599 if (!_dragging_playhead) {
1600 snap_to_with_modifier (where, event, 0, true);
1601 mouse_add_new_marker (where);
1605 case CdMarkerBarItem:
1606 if (!_dragging_playhead) {
1607 // if we get here then a dragged range wasn't done
1608 snap_to_with_modifier (where, event, 0, true);
1609 mouse_add_new_marker (where, true);
1614 if (!_dragging_playhead) {
1615 snap_to_with_modifier (where, event);
1616 mouse_add_new_tempo_event (where);
1621 if (!_dragging_playhead) {
1622 mouse_add_new_meter_event (pixel_to_sample (event->button.x));
1627 case TimecodeRulerItem:
1628 case SamplesRulerItem:
1629 case MinsecRulerItem:
1640 switch (item_type) {
1641 case AutomationTrackItem:
1642 atv = dynamic_cast<AutomationTimeAxisView*>(clicked_axisview);
1644 bool with_guard_points = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
1645 atv->add_automation_event (event, where, event->button.y, with_guard_points);
1655 switch (item_type) {
1658 /* check that we didn't drag before releasing, since
1659 its really annoying to create new control
1660 points when doing this.
1662 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (clicked_regionview);
1663 if (!were_dragging && arv) {
1664 bool with_guard_points = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
1665 arv->add_gain_point_event (item, event, with_guard_points);
1671 case AutomationTrackItem: {
1672 bool with_guard_points = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
1673 dynamic_cast<AutomationTimeAxisView*>(clicked_axisview)->
1674 add_automation_event (event, where, event->button.y, with_guard_points);
1684 pop_canvas_cursor ();
1685 if (scrubbing_direction == 0) {
1686 /* no drag, just a click */
1687 switch (item_type) {
1689 play_selected_region ();
1695 /* make sure we stop */
1696 _session->request_transport_speed (0.0);
1705 /* do any (de)selection operations that should occur on button release */
1706 button_selection (item, event, item_type);
1715 switch (item_type) {
1717 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1719 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier|Keyboard::SecondaryModifier))) {
1722 // Button2 click is unused
1737 // x_style_paste (where, 1.0);
1758 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1765 /* by the time we reach here, entered_regionview and entered trackview
1766 * will have already been set as appropriate. Things are done this
1767 * way because this method isn't passed a pointer to a variable type of
1768 * thing that is entered (which may or may not be canvas item).
1769 * (e.g. the actual entered regionview)
1772 choose_canvas_cursor_on_entry (&event->crossing, item_type);
1774 switch (item_type) {
1775 case ControlPointItem:
1776 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1777 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1780 fraction = 1.0 - (cp->get_y() / cp->line().height());
1782 _verbose_cursor->set (cp->line().get_verbose_cursor_string (fraction));
1783 _verbose_cursor->show ();
1788 if (mouse_mode == MouseGain) {
1789 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1791 line->set_outline_color (ARDOUR_UI::config()->get_canvasvar_EnteredGainLine());
1796 case AutomationLineItem:
1797 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1798 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1800 line->set_outline_color (ARDOUR_UI::config()->get_canvasvar_EnteredAutomationLine());
1805 case AutomationTrackItem:
1806 AutomationTimeAxisView* atv;
1807 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1808 clear_entered_track = false;
1809 set_entered_track (atv);
1814 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1817 entered_marker = marker;
1818 marker->set_color_rgba (ARDOUR_UI::config()->get_canvasvar_EnteredMarker());
1820 case MeterMarkerItem:
1821 case TempoMarkerItem:
1824 case FadeInHandleItem:
1825 case FadeInTrimHandleItem:
1826 if (mouse_mode == MouseObject && !internal_editing()) {
1827 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
1829 RegionView* rv = static_cast<RegionView*>(item->get_data ("regionview"));
1830 rect->set_fill_color (rv->get_fill_color());
1835 case FadeOutHandleItem:
1836 case FadeOutTrimHandleItem:
1837 if (mouse_mode == MouseObject && !internal_editing()) {
1838 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
1840 RegionView* rv = static_cast<RegionView*>(item->get_data ("regionview"));
1841 rect->set_fill_color (rv->get_fill_color ());
1846 case FeatureLineItem:
1848 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1849 line->set_outline_color (0xFF0000FF);
1860 /* third pass to handle entered track status in a comprehensible way.
1863 switch (item_type) {
1865 case AutomationLineItem:
1866 case ControlPointItem:
1867 /* these do not affect the current entered track state */
1868 clear_entered_track = false;
1871 case AutomationTrackItem:
1872 /* handled above already */
1884 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent*, ItemType item_type)
1892 switch (item_type) {
1893 case ControlPointItem:
1894 _verbose_cursor->hide ();
1898 case AutomationLineItem:
1899 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1901 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1903 line->set_outline_color (al->get_line_color());
1909 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1913 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
1914 location_flags_changed (loc, this);
1917 case MeterMarkerItem:
1918 case TempoMarkerItem:
1921 case FadeInTrimHandleItem:
1922 case FadeOutTrimHandleItem:
1923 case FadeInHandleItem:
1924 case FadeOutHandleItem:
1926 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
1928 rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_InactiveFadeHandle());
1933 case AutomationTrackItem:
1936 case FeatureLineItem:
1938 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1939 line->set_outline_color (ARDOUR_UI::config()->get_canvasvar_ZeroLine());
1951 Editor::scrub (framepos_t frame, double current_x)
1955 if (scrubbing_direction == 0) {
1957 _session->request_locate (frame, false);
1958 _session->request_transport_speed (0.1);
1959 scrubbing_direction = 1;
1963 if (last_scrub_x > current_x) {
1965 /* pointer moved to the left */
1967 if (scrubbing_direction > 0) {
1969 /* we reversed direction to go backwards */
1972 scrub_reverse_distance += (int) (last_scrub_x - current_x);
1976 /* still moving to the left (backwards) */
1978 scrub_reversals = 0;
1979 scrub_reverse_distance = 0;
1981 delta = 0.01 * (last_scrub_x - current_x);
1982 _session->request_transport_speed_nonzero (_session->transport_speed() - delta);
1986 /* pointer moved to the right */
1988 if (scrubbing_direction < 0) {
1989 /* we reversed direction to go forward */
1992 scrub_reverse_distance += (int) (current_x - last_scrub_x);
1995 /* still moving to the right */
1997 scrub_reversals = 0;
1998 scrub_reverse_distance = 0;
2000 delta = 0.01 * (current_x - last_scrub_x);
2001 _session->request_transport_speed_nonzero (_session->transport_speed() + delta);
2005 /* if there have been more than 2 opposite motion moves detected, or one that moves
2006 back more than 10 pixels, reverse direction
2009 if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
2011 if (scrubbing_direction > 0) {
2012 /* was forwards, go backwards */
2013 _session->request_transport_speed (-0.1);
2014 scrubbing_direction = -1;
2016 /* was backwards, go forwards */
2017 _session->request_transport_speed (0.1);
2018 scrubbing_direction = 1;
2021 scrub_reverse_distance = 0;
2022 scrub_reversals = 0;
2026 last_scrub_x = current_x;
2030 Editor::motion_handler (ArdourCanvas::Item* /*item*/, GdkEvent* event, bool from_autoscroll)
2032 _last_motion_y = event->motion.y;
2034 if (event->motion.is_hint) {
2037 /* We call this so that MOTION_NOTIFY events continue to be
2038 delivered to the canvas. We need to do this because we set
2039 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
2040 the density of the events, at the expense of a round-trip
2041 to the server. Given that this will mostly occur on cases
2042 where DISPLAY = :0.0, and given the cost of what the motion
2043 event might do, its a good tradeoff.
2046 _track_canvas->get_pointer (x, y);
2049 if (current_stepping_trackview) {
2050 /* don't keep the persistent stepped trackview if the mouse moves */
2051 current_stepping_trackview = 0;
2052 step_timeout.disconnect ();
2055 if (_session && _session->actively_recording()) {
2056 /* Sorry. no dragging stuff around while we record */
2060 update_join_object_range_location (event->motion.y);
2062 if (_drags->active ()) {
2063 return _drags->motion_handler (event, from_autoscroll);
2070 Editor::can_remove_control_point (ArdourCanvas::Item* item)
2072 ControlPoint* control_point;
2074 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2075 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2079 AutomationLine& line = control_point->line ();
2080 if (dynamic_cast<AudioRegionGainLine*> (&line)) {
2081 /* we shouldn't remove the first or last gain point in region gain lines */
2082 if (line.is_last_point(*control_point) || line.is_first_point(*control_point)) {
2091 Editor::remove_control_point (ArdourCanvas::Item* item)
2093 if (!can_remove_control_point (item)) {
2097 ControlPoint* control_point;
2099 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2100 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2104 control_point->line().remove_point (*control_point);
2108 Editor::edit_control_point (ArdourCanvas::Item* item)
2110 ControlPoint* p = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"));
2113 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2117 ControlPointDialog d (p);
2120 if (d.run () != RESPONSE_ACCEPT) {
2124 p->line().modify_point_y (*p, d.get_y_fraction ());
2128 Editor::edit_notes (TimeAxisViewItem& tavi)
2130 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(&tavi);
2136 MidiRegionView::Selection const & s = mrv->selection();
2142 EditNoteDialog* d = new EditNoteDialog (&(*s.begin())->region_view(), s);
2146 d->signal_response().connect (sigc::bind (sigc::mem_fun (*this, &Editor::note_edit_done), d));
2150 Editor::note_edit_done (int r, EditNoteDialog* d)
2157 Editor::visible_order_range (int* low, int* high) const
2159 *low = TimeAxisView::max_order ();
2162 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
2164 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
2166 if (!rtv->hidden()) {
2168 if (*high < rtv->order()) {
2169 *high = rtv->order ();
2172 if (*low > rtv->order()) {
2173 *low = rtv->order ();
2180 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
2182 /* Either add to or set the set the region selection, unless
2183 this is an alignment click (control used)
2186 if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
2187 TimeAxisView* tv = &rv.get_time_axis_view();
2188 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
2190 if (rtv && rtv->is_track()) {
2191 speed = rtv->track()->speed();
2194 framepos_t where = get_preferred_edit_position();
2198 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
2200 align_region (rv.region(), SyncPoint, (framepos_t) (where * speed));
2202 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
2204 align_region (rv.region(), End, (framepos_t) (where * speed));
2208 align_region (rv.region(), Start, (framepos_t) (where * speed));
2215 Editor::collect_new_region_view (RegionView* rv)
2217 latest_regionviews.push_back (rv);
2221 Editor::collect_and_select_new_region_view (RegionView* rv)
2224 latest_regionviews.push_back (rv);
2228 Editor::cancel_selection ()
2230 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2231 (*i)->hide_selection ();
2234 selection->clear ();
2235 clicked_selection = 0;
2239 Editor::cancel_time_selection ()
2241 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2242 (*i)->hide_selection ();
2244 selection->time.clear ();
2245 clicked_selection = 0;
2249 Editor::point_trim (GdkEvent* event, framepos_t new_bound)
2251 RegionView* rv = clicked_regionview;
2253 /* Choose action dependant on which button was pressed */
2254 switch (event->button.button) {
2256 begin_reversible_command (_("start point trim"));
2258 if (selection->selected (rv)) {
2259 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
2260 i != selection->regions.by_layer().end(); ++i)
2262 if (!(*i)->region()->locked()) {
2263 (*i)->region()->clear_changes ();
2264 (*i)->region()->trim_front (new_bound);
2265 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2270 if (!rv->region()->locked()) {
2271 rv->region()->clear_changes ();
2272 rv->region()->trim_front (new_bound);
2273 _session->add_command(new StatefulDiffCommand (rv->region()));
2277 commit_reversible_command();
2281 begin_reversible_command (_("End point trim"));
2283 if (selection->selected (rv)) {
2285 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
2287 if (!(*i)->region()->locked()) {
2288 (*i)->region()->clear_changes();
2289 (*i)->region()->trim_end (new_bound);
2290 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2296 if (!rv->region()->locked()) {
2297 rv->region()->clear_changes ();
2298 rv->region()->trim_end (new_bound);
2299 _session->add_command (new StatefulDiffCommand (rv->region()));
2303 commit_reversible_command();
2312 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* /*event*/)
2317 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2318 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2322 Location* location = find_location_from_marker (marker, is_start);
2323 location->set_hidden (true, this);
2328 Editor::reposition_zoom_rect (framepos_t start, framepos_t end)
2330 double x1 = sample_to_pixel (start);
2331 double x2 = sample_to_pixel (end);
2332 double y2 = _full_canvas_height - 1.0;
2334 zoom_rect->set (ArdourCanvas::Rect (x1, 1.0, x2, y2));
2339 Editor::mouse_rename_region (ArdourCanvas::Item* /*item*/, GdkEvent* /*event*/)
2341 using namespace Gtkmm2ext;
2343 ArdourPrompter prompter (false);
2345 prompter.set_prompt (_("Name for region:"));
2346 prompter.set_initial_text (clicked_regionview->region()->name());
2347 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
2348 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
2349 prompter.show_all ();
2350 switch (prompter.run ()) {
2351 case Gtk::RESPONSE_ACCEPT:
2353 prompter.get_result(str);
2355 clicked_regionview->region()->set_name (str);
2364 Editor::mouse_brush_insert_region (RegionView* rv, framepos_t pos)
2366 /* no brushing without a useful snap setting */
2368 switch (_snap_mode) {
2370 return; /* can't work because it allows region to be placed anywhere */
2375 switch (_snap_type) {
2383 /* don't brush a copy over the original */
2385 if (pos == rv->region()->position()) {
2389 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
2391 if (rtv == 0 || !rtv->is_track()) {
2395 boost::shared_ptr<Playlist> playlist = rtv->playlist();
2396 double speed = rtv->track()->speed();
2398 playlist->clear_changes ();
2399 boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region(), true));
2400 playlist->add_region (new_region, (framepos_t) (pos * speed));
2401 _session->add_command (new StatefulDiffCommand (playlist));
2403 // playlist is frozen, so we have to update manually XXX this is disgusting
2405 playlist->RegionAdded (new_region); /* EMIT SIGNAL */
2409 Editor::track_height_step_timeout ()
2411 if (get_microseconds() - last_track_height_step_timestamp < 250000) {
2412 current_stepping_trackview = 0;
2419 Editor::add_region_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2421 assert (region_view);
2423 if (!region_view->region()->playlist()) {
2427 switch (Config->get_edit_mode()) {
2429 _drags->add (new RegionSpliceDrag (this, item, region_view, selection->regions.by_layer()));
2432 _drags->add (new RegionRippleDrag (this, item, region_view, selection->regions.by_layer()));
2435 _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), false, false));
2442 Editor::add_region_copy_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2444 assert (region_view);
2446 if (!region_view->region()->playlist()) {
2450 _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), false, true));
2454 Editor::add_region_brush_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2456 assert (region_view);
2458 if (!region_view->region()->playlist()) {
2462 if (Config->get_edit_mode() == Splice || Config->get_edit_mode() == Ripple) {
2466 _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), true, false));
2468 begin_reversible_command (Operations::drag_region_brush);
2471 /** Start a grab where a time range is selected, track(s) are selected, and the
2472 * user clicks and drags a region with a modifier in order to create a new region containing
2473 * the section of the clicked region that lies within the time range.
2476 Editor::start_selection_grab (ArdourCanvas::Item* /*item*/, GdkEvent* event)
2478 if (clicked_regionview == 0) {
2482 /* lets try to create new Region for the selection */
2484 vector<boost::shared_ptr<Region> > new_regions;
2485 create_region_from_selection (new_regions);
2487 if (new_regions.empty()) {
2491 /* XXX fix me one day to use all new regions */
2493 boost::shared_ptr<Region> region (new_regions.front());
2495 /* add it to the current stream/playlist.
2497 tricky: the streamview for the track will add a new regionview. we will
2498 catch the signal it sends when it creates the regionview to
2499 set the regionview we want to then drag.
2502 latest_regionviews.clear();
2503 sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
2505 /* A selection grab currently creates two undo/redo operations, one for
2506 creating the new region and another for moving it.
2509 begin_reversible_command (Operations::selection_grab);
2511 boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
2513 playlist->clear_changes ();
2514 clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
2515 _session->add_command(new StatefulDiffCommand (playlist));
2517 commit_reversible_command ();
2521 if (latest_regionviews.empty()) {
2522 /* something went wrong */
2526 /* we need to deselect all other regionviews, and select this one
2527 i'm ignoring undo stuff, because the region creation will take care of it
2529 selection->set (latest_regionviews);
2531 _drags->set (new RegionMoveDrag (this, latest_regionviews.front()->get_canvas_group(), latest_regionviews.front(), latest_regionviews, false, false), event);
2537 if (_drags->active ()) {
2540 selection->clear ();
2545 Editor::set_internal_edit (bool yn)
2547 if (_internal_editing == yn) {
2551 _internal_editing = yn;
2554 pre_internal_mouse_mode = mouse_mode;
2555 pre_internal_snap_type = _snap_type;
2556 pre_internal_snap_mode = _snap_mode;
2558 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2559 (*i)->enter_internal_edit_mode ();
2562 set_snap_to (internal_snap_type);
2563 set_snap_mode (internal_snap_mode);
2567 internal_snap_mode = _snap_mode;
2568 internal_snap_type = _snap_type;
2570 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2571 (*i)->leave_internal_edit_mode ();
2574 if (mouse_mode == MouseDraw && pre_internal_mouse_mode != MouseDraw) {
2575 /* we were drawing .. flip back to something sensible */
2576 set_mouse_mode (pre_internal_mouse_mode);
2579 set_snap_to (pre_internal_snap_type);
2580 set_snap_mode (pre_internal_snap_mode);
2583 reset_canvas_cursor ();
2586 /** Update _join_object_range_state which indicate whether we are over the top
2587 * or bottom half of a route view, used by the `join object/range' tool
2588 * mode. Coordinates in canvas space.
2591 Editor::update_join_object_range_location (double y)
2593 if (_internal_editing || !get_smart_mode()) {
2594 _join_object_range_state = JOIN_OBJECT_RANGE_NONE;
2598 JoinObjectRangeState const old = _join_object_range_state;
2600 if (mouse_mode == MouseObject) {
2601 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2602 } else if (mouse_mode == MouseRange) {
2603 _join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
2606 if (entered_regionview) {
2608 ArdourCanvas::Duple const item_space = entered_regionview->get_canvas_group()->canvas_to_item (ArdourCanvas::Duple (0, y));
2609 double const c = item_space.y / entered_regionview->height();
2611 _join_object_range_state = c <= 0.5 ? JOIN_OBJECT_RANGE_RANGE : JOIN_OBJECT_RANGE_OBJECT;
2613 if (_join_object_range_state != old) {
2614 set_canvas_cursor (which_track_cursor ());
2617 } else if (entered_track) {
2619 RouteTimeAxisView* entered_route_view = dynamic_cast<RouteTimeAxisView*> (entered_track);
2621 if (entered_route_view) {
2626 entered_route_view->canvas_display()->canvas_to_item (cx, cy);
2628 double track_height = entered_route_view->view()->child_height();
2629 if (Config->get_show_name_highlight()) {
2630 track_height -= TimeAxisViewItem::NAME_HIGHLIGHT_SIZE;
2632 double const c = cy / track_height;
2636 _join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
2638 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2642 /* Other kinds of tracks use object mode */
2643 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2646 if (_join_object_range_state != old) {
2647 set_canvas_cursor (which_track_cursor ());
2653 Editor::effective_mouse_mode () const
2655 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
2657 } else if (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE) {
2665 Editor::remove_midi_note (ArdourCanvas::Item* item, GdkEvent *)
2667 NoteBase* e = reinterpret_cast<NoteBase*> (item->get_data ("notebase"));
2670 e->region_view().delete_note (e->note ());
2673 /** Obtain the pointer position in canvas coordinates */
2675 Editor::get_pointer_position (double& x, double& y) const
2678 _track_canvas->get_pointer (px, py);
2679 _track_canvas->window_to_canvas (px, py, x, y);