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;
1338 if ((tempo_marker = dynamic_cast<TempoMarker*> (marker)) == 0) {
1339 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
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;
1356 if ((meter_marker = dynamic_cast<MeterMarker*> (marker)) == 0) {
1357 fatal << _("programming error: marker for meter is not a meter marker!") << endmsg;
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 case LeftFrameHandle:
1397 popup_xfade_in_context_menu (1, event->button.time, item, item_type);
1401 case FadeOutHandleItem:
1402 case FadeOutTrimHandleItem:
1403 case EndCrossFadeItem:
1404 case RightFrameHandle:
1405 popup_xfade_out_context_menu (1, event->button.time, item, item_type);
1409 popup_track_context_menu (1, event->button.time, item_type, false);
1413 case RegionViewNameHighlight:
1414 case RegionViewName:
1415 popup_track_context_menu (1, event->button.time, item_type, false);
1419 popup_track_context_menu (1, event->button.time, item_type, true);
1422 case AutomationTrackItem:
1423 popup_track_context_menu (1, event->button.time, item_type, false);
1427 case RangeMarkerBarItem:
1428 case TransportMarkerBarItem:
1429 case CdMarkerBarItem:
1433 case TimecodeRulerItem:
1434 case SamplesRulerItem:
1435 case MinsecRulerItem:
1437 popup_ruler_menu (where, item_type);
1441 marker_context_menu (&event->button, item);
1444 case TempoMarkerItem:
1445 tempo_or_meter_marker_context_menu (&event->button, item);
1448 case MeterMarkerItem:
1449 tempo_or_meter_marker_context_menu (&event->button, item);
1452 case CrossfadeViewItem:
1453 popup_track_context_menu (1, event->button.time, item_type, false);
1456 case ControlPointItem:
1457 popup_control_point_context_menu (item, event);
1468 /* delete events get handled here */
1470 Editing::MouseMode const eff = effective_mouse_mode ();
1472 if (!_drags->active () && Keyboard::is_delete_event (&event->button)) {
1474 switch (item_type) {
1475 case TempoMarkerItem:
1476 remove_tempo_marker (item);
1479 case MeterMarkerItem:
1480 remove_meter_marker (item);
1484 remove_marker (*item, event);
1488 if (eff == MouseObject) {
1489 remove_clicked_region ();
1493 case ControlPointItem:
1494 remove_control_point (item);
1498 remove_midi_note (item, event);
1507 switch (event->button.button) {
1510 switch (item_type) {
1511 /* see comments in button_press_handler */
1512 case PlayheadCursorItem:
1515 case AutomationLineItem:
1516 case StartSelectionTrimItem:
1517 case EndSelectionTrimItem:
1521 if (!_dragging_playhead) {
1522 snap_to_with_modifier (where, event, 0, true);
1523 mouse_add_new_marker (where);
1527 case CdMarkerBarItem:
1528 if (!_dragging_playhead) {
1529 // if we get here then a dragged range wasn't done
1530 snap_to_with_modifier (where, event, 0, true);
1531 mouse_add_new_marker (where, true);
1536 if (!_dragging_playhead) {
1537 snap_to_with_modifier (where, event);
1538 mouse_add_new_tempo_event (where);
1543 if (!_dragging_playhead) {
1544 mouse_add_new_meter_event (pixel_to_sample (event->button.x));
1549 case TimecodeRulerItem:
1550 case SamplesRulerItem:
1551 case MinsecRulerItem:
1562 switch (item_type) {
1563 case AutomationTrackItem:
1564 atv = dynamic_cast<AutomationTimeAxisView*>(clicked_axisview);
1566 bool with_guard_points = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
1567 atv->add_automation_event (event, where, event->button.y, with_guard_points);
1577 switch (item_type) {
1580 /* check that we didn't drag before releasing, since
1581 its really annoying to create new control
1582 points when doing this.
1584 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (clicked_regionview);
1585 if (!were_dragging && arv) {
1586 bool with_guard_points = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
1587 arv->add_gain_point_event (item, event, with_guard_points);
1593 case AutomationTrackItem: {
1594 bool with_guard_points = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
1595 dynamic_cast<AutomationTimeAxisView*>(clicked_axisview)->
1596 add_automation_event (event, where, event->button.y, with_guard_points);
1606 pop_canvas_cursor ();
1607 if (scrubbing_direction == 0) {
1608 /* no drag, just a click */
1609 switch (item_type) {
1611 play_selected_region ();
1617 /* make sure we stop */
1618 _session->request_transport_speed (0.0);
1627 /* do any (de)selection operations that should occur on button release */
1628 button_selection (item, event, item_type);
1637 switch (item_type) {
1639 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1641 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier|Keyboard::SecondaryModifier))) {
1644 // Button2 click is unused
1659 // x_style_paste (where, 1.0);
1680 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1687 /* by the time we reach here, entered_regionview and entered trackview
1688 * will have already been set as appropriate. Things are done this
1689 * way because this method isn't passed a pointer to a variable type of
1690 * thing that is entered (which may or may not be canvas item).
1691 * (e.g. the actual entered regionview)
1694 choose_canvas_cursor_on_entry (&event->crossing, item_type);
1696 switch (item_type) {
1697 case ControlPointItem:
1698 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1699 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1702 fraction = 1.0 - (cp->get_y() / cp->line().height());
1704 _verbose_cursor->set (cp->line().get_verbose_cursor_string (fraction));
1705 _verbose_cursor->show ();
1710 if (mouse_mode == MouseGain) {
1711 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1713 line->set_outline_color (ARDOUR_UI::config()->get_EnteredGainLine());
1718 case AutomationLineItem:
1719 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1720 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1722 line->set_outline_color (ARDOUR_UI::config()->get_EnteredAutomationLine());
1727 case AutomationTrackItem:
1728 AutomationTimeAxisView* atv;
1729 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1730 clear_entered_track = false;
1731 set_entered_track (atv);
1736 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1739 entered_marker = marker;
1740 marker->set_color_rgba (ARDOUR_UI::config()->get_EnteredMarker());
1742 case MeterMarkerItem:
1743 case TempoMarkerItem:
1746 case FadeInHandleItem:
1747 case FadeInTrimHandleItem:
1748 if (mouse_mode == MouseObject && !internal_editing()) {
1749 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
1751 RegionView* rv = static_cast<RegionView*>(item->get_data ("regionview"));
1752 rect->set_fill_color (rv->get_fill_color());
1757 case FadeOutHandleItem:
1758 case FadeOutTrimHandleItem:
1759 if (mouse_mode == MouseObject && !internal_editing()) {
1760 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
1762 RegionView* rv = static_cast<RegionView*>(item->get_data ("regionview"));
1763 rect->set_fill_color (rv->get_fill_color ());
1768 case FeatureLineItem:
1770 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1771 line->set_outline_color (0xFF0000FF);
1782 /* third pass to handle entered track status in a comprehensible way.
1785 switch (item_type) {
1787 case AutomationLineItem:
1788 case ControlPointItem:
1789 /* these do not affect the current entered track state */
1790 clear_entered_track = false;
1793 case AutomationTrackItem:
1794 /* handled above already */
1806 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent*, ItemType item_type)
1814 switch (item_type) {
1815 case ControlPointItem:
1816 _verbose_cursor->hide ();
1820 case AutomationLineItem:
1821 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1823 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1825 line->set_outline_color (al->get_line_color());
1831 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1835 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
1836 location_flags_changed (loc);
1839 case MeterMarkerItem:
1840 case TempoMarkerItem:
1843 case FadeInTrimHandleItem:
1844 case FadeOutTrimHandleItem:
1845 case FadeInHandleItem:
1846 case FadeOutHandleItem:
1848 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
1850 rect->set_fill_color (ARDOUR_UI::config()->get_InactiveFadeHandle());
1855 case AutomationTrackItem:
1858 case FeatureLineItem:
1860 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1861 line->set_outline_color (ARDOUR_UI::config()->get_ZeroLine());
1873 Editor::scrub (framepos_t frame, double current_x)
1877 if (scrubbing_direction == 0) {
1879 _session->request_locate (frame, false);
1880 _session->request_transport_speed (0.1);
1881 scrubbing_direction = 1;
1885 if (last_scrub_x > current_x) {
1887 /* pointer moved to the left */
1889 if (scrubbing_direction > 0) {
1891 /* we reversed direction to go backwards */
1894 scrub_reverse_distance += (int) (last_scrub_x - current_x);
1898 /* still moving to the left (backwards) */
1900 scrub_reversals = 0;
1901 scrub_reverse_distance = 0;
1903 delta = 0.01 * (last_scrub_x - current_x);
1904 _session->request_transport_speed_nonzero (_session->transport_speed() - delta);
1908 /* pointer moved to the right */
1910 if (scrubbing_direction < 0) {
1911 /* we reversed direction to go forward */
1914 scrub_reverse_distance += (int) (current_x - last_scrub_x);
1917 /* still moving to the right */
1919 scrub_reversals = 0;
1920 scrub_reverse_distance = 0;
1922 delta = 0.01 * (current_x - last_scrub_x);
1923 _session->request_transport_speed_nonzero (_session->transport_speed() + delta);
1927 /* if there have been more than 2 opposite motion moves detected, or one that moves
1928 back more than 10 pixels, reverse direction
1931 if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
1933 if (scrubbing_direction > 0) {
1934 /* was forwards, go backwards */
1935 _session->request_transport_speed (-0.1);
1936 scrubbing_direction = -1;
1938 /* was backwards, go forwards */
1939 _session->request_transport_speed (0.1);
1940 scrubbing_direction = 1;
1943 scrub_reverse_distance = 0;
1944 scrub_reversals = 0;
1948 last_scrub_x = current_x;
1952 Editor::motion_handler (ArdourCanvas::Item* /*item*/, GdkEvent* event, bool from_autoscroll)
1954 _last_motion_y = event->motion.y;
1956 if (event->motion.is_hint) {
1959 /* We call this so that MOTION_NOTIFY events continue to be
1960 delivered to the canvas. We need to do this because we set
1961 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
1962 the density of the events, at the expense of a round-trip
1963 to the server. Given that this will mostly occur on cases
1964 where DISPLAY = :0.0, and given the cost of what the motion
1965 event might do, its a good tradeoff.
1968 _track_canvas->get_pointer (x, y);
1971 if (current_stepping_trackview) {
1972 /* don't keep the persistent stepped trackview if the mouse moves */
1973 current_stepping_trackview = 0;
1974 step_timeout.disconnect ();
1977 if (_session && _session->actively_recording()) {
1978 /* Sorry. no dragging stuff around while we record */
1982 update_join_object_range_location (event->motion.y);
1984 if (_drags->active ()) {
1985 return _drags->motion_handler (event, from_autoscroll);
1992 Editor::can_remove_control_point (ArdourCanvas::Item* item)
1994 ControlPoint* control_point;
1996 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
1997 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2001 AutomationLine& line = control_point->line ();
2002 if (dynamic_cast<AudioRegionGainLine*> (&line)) {
2003 /* we shouldn't remove the first or last gain point in region gain lines */
2004 if (line.is_last_point(*control_point) || line.is_first_point(*control_point)) {
2013 Editor::remove_control_point (ArdourCanvas::Item* item)
2015 if (!can_remove_control_point (item)) {
2019 ControlPoint* control_point;
2021 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2022 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2026 control_point->line().remove_point (*control_point);
2030 Editor::edit_control_point (ArdourCanvas::Item* item)
2032 ControlPoint* p = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"));
2035 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2039 ControlPointDialog d (p);
2042 if (d.run () != RESPONSE_ACCEPT) {
2046 p->line().modify_point_y (*p, d.get_y_fraction ());
2050 Editor::edit_notes (TimeAxisViewItem& tavi)
2052 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(&tavi);
2058 MidiRegionView::Selection const & s = mrv->selection();
2064 EditNoteDialog* d = new EditNoteDialog (&(*s.begin())->region_view(), s);
2068 d->signal_response().connect (sigc::bind (sigc::mem_fun (*this, &Editor::note_edit_done), d));
2072 Editor::note_edit_done (int r, EditNoteDialog* d)
2079 Editor::visible_order_range (int* low, int* high) const
2081 *low = TimeAxisView::max_order ();
2084 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
2086 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
2088 if (!rtv->hidden()) {
2090 if (*high < rtv->order()) {
2091 *high = rtv->order ();
2094 if (*low > rtv->order()) {
2095 *low = rtv->order ();
2102 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
2104 /* Either add to or set the set the region selection, unless
2105 this is an alignment click (control used)
2108 if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
2109 TimeAxisView* tv = &rv.get_time_axis_view();
2110 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
2112 if (rtv && rtv->is_track()) {
2113 speed = rtv->track()->speed();
2116 framepos_t where = get_preferred_edit_position();
2120 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
2122 align_region (rv.region(), SyncPoint, (framepos_t) (where * speed));
2124 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
2126 align_region (rv.region(), End, (framepos_t) (where * speed));
2130 align_region (rv.region(), Start, (framepos_t) (where * speed));
2137 Editor::collect_new_region_view (RegionView* rv)
2139 latest_regionviews.push_back (rv);
2143 Editor::collect_and_select_new_region_view (RegionView* rv)
2146 latest_regionviews.push_back (rv);
2150 Editor::cancel_selection ()
2152 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2153 (*i)->hide_selection ();
2156 selection->clear ();
2157 clicked_selection = 0;
2161 Editor::cancel_time_selection ()
2163 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2164 (*i)->hide_selection ();
2166 selection->time.clear ();
2167 clicked_selection = 0;
2171 Editor::point_trim (GdkEvent* event, framepos_t new_bound)
2173 RegionView* rv = clicked_regionview;
2175 /* Choose action dependant on which button was pressed */
2176 switch (event->button.button) {
2178 begin_reversible_command (_("start point trim"));
2180 if (selection->selected (rv)) {
2181 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
2182 i != selection->regions.by_layer().end(); ++i)
2184 if (!(*i)->region()->locked()) {
2185 (*i)->region()->clear_changes ();
2186 (*i)->region()->trim_front (new_bound);
2187 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2192 if (!rv->region()->locked()) {
2193 rv->region()->clear_changes ();
2194 rv->region()->trim_front (new_bound);
2195 _session->add_command(new StatefulDiffCommand (rv->region()));
2199 commit_reversible_command();
2203 begin_reversible_command (_("End point trim"));
2205 if (selection->selected (rv)) {
2207 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
2209 if (!(*i)->region()->locked()) {
2210 (*i)->region()->clear_changes();
2211 (*i)->region()->trim_end (new_bound);
2212 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2218 if (!rv->region()->locked()) {
2219 rv->region()->clear_changes ();
2220 rv->region()->trim_end (new_bound);
2221 _session->add_command (new StatefulDiffCommand (rv->region()));
2225 commit_reversible_command();
2234 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* /*event*/)
2239 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2240 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2244 Location* location = find_location_from_marker (marker, is_start);
2245 location->set_hidden (true, this);
2249 Editor::mouse_rename_region (ArdourCanvas::Item* /*item*/, GdkEvent* /*event*/)
2251 using namespace Gtkmm2ext;
2253 ArdourPrompter prompter (false);
2255 prompter.set_prompt (_("Name for region:"));
2256 prompter.set_initial_text (clicked_regionview->region()->name());
2257 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
2258 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
2259 prompter.show_all ();
2260 switch (prompter.run ()) {
2261 case Gtk::RESPONSE_ACCEPT:
2263 prompter.get_result(str);
2265 clicked_regionview->region()->set_name (str);
2274 Editor::mouse_brush_insert_region (RegionView* rv, framepos_t pos)
2276 /* no brushing without a useful snap setting */
2278 switch (_snap_mode) {
2280 return; /* can't work because it allows region to be placed anywhere */
2285 switch (_snap_type) {
2293 /* don't brush a copy over the original */
2295 if (pos == rv->region()->position()) {
2299 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
2301 if (rtv == 0 || !rtv->is_track()) {
2305 boost::shared_ptr<Playlist> playlist = rtv->playlist();
2306 double speed = rtv->track()->speed();
2308 playlist->clear_changes ();
2309 boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region(), true));
2310 playlist->add_region (new_region, (framepos_t) (pos * speed));
2311 _session->add_command (new StatefulDiffCommand (playlist));
2313 // playlist is frozen, so we have to update manually XXX this is disgusting
2315 playlist->RegionAdded (new_region); /* EMIT SIGNAL */
2319 Editor::track_height_step_timeout ()
2321 if (get_microseconds() - last_track_height_step_timestamp < 250000) {
2322 current_stepping_trackview = 0;
2329 Editor::add_region_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2331 assert (region_view);
2333 if (!region_view->region()->playlist()) {
2337 switch (Config->get_edit_mode()) {
2339 _drags->add (new RegionSpliceDrag (this, item, region_view, selection->regions.by_layer()));
2342 _drags->add (new RegionRippleDrag (this, item, region_view, selection->regions.by_layer()));
2345 _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), false, false));
2352 Editor::add_region_copy_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2354 assert (region_view);
2356 if (!region_view->region()->playlist()) {
2360 _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), false, true));
2364 Editor::add_region_brush_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2366 assert (region_view);
2368 if (!region_view->region()->playlist()) {
2372 if (Config->get_edit_mode() == Splice || Config->get_edit_mode() == Ripple) {
2376 _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), true, false));
2378 begin_reversible_command (Operations::drag_region_brush);
2381 /** Start a grab where a time range is selected, track(s) are selected, and the
2382 * user clicks and drags a region with a modifier in order to create a new region containing
2383 * the section of the clicked region that lies within the time range.
2386 Editor::start_selection_grab (ArdourCanvas::Item* /*item*/, GdkEvent* event)
2388 if (clicked_regionview == 0) {
2392 /* lets try to create new Region for the selection */
2394 vector<boost::shared_ptr<Region> > new_regions;
2395 create_region_from_selection (new_regions);
2397 if (new_regions.empty()) {
2401 /* XXX fix me one day to use all new regions */
2403 boost::shared_ptr<Region> region (new_regions.front());
2405 /* add it to the current stream/playlist.
2407 tricky: the streamview for the track will add a new regionview. we will
2408 catch the signal it sends when it creates the regionview to
2409 set the regionview we want to then drag.
2412 latest_regionviews.clear();
2413 sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
2415 /* A selection grab currently creates two undo/redo operations, one for
2416 creating the new region and another for moving it.
2419 begin_reversible_command (Operations::selection_grab);
2421 boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
2423 playlist->clear_changes ();
2424 clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
2425 _session->add_command(new StatefulDiffCommand (playlist));
2427 commit_reversible_command ();
2431 if (latest_regionviews.empty()) {
2432 /* something went wrong */
2436 /* we need to deselect all other regionviews, and select this one
2437 i'm ignoring undo stuff, because the region creation will take care of it
2439 selection->set (latest_regionviews);
2441 _drags->set (new RegionMoveDrag (this, latest_regionviews.front()->get_canvas_group(), latest_regionviews.front(), latest_regionviews, false, false), event);
2447 if (_drags->active ()) {
2450 selection->clear ();
2457 Editor::set_internal_edit (bool yn)
2459 if (_internal_editing == yn) {
2463 _internal_editing = yn;
2466 pre_internal_mouse_mode = mouse_mode;
2467 pre_internal_snap_type = _snap_type;
2468 pre_internal_snap_mode = _snap_mode;
2470 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2471 (*i)->enter_internal_edit_mode ();
2474 set_snap_to (internal_snap_type);
2475 set_snap_mode (internal_snap_mode);
2479 internal_snap_mode = _snap_mode;
2480 internal_snap_type = _snap_type;
2482 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2483 (*i)->leave_internal_edit_mode ();
2486 if (mouse_mode == MouseDraw && pre_internal_mouse_mode != MouseDraw) {
2487 /* we were drawing .. flip back to something sensible */
2488 set_mouse_mode (pre_internal_mouse_mode);
2491 set_snap_to (pre_internal_snap_type);
2492 set_snap_mode (pre_internal_snap_mode);
2495 reset_canvas_cursor ();
2496 MouseModeChanged ();
2499 /** Update _join_object_range_state which indicate whether we are over the top
2500 * or bottom half of a route view, used by the `join object/range' tool
2501 * mode. Coordinates in canvas space.
2504 Editor::update_join_object_range_location (double y)
2506 if (_internal_editing || !get_smart_mode()) {
2507 _join_object_range_state = JOIN_OBJECT_RANGE_NONE;
2511 JoinObjectRangeState const old = _join_object_range_state;
2513 if (mouse_mode == MouseObject) {
2514 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2515 } else if (mouse_mode == MouseRange) {
2516 _join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
2519 if (entered_regionview) {
2521 ArdourCanvas::Duple const item_space = entered_regionview->get_canvas_group()->canvas_to_item (ArdourCanvas::Duple (0, y));
2522 double const c = item_space.y / entered_regionview->height();
2524 _join_object_range_state = c <= 0.5 ? JOIN_OBJECT_RANGE_RANGE : JOIN_OBJECT_RANGE_OBJECT;
2526 if (_join_object_range_state != old) {
2527 set_canvas_cursor (which_track_cursor ());
2530 } else if (entered_track) {
2532 RouteTimeAxisView* entered_route_view = dynamic_cast<RouteTimeAxisView*> (entered_track);
2534 if (entered_route_view) {
2539 entered_route_view->canvas_display()->canvas_to_item (cx, cy);
2541 double track_height = entered_route_view->view()->child_height();
2542 if (Config->get_show_name_highlight()) {
2543 track_height -= TimeAxisViewItem::NAME_HIGHLIGHT_SIZE;
2545 double const c = cy / track_height;
2549 _join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
2551 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2555 /* Other kinds of tracks use object mode */
2556 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2559 if (_join_object_range_state != old) {
2560 set_canvas_cursor (which_track_cursor ());
2566 Editor::effective_mouse_mode () const
2568 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
2570 } else if (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE) {
2578 Editor::remove_midi_note (ArdourCanvas::Item* item, GdkEvent *)
2580 NoteBase* e = reinterpret_cast<NoteBase*> (item->get_data ("notebase"));
2583 e->region_view().delete_note (e->note ());
2586 /** Obtain the pointer position in canvas coordinates */
2588 Editor::get_pointer_position (double& x, double& y) const
2591 _track_canvas->get_pointer (px, py);
2592 _track_canvas->window_to_canvas (px, py, x, y);