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 == MouseCut) m = MouseObject;
239 Glib::RefPtr<Action> act;
243 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-range"));
247 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-cut"));
251 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-object"));
255 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-draw"));
259 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-gain"));
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;
289 if (ARDOUR::Profile->get_mixbus()) {
290 if ( m == MouseCut) m = MouseObject;
295 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-range"));
299 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-object"));
303 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-cut"));
307 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-draw"));
311 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-gain"));
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 set_mouse_mode (MouseRange);
399 set_mouse_mode (MouseTimeFX);
404 if (next) set_mouse_mode (MouseDraw);
405 else set_mouse_mode (MouseCut);
409 if (next) set_mouse_mode (MouseRange);
410 else set_mouse_mode (MouseDraw);
414 if (next) set_mouse_mode (MouseCut);
415 else set_mouse_mode (MouseRange);
419 if (next) set_mouse_mode (MouseTimeFX);
420 else set_mouse_mode (MouseDraw);
425 set_mouse_mode (MouseAudition);
427 set_mouse_mode (MouseGain);
432 if (next) set_mouse_mode (MouseObject);
433 else set_mouse_mode (MouseTimeFX);
439 Editor::toggle_internal_editing_from_double_click (GdkEvent* event)
441 if (_drags->active()) {
442 _drags->end_grab (event);
445 ActionManager::do_action ("MouseMode", "toggle-internal-edit");
447 /* prevent reversion of edit cursor on button release */
449 pre_press_cursor = 0;
455 Editor::button_selection (ArdourCanvas::Item* /*item*/, GdkEvent* event, ItemType item_type)
457 /* in object/audition/timefx/gain-automation mode,
458 any button press sets the selection if the object
459 can be selected. this is a bit of hack, because
460 we want to avoid this if the mouse operation is a
463 note: not dbl-click or triple-click
465 Also note that there is no region selection in internal edit mode, otherwise
466 for operations operating on the selection (e.g. cut) it is not obvious whether
467 to cut notes or regions.
470 MouseMode eff_mouse_mode = effective_mouse_mode ();
472 if (eff_mouse_mode == MouseCut) {
473 /* never change selection in cut mode */
477 if (get_smart_mode() && eff_mouse_mode == MouseRange && event->button.button == 3 && item_type == RegionItem) {
478 /* context clicks are always about object properties, even if
479 we're in range mode within smart mode.
481 eff_mouse_mode = MouseObject;
484 /* special case: allow drag of region fade in/out in object mode with join object/range enabled */
485 if (get_smart_mode()) {
487 case FadeInHandleItem:
488 case FadeInTrimHandleItem:
489 case FadeOutHandleItem:
490 case FadeOutTrimHandleItem:
491 eff_mouse_mode = MouseObject;
498 if (((mouse_mode != MouseObject) &&
499 (mouse_mode != MouseAudition || item_type != RegionItem) &&
500 (mouse_mode != MouseTimeFX || item_type != RegionItem) &&
501 (mouse_mode != MouseGain) &&
502 (mouse_mode != MouseDraw)) ||
503 ((event->type != GDK_BUTTON_PRESS && event->type != GDK_BUTTON_RELEASE) || event->button.button > 3) ||
504 (internal_editing() && mouse_mode != MouseTimeFX)) {
509 if (event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) {
511 if ((event->button.state & Keyboard::RelevantModifierKeyMask) && event->button.button != 1) {
513 /* almost no selection action on modified button-2 or button-3 events */
515 if (item_type != RegionItem && event->button.button != 2) {
521 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
522 bool press = (event->type == GDK_BUTTON_PRESS);
527 if (eff_mouse_mode != MouseRange) {
528 set_selected_regionview_from_click (press, op);
530 /* don't change the selection unless the
531 clicked track is not currently selected. if
532 so, "collapse" the selection to just this
535 if (!selection->selected (clicked_axisview)) {
536 set_selected_track_as_side_effect (Selection::Set);
540 if (eff_mouse_mode != MouseRange) {
541 set_selected_regionview_from_click (press, op);
546 case RegionViewNameHighlight:
548 case LeftFrameHandle:
549 case RightFrameHandle:
550 case FadeInHandleItem:
551 case FadeInTrimHandleItem:
553 case FadeOutHandleItem:
554 case FadeOutTrimHandleItem:
556 case StartCrossFadeItem:
557 case EndCrossFadeItem:
558 if (get_smart_mode() || eff_mouse_mode != MouseRange) {
559 set_selected_regionview_from_click (press, op);
560 } else if (event->type == GDK_BUTTON_PRESS) {
561 set_selected_track_as_side_effect (op);
565 case ControlPointItem:
566 set_selected_track_as_side_effect (op);
567 if (eff_mouse_mode != MouseRange) {
568 set_selected_control_point_from_click (press, op);
573 /* for context click, select track */
574 if (event->button.button == 3) {
575 selection->clear_tracks ();
576 set_selected_track_as_side_effect (op);
580 case AutomationTrackItem:
581 set_selected_track_as_side_effect (op);
590 Editor::button_press_handler_1 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
592 /* single mouse clicks on any of these item types operate
593 independent of mouse mode, mostly because they are
594 not on the main track canvas or because we want
599 case PlayheadCursorItem:
600 _drags->set (new CursorDrag (this, *playhead_cursor, true), event);
604 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
605 hide_marker (item, event);
607 _drags->set (new MarkerDrag (this, item), event);
611 case TempoMarkerItem:
613 TempoMarker* m = reinterpret_cast<TempoMarker*> (item->get_data ("marker"));
616 new TempoMarkerDrag (
619 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
626 case MeterMarkerItem:
628 MeterMarker* m = reinterpret_cast<MeterMarker*> (item->get_data ("marker"));
631 new MeterMarkerDrag (
634 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
642 _drags->set (new VideoTimeLineDrag (this, item), event);
649 case TimecodeRulerItem:
650 case SamplesRulerItem:
651 case MinsecRulerItem:
653 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
654 _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
660 case RangeMarkerBarItem:
661 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier)) {
662 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateSkipMarker), event);
663 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
664 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateRangeMarker), event);
666 _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
671 case CdMarkerBarItem:
672 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
673 _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
675 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateCDMarker), event);
680 case TransportMarkerBarItem:
681 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
682 _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
684 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateTransportMarker), event);
693 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
694 /* special case: allow trim of range selections in joined object mode;
695 in theory eff should equal MouseRange in this case, but it doesn't
696 because entering the range selection canvas item results in entered_regionview
697 being set to 0, so update_join_object_range_location acts as if we aren't
700 if (item_type == StartSelectionTrimItem) {
701 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
702 } else if (item_type == EndSelectionTrimItem) {
703 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
707 Editing::MouseMode eff = effective_mouse_mode ();
709 /* special case: allow drag of region fade in/out in object mode with join object/range enabled */
710 if (get_smart_mode()) {
712 case FadeInHandleItem:
713 case FadeInTrimHandleItem:
714 case FadeOutHandleItem:
715 case FadeOutTrimHandleItem:
723 /* there is no Range mode when in internal edit mode */
724 if (eff == MouseRange && internal_editing()) {
731 case StartSelectionTrimItem:
732 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
735 case EndSelectionTrimItem:
736 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
740 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
741 start_selection_grab (item, event);
743 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
744 /* grab selection for moving */
745 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionMove), event);
747 /* this was debated, but decided the more common action was to
748 make a new selection */
749 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
754 if (internal_editing()) {
755 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
756 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
760 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::RangeSelectModifier)) {
761 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionExtend), event);
763 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
769 case RegionViewNameHighlight:
770 if (!clicked_regionview->region()->locked()) {
771 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
777 if (!internal_editing()) {
778 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::RangeSelectModifier)) {
779 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionExtend), event);
781 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
791 /* Existing note: allow trimming/motion */
792 if (internal_editing()) {
793 /* trim notes if we're in internal edit mode and near the ends of the note */
794 NoteBase* cn = reinterpret_cast<NoteBase*>(item->get_data ("notebase"));
796 if (cn->big_enough_to_trim() && cn->mouse_near_ends()) {
797 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
799 _drags->set (new NoteDrag (this, item), event);
805 if (internal_editing()) {
806 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
807 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
821 case FadeInHandleItem:
822 case FadeOutHandleItem:
823 case LeftFrameHandle:
824 case RightFrameHandle:
825 case FeatureLineItem:
826 case RegionViewNameHighlight:
829 case AutomationTrackItem:
830 _drags->set (new RegionCutDrag (this, item, canvas_event_sample (event)), event, current_canvas_cursor);
841 /* Existing note: allow trimming/motion */
842 if (internal_editing()) {
843 NoteBase* cn = reinterpret_cast<NoteBase*> (item->get_data ("notebase"));
845 if (cn->big_enough_to_trim() && cn->mouse_near_ends()) {
846 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
848 _drags->set (new NoteDrag (this, item), event);
858 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)) &&
859 event->type == GDK_BUTTON_PRESS) {
861 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
863 } else if (event->type == GDK_BUTTON_PRESS) {
866 case FadeInHandleItem:
868 _drags->set (new FadeInDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), selection->regions), event, _cursors->fade_in);
872 case FadeOutHandleItem:
874 _drags->set (new FadeOutDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), selection->regions), event, _cursors->fade_out);
878 case StartCrossFadeItem:
879 case EndCrossFadeItem:
880 /* we might allow user to grab inside the fade to trim a region with preserve_fade_anchor. for not this is not fully implemented */
881 // if (!clicked_regionview->region()->locked()) {
882 // _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer(), true), event);
887 case FeatureLineItem:
889 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier)) {
890 remove_transient(item);
894 _drags->set (new FeatureLineDrag (this, item), event);
900 if (dynamic_cast<AutomationRegionView*> (clicked_regionview)) {
901 /* click on an automation region view; do nothing here and let the ARV's signal handler
907 if (internal_editing ()) {
911 /* click on a normal region view */
912 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
913 add_region_copy_drag (item, event, clicked_regionview);
914 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
915 add_region_brush_drag (item, event, clicked_regionview);
917 add_region_drag (item, event, clicked_regionview);
921 // if (!internal_editing() && (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE && !selection->regions.empty())) {
922 // _drags->add (new SelectionDrag (this, clicked_axisview->get_selection_rect (clicked_selection)->rect, SelectionDrag::SelectionMove));
925 _drags->start_grab (event);
929 case RegionViewNameHighlight:
930 case LeftFrameHandle:
931 case RightFrameHandle:
932 if (!clicked_regionview->region()->locked()) {
933 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
938 case FadeInTrimHandleItem:
939 case FadeOutTrimHandleItem:
940 if (!clicked_regionview->region()->locked()) {
941 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer(), true), event);
948 /* rename happens on edit clicks */
949 if (clicked_regionview->get_name_highlight()) {
950 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer()), event);
956 case ControlPointItem:
957 _drags->set (new ControlPointDrag (this, item), event);
961 case AutomationLineItem:
962 _drags->set (new LineDrag (this, item), event);
967 if (internal_editing()) {
968 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
969 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
973 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
977 case AutomationTrackItem:
979 TimeAxisView* parent = clicked_axisview->get_parent ();
980 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (clicked_axisview);
982 if (parent && dynamic_cast<MidiTimeAxisView*> (parent) && atv->show_regions ()) {
984 RouteTimeAxisView* p = dynamic_cast<RouteTimeAxisView*> (parent);
986 boost::shared_ptr<Playlist> pl = p->track()->playlist ();
987 if (pl->n_regions() == 0) {
988 /* Parent has no regions; create one so that we have somewhere to put automation */
989 _drags->set (new RegionCreateDrag (this, item, parent), event);
991 /* See if there's a region before the click that we can extend, and extend it if so */
992 framepos_t const t = canvas_event_sample (event);
993 boost::shared_ptr<Region> prev = pl->find_next_region (t, End, -1);
995 _drags->set (new RegionCreateDrag (this, item, parent), event);
997 prev->set_length (t - prev->position ());
1001 /* rubberband drag to select automation points */
1002 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
1024 switch (item_type) {
1026 _drags->set (new LineDrag (this, item), event);
1029 case ControlPointItem:
1030 _drags->set (new ControlPointDrag (this, item), event);
1036 AudioRegionView* arv = dynamic_cast<AudioRegionView *> (clicked_regionview);
1038 _drags->set (new AutomationRangeDrag (this, arv, selection->time), event, _cursors->up_down);
1040 double const y = event->button.y;
1041 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
1043 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
1045 /* smart "join" mode: drag automation */
1046 _drags->set (new AutomationRangeDrag (this, atv, selection->time), event, _cursors->up_down);
1054 case AutomationLineItem:
1055 _drags->set (new LineDrag (this, item), event);
1065 if (internal_editing() && item_type == NoteItem ) {
1066 /* drag notes if we're in internal edit mode */
1067 NoteBase* cn = reinterpret_cast<NoteBase*>(item->get_data ("notebase"));
1069 if (cn->big_enough_to_trim()) {
1070 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
1073 } else if (clicked_regionview) {
1075 _drags->set (new TimeFXDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1081 _drags->set (new ScrubDrag (this, item), event);
1082 scrub_reversals = 0;
1083 scrub_reverse_distance = 0;
1084 last_scrub_x = event->button.x;
1085 scrubbing_direction = 0;
1086 push_canvas_cursor (_cursors->transparent);
1098 Editor::button_press_handler_2 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1100 Editing::MouseMode const eff = effective_mouse_mode ();
1103 switch (item_type) {
1105 if (internal_editing ()) {
1106 /* no region drags in internal edit mode */
1110 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
1111 add_region_copy_drag (item, event, clicked_regionview);
1113 add_region_drag (item, event, clicked_regionview);
1115 _drags->start_grab (event);
1118 case ControlPointItem:
1119 _drags->set (new ControlPointDrag (this, item), event);
1127 switch (item_type) {
1128 case RegionViewNameHighlight:
1129 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1133 case LeftFrameHandle:
1134 case RightFrameHandle:
1135 if (!internal_editing ()) {
1136 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1141 case RegionViewName:
1142 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer()), event);
1156 /* relax till release */
1168 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1170 if (event->type == GDK_2BUTTON_PRESS) {
1171 _drags->mark_double_click ();
1172 gdk_pointer_ungrab (GDK_CURRENT_TIME);
1176 if (event->type != GDK_BUTTON_PRESS) {
1180 pre_press_cursor = current_canvas_cursor;
1182 _track_canvas->grab_focus();
1184 if (_session && _session->actively_recording()) {
1188 if (internal_editing()) {
1189 bool leave_internal_edit_mode = false;
1191 switch (item_type) {
1196 if (!dynamic_cast<MidiRegionView*> (clicked_regionview) && !dynamic_cast<AutomationRegionView*> (clicked_regionview)) {
1197 leave_internal_edit_mode = true;
1201 case PlayheadCursorItem:
1203 case TempoMarkerItem:
1204 case MeterMarkerItem:
1208 case RangeMarkerBarItem:
1209 case CdMarkerBarItem:
1210 case TransportMarkerBarItem:
1212 case TimecodeRulerItem:
1213 case SamplesRulerItem:
1214 case MinsecRulerItem:
1216 /* button press on these items never does anything to
1217 change the editing mode.
1225 if (leave_internal_edit_mode) {
1226 ActionManager::do_action ("MouseMode", "toggle-internal-edit");
1230 button_selection (item, event, item_type);
1232 if (!_drags->active () &&
1233 (Keyboard::is_delete_event (&event->button) ||
1234 Keyboard::is_context_menu_event (&event->button) ||
1235 Keyboard::is_edit_event (&event->button))) {
1237 /* handled by button release */
1241 //not rolling, range mode click + join_play_range : locate the PH here
1242 if ( !_drags->active () && !_session->transport_rolling() && ( effective_mouse_mode() == MouseRange ) && Config->get_follow_edits() ) {
1243 framepos_t where = canvas_event_sample (event);
1245 _session->request_locate (where, false);
1248 switch (event->button.button) {
1250 return button_press_handler_1 (item, event, item_type);
1254 return button_press_handler_2 (item, event, item_type);
1261 return button_press_dispatch (&event->button);
1270 Editor::button_press_dispatch (GdkEventButton* ev)
1272 /* this function is intended only for buttons 4 and above.
1275 Gtkmm2ext::MouseButton b (ev->state, ev->button);
1276 return button_bindings->activate (b, Gtkmm2ext::Bindings::Press);
1280 Editor::button_release_dispatch (GdkEventButton* ev)
1282 /* this function is intended only for buttons 4 and above.
1285 Gtkmm2ext::MouseButton b (ev->state, ev->button);
1286 return button_bindings->activate (b, Gtkmm2ext::Bindings::Release);
1290 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1292 framepos_t where = canvas_event_sample (event);
1293 AutomationTimeAxisView* atv = 0;
1295 if (pre_press_cursor) {
1296 set_canvas_cursor (pre_press_cursor);
1297 pre_press_cursor = 0;
1300 /* no action if we're recording */
1302 if (_session && _session->actively_recording()) {
1306 /* see if we're finishing a drag */
1308 bool were_dragging = false;
1309 if (_drags->active ()) {
1310 bool const r = _drags->end_grab (event);
1312 /* grab dragged, so do nothing else */
1316 were_dragging = true;
1319 update_region_layering_order_editor ();
1321 /* edit events get handled here */
1323 if (!_drags->active () && Keyboard::is_edit_event (&event->button)) {
1324 switch (item_type) {
1326 show_region_properties ();
1329 case TempoMarkerItem: {
1331 TempoMarker* tempo_marker;
1333 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
1334 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
1335 abort(); /*NOTREACHED*/
1338 if ((tempo_marker = dynamic_cast<TempoMarker*> (marker)) == 0) {
1339 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
1340 abort(); /*NOTREACHED*/
1343 edit_tempo_marker (*tempo_marker);
1347 case MeterMarkerItem: {
1349 MeterMarker* meter_marker;
1351 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
1352 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
1353 abort(); /*NOTREACHED*/
1356 if ((meter_marker = dynamic_cast<MeterMarker*> (marker)) == 0) {
1357 fatal << _("programming error: marker for meter is not a meter marker!") << endmsg;
1358 abort(); /*NOTREACHED*/
1360 edit_meter_marker (*meter_marker);
1364 case RegionViewName:
1365 if (clicked_regionview->name_active()) {
1366 return mouse_rename_region (item, event);
1370 case ControlPointItem:
1371 edit_control_point (item);
1380 /* context menu events get handled here */
1381 if (Keyboard::is_context_menu_event (&event->button)) {
1383 context_click_event = *event;
1385 if (!_drags->active ()) {
1387 /* no matter which button pops up the context menu, tell the menu
1388 widget to use button 1 to drive menu selection.
1391 switch (item_type) {
1393 case FadeInHandleItem:
1394 case FadeInTrimHandleItem:
1395 case StartCrossFadeItem:
1396 popup_xfade_in_context_menu (1, event->button.time, item, item_type);
1400 case FadeOutHandleItem:
1401 case FadeOutTrimHandleItem:
1402 case EndCrossFadeItem:
1403 popup_xfade_out_context_menu (1, event->button.time, item, item_type);
1406 case LeftFrameHandle:
1407 case RightFrameHandle:
1411 popup_track_context_menu (1, event->button.time, item_type, false);
1415 case RegionViewNameHighlight:
1416 case RegionViewName:
1417 popup_track_context_menu (1, event->button.time, item_type, false);
1421 popup_track_context_menu (1, event->button.time, item_type, true);
1424 case AutomationTrackItem:
1425 popup_track_context_menu (1, event->button.time, item_type, false);
1429 case RangeMarkerBarItem:
1430 case TransportMarkerBarItem:
1431 case CdMarkerBarItem:
1435 case TimecodeRulerItem:
1436 case SamplesRulerItem:
1437 case MinsecRulerItem:
1439 popup_ruler_menu (where, item_type);
1443 marker_context_menu (&event->button, item);
1446 case TempoMarkerItem:
1447 tempo_or_meter_marker_context_menu (&event->button, item);
1450 case MeterMarkerItem:
1451 tempo_or_meter_marker_context_menu (&event->button, item);
1454 case CrossfadeViewItem:
1455 popup_track_context_menu (1, event->button.time, item_type, false);
1458 case ControlPointItem:
1459 popup_control_point_context_menu (item, event);
1470 /* delete events get handled here */
1472 Editing::MouseMode const eff = effective_mouse_mode ();
1474 if (!_drags->active () && Keyboard::is_delete_event (&event->button)) {
1476 switch (item_type) {
1477 case TempoMarkerItem:
1478 remove_tempo_marker (item);
1481 case MeterMarkerItem:
1482 remove_meter_marker (item);
1486 remove_marker (*item, event);
1490 if (eff == MouseObject) {
1491 remove_clicked_region ();
1495 case ControlPointItem:
1496 remove_control_point (item);
1500 remove_midi_note (item, event);
1509 switch (event->button.button) {
1512 switch (item_type) {
1513 /* see comments in button_press_handler */
1514 case PlayheadCursorItem:
1517 case AutomationLineItem:
1518 case StartSelectionTrimItem:
1519 case EndSelectionTrimItem:
1523 if (!_dragging_playhead) {
1524 snap_to_with_modifier (where, event, RoundNearest, true);
1525 mouse_add_new_marker (where);
1529 case CdMarkerBarItem:
1530 if (!_dragging_playhead) {
1531 // if we get here then a dragged range wasn't done
1532 snap_to_with_modifier (where, event, RoundNearest, true);
1533 mouse_add_new_marker (where, true);
1538 if (!_dragging_playhead) {
1539 snap_to_with_modifier (where, event);
1540 mouse_add_new_tempo_event (where);
1545 if (!_dragging_playhead) {
1546 mouse_add_new_meter_event (pixel_to_sample (event->button.x));
1551 case TimecodeRulerItem:
1552 case SamplesRulerItem:
1553 case MinsecRulerItem:
1565 switch (item_type) {
1566 case AutomationTrackItem:
1567 atv = dynamic_cast<AutomationTimeAxisView*>(clicked_axisview);
1569 bool with_guard_points = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
1570 atv->add_automation_event (event, where, event->button.y, with_guard_points);
1580 switch (item_type) {
1583 /* check that we didn't drag before releasing, since
1584 its really annoying to create new control
1585 points when doing this.
1587 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (clicked_regionview);
1588 if (!were_dragging && arv) {
1589 bool with_guard_points = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
1590 arv->add_gain_point_event (item, event, with_guard_points);
1596 case AutomationTrackItem: {
1597 bool with_guard_points = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
1598 dynamic_cast<AutomationTimeAxisView*>(clicked_axisview)->
1599 add_automation_event (event, where, event->button.y, with_guard_points);
1609 pop_canvas_cursor ();
1610 if (scrubbing_direction == 0) {
1611 /* no drag, just a click */
1612 switch (item_type) {
1614 play_selected_region ();
1620 /* make sure we stop */
1621 _session->request_transport_speed (0.0);
1630 /* do any (de)selection operations that should occur on button release */
1631 button_selection (item, event, item_type);
1640 switch (item_type) {
1642 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1644 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier|Keyboard::SecondaryModifier))) {
1647 // Button2 click is unused
1662 // x_style_paste (where, 1.0);
1683 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1690 /* by the time we reach here, entered_regionview and entered trackview
1691 * will have already been set as appropriate. Things are done this
1692 * way because this method isn't passed a pointer to a variable type of
1693 * thing that is entered (which may or may not be canvas item).
1694 * (e.g. the actual entered regionview)
1697 choose_canvas_cursor_on_entry (&event->crossing, item_type);
1699 switch (item_type) {
1700 case ControlPointItem:
1701 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1702 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1705 fraction = 1.0 - (cp->get_y() / cp->line().height());
1707 _verbose_cursor->set (cp->line().get_verbose_cursor_string (fraction));
1708 _verbose_cursor->show ();
1713 if (mouse_mode == MouseGain) {
1714 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1716 line->set_outline_color (ARDOUR_UI::config()->get_EnteredGainLine());
1721 case AutomationLineItem:
1722 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1723 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1725 line->set_outline_color (ARDOUR_UI::config()->get_EnteredAutomationLine());
1730 case AutomationTrackItem:
1731 AutomationTimeAxisView* atv;
1732 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1733 clear_entered_track = false;
1734 set_entered_track (atv);
1739 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1742 entered_marker = marker;
1743 marker->set_color_rgba (ARDOUR_UI::config()->get_EnteredMarker());
1745 case MeterMarkerItem:
1746 case TempoMarkerItem:
1749 case FadeInHandleItem:
1750 case FadeInTrimHandleItem:
1751 if (mouse_mode == MouseObject && !internal_editing()) {
1752 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
1754 RegionView* rv = static_cast<RegionView*>(item->get_data ("regionview"));
1755 rect->set_fill_color (rv->get_fill_color());
1760 case FadeOutHandleItem:
1761 case FadeOutTrimHandleItem:
1762 if (mouse_mode == MouseObject && !internal_editing()) {
1763 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
1765 RegionView* rv = static_cast<RegionView*>(item->get_data ("regionview"));
1766 rect->set_fill_color (rv->get_fill_color ());
1771 case FeatureLineItem:
1773 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1774 line->set_outline_color (0xFF0000FF);
1785 /* third pass to handle entered track status in a comprehensible way.
1788 switch (item_type) {
1790 case AutomationLineItem:
1791 case ControlPointItem:
1792 /* these do not affect the current entered track state */
1793 clear_entered_track = false;
1796 case AutomationTrackItem:
1797 /* handled above already */
1809 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent*, ItemType item_type)
1817 reset_canvas_cursor ();
1819 switch (item_type) {
1820 case ControlPointItem:
1821 _verbose_cursor->hide ();
1825 case AutomationLineItem:
1826 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1828 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1830 line->set_outline_color (al->get_line_color());
1836 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1840 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
1841 location_flags_changed (loc);
1844 case MeterMarkerItem:
1845 case TempoMarkerItem:
1848 case FadeInTrimHandleItem:
1849 case FadeOutTrimHandleItem:
1850 case FadeInHandleItem:
1851 case FadeOutHandleItem:
1853 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
1855 rect->set_fill_color (ARDOUR_UI::config()->get_InactiveFadeHandle());
1860 case AutomationTrackItem:
1863 case FeatureLineItem:
1865 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1866 line->set_outline_color (ARDOUR_UI::config()->get_ZeroLine());
1878 Editor::scrub (framepos_t frame, double current_x)
1882 if (scrubbing_direction == 0) {
1884 _session->request_locate (frame, false);
1885 _session->request_transport_speed (0.1);
1886 scrubbing_direction = 1;
1890 if (last_scrub_x > current_x) {
1892 /* pointer moved to the left */
1894 if (scrubbing_direction > 0) {
1896 /* we reversed direction to go backwards */
1899 scrub_reverse_distance += (int) (last_scrub_x - current_x);
1903 /* still moving to the left (backwards) */
1905 scrub_reversals = 0;
1906 scrub_reverse_distance = 0;
1908 delta = 0.01 * (last_scrub_x - current_x);
1909 _session->request_transport_speed_nonzero (_session->transport_speed() - delta);
1913 /* pointer moved to the right */
1915 if (scrubbing_direction < 0) {
1916 /* we reversed direction to go forward */
1919 scrub_reverse_distance += (int) (current_x - last_scrub_x);
1922 /* still moving to the right */
1924 scrub_reversals = 0;
1925 scrub_reverse_distance = 0;
1927 delta = 0.01 * (current_x - last_scrub_x);
1928 _session->request_transport_speed_nonzero (_session->transport_speed() + delta);
1932 /* if there have been more than 2 opposite motion moves detected, or one that moves
1933 back more than 10 pixels, reverse direction
1936 if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
1938 if (scrubbing_direction > 0) {
1939 /* was forwards, go backwards */
1940 _session->request_transport_speed (-0.1);
1941 scrubbing_direction = -1;
1943 /* was backwards, go forwards */
1944 _session->request_transport_speed (0.1);
1945 scrubbing_direction = 1;
1948 scrub_reverse_distance = 0;
1949 scrub_reversals = 0;
1953 last_scrub_x = current_x;
1957 Editor::motion_handler (ArdourCanvas::Item* /*item*/, GdkEvent* event, bool from_autoscroll)
1959 _last_motion_y = event->motion.y;
1961 if (event->motion.is_hint) {
1964 /* We call this so that MOTION_NOTIFY events continue to be
1965 delivered to the canvas. We need to do this because we set
1966 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
1967 the density of the events, at the expense of a round-trip
1968 to the server. Given that this will mostly occur on cases
1969 where DISPLAY = :0.0, and given the cost of what the motion
1970 event might do, its a good tradeoff.
1973 _track_canvas->get_pointer (x, y);
1976 if (current_stepping_trackview) {
1977 /* don't keep the persistent stepped trackview if the mouse moves */
1978 current_stepping_trackview = 0;
1979 step_timeout.disconnect ();
1982 if (_session && _session->actively_recording()) {
1983 /* Sorry. no dragging stuff around while we record */
1987 update_join_object_range_location (event->motion.y);
1989 if (_drags->active ()) {
1990 return _drags->motion_handler (event, from_autoscroll);
1997 Editor::can_remove_control_point (ArdourCanvas::Item* item)
1999 ControlPoint* control_point;
2001 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2002 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2003 abort(); /*NOTREACHED*/
2006 AutomationLine& line = control_point->line ();
2007 if (dynamic_cast<AudioRegionGainLine*> (&line)) {
2008 /* we shouldn't remove the first or last gain point in region gain lines */
2009 if (line.is_last_point(*control_point) || line.is_first_point(*control_point)) {
2018 Editor::remove_control_point (ArdourCanvas::Item* item)
2020 if (!can_remove_control_point (item)) {
2024 ControlPoint* control_point;
2026 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2027 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2028 abort(); /*NOTREACHED*/
2031 control_point->line().remove_point (*control_point);
2035 Editor::edit_control_point (ArdourCanvas::Item* item)
2037 ControlPoint* p = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"));
2040 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2041 abort(); /*NOTREACHED*/
2044 ControlPointDialog d (p);
2047 if (d.run () != RESPONSE_ACCEPT) {
2051 p->line().modify_point_y (*p, d.get_y_fraction ());
2055 Editor::edit_notes (TimeAxisViewItem& tavi)
2057 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(&tavi);
2063 MidiRegionView::Selection const & s = mrv->selection();
2069 EditNoteDialog* d = new EditNoteDialog (&(*s.begin())->region_view(), s);
2073 d->signal_response().connect (sigc::bind (sigc::mem_fun (*this, &Editor::note_edit_done), d));
2077 Editor::note_edit_done (int r, EditNoteDialog* d)
2084 Editor::visible_order_range (int* low, int* high) const
2086 *low = TimeAxisView::max_order ();
2089 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
2091 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
2093 if (!rtv->hidden()) {
2095 if (*high < rtv->order()) {
2096 *high = rtv->order ();
2099 if (*low > rtv->order()) {
2100 *low = rtv->order ();
2107 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
2109 /* Either add to or set the set the region selection, unless
2110 this is an alignment click (control used)
2113 if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
2114 TimeAxisView* tv = &rv.get_time_axis_view();
2115 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
2117 if (rtv && rtv->is_track()) {
2118 speed = rtv->track()->speed();
2121 framepos_t where = get_preferred_edit_position();
2125 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
2127 align_region (rv.region(), SyncPoint, (framepos_t) (where * speed));
2129 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
2131 align_region (rv.region(), End, (framepos_t) (where * speed));
2135 align_region (rv.region(), Start, (framepos_t) (where * speed));
2142 Editor::collect_new_region_view (RegionView* rv)
2144 latest_regionviews.push_back (rv);
2148 Editor::collect_and_select_new_region_view (RegionView* rv)
2151 latest_regionviews.push_back (rv);
2155 Editor::cancel_selection ()
2157 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2158 (*i)->hide_selection ();
2161 selection->clear ();
2162 clicked_selection = 0;
2166 Editor::cancel_time_selection ()
2168 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2169 (*i)->hide_selection ();
2171 selection->time.clear ();
2172 clicked_selection = 0;
2176 Editor::point_trim (GdkEvent* event, framepos_t new_bound)
2178 RegionView* rv = clicked_regionview;
2180 /* Choose action dependant on which button was pressed */
2181 switch (event->button.button) {
2183 begin_reversible_command (_("start point trim"));
2185 if (selection->selected (rv)) {
2186 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
2187 i != selection->regions.by_layer().end(); ++i)
2189 if (!(*i)->region()->locked()) {
2190 (*i)->region()->clear_changes ();
2191 (*i)->region()->trim_front (new_bound);
2192 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2197 if (!rv->region()->locked()) {
2198 rv->region()->clear_changes ();
2199 rv->region()->trim_front (new_bound);
2200 _session->add_command(new StatefulDiffCommand (rv->region()));
2204 commit_reversible_command();
2208 begin_reversible_command (_("End point trim"));
2210 if (selection->selected (rv)) {
2212 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
2214 if (!(*i)->region()->locked()) {
2215 (*i)->region()->clear_changes();
2216 (*i)->region()->trim_end (new_bound);
2217 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2223 if (!rv->region()->locked()) {
2224 rv->region()->clear_changes ();
2225 rv->region()->trim_end (new_bound);
2226 _session->add_command (new StatefulDiffCommand (rv->region()));
2230 commit_reversible_command();
2239 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* /*event*/)
2244 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2245 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2246 abort(); /*NOTREACHED*/
2249 Location* location = find_location_from_marker (marker, is_start);
2250 location->set_hidden (true, this);
2254 Editor::mouse_rename_region (ArdourCanvas::Item* /*item*/, GdkEvent* /*event*/)
2256 using namespace Gtkmm2ext;
2258 ArdourPrompter prompter (false);
2260 prompter.set_prompt (_("Name for region:"));
2261 prompter.set_initial_text (clicked_regionview->region()->name());
2262 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
2263 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
2264 prompter.show_all ();
2265 switch (prompter.run ()) {
2266 case Gtk::RESPONSE_ACCEPT:
2268 prompter.get_result(str);
2270 clicked_regionview->region()->set_name (str);
2279 Editor::mouse_brush_insert_region (RegionView* rv, framepos_t pos)
2281 /* no brushing without a useful snap setting */
2283 switch (_snap_mode) {
2285 return; /* can't work because it allows region to be placed anywhere */
2290 switch (_snap_type) {
2298 /* don't brush a copy over the original */
2300 if (pos == rv->region()->position()) {
2304 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
2306 if (rtv == 0 || !rtv->is_track()) {
2310 boost::shared_ptr<Playlist> playlist = rtv->playlist();
2311 double speed = rtv->track()->speed();
2313 playlist->clear_changes ();
2314 boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region(), true));
2315 playlist->add_region (new_region, (framepos_t) (pos * speed));
2316 _session->add_command (new StatefulDiffCommand (playlist));
2318 // playlist is frozen, so we have to update manually XXX this is disgusting
2320 playlist->RegionAdded (new_region); /* EMIT SIGNAL */
2324 Editor::track_height_step_timeout ()
2326 if (get_microseconds() - last_track_height_step_timestamp < 250000) {
2327 current_stepping_trackview = 0;
2334 Editor::add_region_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2336 assert (region_view);
2338 if (!region_view->region()->playlist()) {
2342 switch (Config->get_edit_mode()) {
2344 _drags->add (new RegionSpliceDrag (this, item, region_view, selection->regions.by_layer()));
2347 _drags->add (new RegionRippleDrag (this, item, region_view, selection->regions.by_layer()));
2350 _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), false, false));
2357 Editor::add_region_copy_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2359 assert (region_view);
2361 if (!region_view->region()->playlist()) {
2365 _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), false, true));
2369 Editor::add_region_brush_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2371 assert (region_view);
2373 if (!region_view->region()->playlist()) {
2377 if (Config->get_edit_mode() == Splice || Config->get_edit_mode() == Ripple) {
2381 _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), true, false));
2383 begin_reversible_command (Operations::drag_region_brush);
2386 /** Start a grab where a time range is selected, track(s) are selected, and the
2387 * user clicks and drags a region with a modifier in order to create a new region containing
2388 * the section of the clicked region that lies within the time range.
2391 Editor::start_selection_grab (ArdourCanvas::Item* /*item*/, GdkEvent* event)
2393 if (clicked_regionview == 0) {
2397 /* lets try to create new Region for the selection */
2399 vector<boost::shared_ptr<Region> > new_regions;
2400 create_region_from_selection (new_regions);
2402 if (new_regions.empty()) {
2406 /* XXX fix me one day to use all new regions */
2408 boost::shared_ptr<Region> region (new_regions.front());
2410 /* add it to the current stream/playlist.
2412 tricky: the streamview for the track will add a new regionview. we will
2413 catch the signal it sends when it creates the regionview to
2414 set the regionview we want to then drag.
2417 latest_regionviews.clear();
2418 sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
2420 /* A selection grab currently creates two undo/redo operations, one for
2421 creating the new region and another for moving it.
2424 begin_reversible_command (Operations::selection_grab);
2426 boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
2428 playlist->clear_changes ();
2429 clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
2430 _session->add_command(new StatefulDiffCommand (playlist));
2432 commit_reversible_command ();
2436 if (latest_regionviews.empty()) {
2437 /* something went wrong */
2441 /* we need to deselect all other regionviews, and select this one
2442 i'm ignoring undo stuff, because the region creation will take care of it
2444 selection->set (latest_regionviews);
2446 _drags->set (new RegionMoveDrag (this, latest_regionviews.front()->get_canvas_group(), latest_regionviews.front(), latest_regionviews, false, false), event);
2452 if (_drags->active ()) {
2455 selection->clear ();
2462 Editor::set_internal_edit (bool yn)
2464 if (_internal_editing == yn) {
2468 _internal_editing = yn;
2471 pre_internal_mouse_mode = mouse_mode;
2472 pre_internal_snap_type = _snap_type;
2473 pre_internal_snap_mode = _snap_mode;
2475 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2476 (*i)->enter_internal_edit_mode ();
2479 set_snap_to (internal_snap_type);
2480 set_snap_mode (internal_snap_mode);
2484 internal_snap_mode = _snap_mode;
2485 internal_snap_type = _snap_type;
2487 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2488 (*i)->leave_internal_edit_mode ();
2491 if (mouse_mode == MouseDraw && pre_internal_mouse_mode != MouseDraw) {
2492 /* we were drawing .. flip back to something sensible */
2493 set_mouse_mode (pre_internal_mouse_mode);
2496 set_snap_to (pre_internal_snap_type);
2497 set_snap_mode (pre_internal_snap_mode);
2500 reset_canvas_cursor ();
2501 MouseModeChanged ();
2504 /** Update _join_object_range_state which indicate whether we are over the top
2505 * or bottom half of a route view, used by the `join object/range' tool
2506 * mode. Coordinates in canvas space.
2509 Editor::update_join_object_range_location (double y)
2511 if (_internal_editing || !get_smart_mode()) {
2512 _join_object_range_state = JOIN_OBJECT_RANGE_NONE;
2516 JoinObjectRangeState const old = _join_object_range_state;
2518 if (mouse_mode == MouseObject) {
2519 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2520 } else if (mouse_mode == MouseRange) {
2521 _join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
2524 if (entered_regionview) {
2526 ArdourCanvas::Duple const item_space = entered_regionview->get_canvas_group()->canvas_to_item (ArdourCanvas::Duple (0, y));
2527 double const c = item_space.y / entered_regionview->height();
2529 _join_object_range_state = c <= 0.5 ? JOIN_OBJECT_RANGE_RANGE : JOIN_OBJECT_RANGE_OBJECT;
2531 if (_join_object_range_state != old) {
2532 set_canvas_cursor (which_track_cursor ());
2535 } else if (entered_track) {
2537 RouteTimeAxisView* entered_route_view = dynamic_cast<RouteTimeAxisView*> (entered_track);
2539 if (entered_route_view) {
2544 entered_route_view->canvas_display()->canvas_to_item (cx, cy);
2546 double track_height = entered_route_view->view()->child_height();
2547 if (Config->get_show_name_highlight()) {
2548 track_height -= TimeAxisViewItem::NAME_HIGHLIGHT_SIZE;
2550 double const c = cy / track_height;
2554 _join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
2556 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2560 /* Other kinds of tracks use object mode */
2561 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2564 if (_join_object_range_state != old) {
2565 set_canvas_cursor (which_track_cursor ());
2571 Editor::effective_mouse_mode () const
2573 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
2575 } else if (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE) {
2583 Editor::remove_midi_note (ArdourCanvas::Item* item, GdkEvent *)
2585 NoteBase* e = reinterpret_cast<NoteBase*> (item->get_data ("notebase"));
2588 e->region_view().delete_note (e->note ());
2591 /** Obtain the pointer position in canvas coordinates */
2593 Editor::get_pointer_position (double& x, double& y) const
2596 _track_canvas->get_pointer (px, py);
2597 _track_canvas->window_to_canvas (px, py, x, y);