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 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, 0, 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, 0, 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:
1564 switch (item_type) {
1565 case AutomationTrackItem:
1566 atv = dynamic_cast<AutomationTimeAxisView*>(clicked_axisview);
1568 bool with_guard_points = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
1569 atv->add_automation_event (event, where, event->button.y, with_guard_points);
1579 switch (item_type) {
1582 /* check that we didn't drag before releasing, since
1583 its really annoying to create new control
1584 points when doing this.
1586 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (clicked_regionview);
1587 if (!were_dragging && arv) {
1588 bool with_guard_points = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
1589 arv->add_gain_point_event (item, event, with_guard_points);
1595 case AutomationTrackItem: {
1596 bool with_guard_points = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
1597 dynamic_cast<AutomationTimeAxisView*>(clicked_axisview)->
1598 add_automation_event (event, where, event->button.y, with_guard_points);
1608 pop_canvas_cursor ();
1609 if (scrubbing_direction == 0) {
1610 /* no drag, just a click */
1611 switch (item_type) {
1613 play_selected_region ();
1619 /* make sure we stop */
1620 _session->request_transport_speed (0.0);
1629 /* do any (de)selection operations that should occur on button release */
1630 button_selection (item, event, item_type);
1639 switch (item_type) {
1641 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1643 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier|Keyboard::SecondaryModifier))) {
1646 // Button2 click is unused
1661 // x_style_paste (where, 1.0);
1682 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1689 /* by the time we reach here, entered_regionview and entered trackview
1690 * will have already been set as appropriate. Things are done this
1691 * way because this method isn't passed a pointer to a variable type of
1692 * thing that is entered (which may or may not be canvas item).
1693 * (e.g. the actual entered regionview)
1696 choose_canvas_cursor_on_entry (&event->crossing, item_type);
1698 switch (item_type) {
1699 case ControlPointItem:
1700 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1701 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1704 fraction = 1.0 - (cp->get_y() / cp->line().height());
1706 _verbose_cursor->set (cp->line().get_verbose_cursor_string (fraction));
1707 _verbose_cursor->show ();
1712 if (mouse_mode == MouseGain) {
1713 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1715 line->set_outline_color (ARDOUR_UI::config()->get_EnteredGainLine());
1720 case AutomationLineItem:
1721 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1722 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1724 line->set_outline_color (ARDOUR_UI::config()->get_EnteredAutomationLine());
1729 case AutomationTrackItem:
1730 AutomationTimeAxisView* atv;
1731 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1732 clear_entered_track = false;
1733 set_entered_track (atv);
1738 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1741 entered_marker = marker;
1742 marker->set_color_rgba (ARDOUR_UI::config()->get_EnteredMarker());
1744 case MeterMarkerItem:
1745 case TempoMarkerItem:
1748 case FadeInHandleItem:
1749 case FadeInTrimHandleItem:
1750 if (mouse_mode == MouseObject && !internal_editing()) {
1751 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
1753 RegionView* rv = static_cast<RegionView*>(item->get_data ("regionview"));
1754 rect->set_fill_color (rv->get_fill_color());
1759 case FadeOutHandleItem:
1760 case FadeOutTrimHandleItem:
1761 if (mouse_mode == MouseObject && !internal_editing()) {
1762 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
1764 RegionView* rv = static_cast<RegionView*>(item->get_data ("regionview"));
1765 rect->set_fill_color (rv->get_fill_color ());
1770 case FeatureLineItem:
1772 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1773 line->set_outline_color (0xFF0000FF);
1784 /* third pass to handle entered track status in a comprehensible way.
1787 switch (item_type) {
1789 case AutomationLineItem:
1790 case ControlPointItem:
1791 /* these do not affect the current entered track state */
1792 clear_entered_track = false;
1795 case AutomationTrackItem:
1796 /* handled above already */
1808 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent*, ItemType item_type)
1816 reset_canvas_cursor ();
1818 switch (item_type) {
1819 case ControlPointItem:
1820 _verbose_cursor->hide ();
1824 case AutomationLineItem:
1825 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1827 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1829 line->set_outline_color (al->get_line_color());
1835 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1839 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
1840 location_flags_changed (loc);
1843 case MeterMarkerItem:
1844 case TempoMarkerItem:
1847 case FadeInTrimHandleItem:
1848 case FadeOutTrimHandleItem:
1849 case FadeInHandleItem:
1850 case FadeOutHandleItem:
1852 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
1854 rect->set_fill_color (ARDOUR_UI::config()->get_InactiveFadeHandle());
1859 case AutomationTrackItem:
1862 case FeatureLineItem:
1864 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1865 line->set_outline_color (ARDOUR_UI::config()->get_ZeroLine());
1877 Editor::scrub (framepos_t frame, double current_x)
1881 if (scrubbing_direction == 0) {
1883 _session->request_locate (frame, false);
1884 _session->request_transport_speed (0.1);
1885 scrubbing_direction = 1;
1889 if (last_scrub_x > current_x) {
1891 /* pointer moved to the left */
1893 if (scrubbing_direction > 0) {
1895 /* we reversed direction to go backwards */
1898 scrub_reverse_distance += (int) (last_scrub_x - current_x);
1902 /* still moving to the left (backwards) */
1904 scrub_reversals = 0;
1905 scrub_reverse_distance = 0;
1907 delta = 0.01 * (last_scrub_x - current_x);
1908 _session->request_transport_speed_nonzero (_session->transport_speed() - delta);
1912 /* pointer moved to the right */
1914 if (scrubbing_direction < 0) {
1915 /* we reversed direction to go forward */
1918 scrub_reverse_distance += (int) (current_x - last_scrub_x);
1921 /* still moving to the right */
1923 scrub_reversals = 0;
1924 scrub_reverse_distance = 0;
1926 delta = 0.01 * (current_x - last_scrub_x);
1927 _session->request_transport_speed_nonzero (_session->transport_speed() + delta);
1931 /* if there have been more than 2 opposite motion moves detected, or one that moves
1932 back more than 10 pixels, reverse direction
1935 if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
1937 if (scrubbing_direction > 0) {
1938 /* was forwards, go backwards */
1939 _session->request_transport_speed (-0.1);
1940 scrubbing_direction = -1;
1942 /* was backwards, go forwards */
1943 _session->request_transport_speed (0.1);
1944 scrubbing_direction = 1;
1947 scrub_reverse_distance = 0;
1948 scrub_reversals = 0;
1952 last_scrub_x = current_x;
1956 Editor::motion_handler (ArdourCanvas::Item* /*item*/, GdkEvent* event, bool from_autoscroll)
1958 _last_motion_y = event->motion.y;
1960 if (event->motion.is_hint) {
1963 /* We call this so that MOTION_NOTIFY events continue to be
1964 delivered to the canvas. We need to do this because we set
1965 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
1966 the density of the events, at the expense of a round-trip
1967 to the server. Given that this will mostly occur on cases
1968 where DISPLAY = :0.0, and given the cost of what the motion
1969 event might do, its a good tradeoff.
1972 _track_canvas->get_pointer (x, y);
1975 if (current_stepping_trackview) {
1976 /* don't keep the persistent stepped trackview if the mouse moves */
1977 current_stepping_trackview = 0;
1978 step_timeout.disconnect ();
1981 if (_session && _session->actively_recording()) {
1982 /* Sorry. no dragging stuff around while we record */
1986 update_join_object_range_location (event->motion.y);
1988 if (_drags->active ()) {
1989 return _drags->motion_handler (event, from_autoscroll);
1996 Editor::can_remove_control_point (ArdourCanvas::Item* item)
1998 ControlPoint* control_point;
2000 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2001 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2005 AutomationLine& line = control_point->line ();
2006 if (dynamic_cast<AudioRegionGainLine*> (&line)) {
2007 /* we shouldn't remove the first or last gain point in region gain lines */
2008 if (line.is_last_point(*control_point) || line.is_first_point(*control_point)) {
2017 Editor::remove_control_point (ArdourCanvas::Item* item)
2019 if (!can_remove_control_point (item)) {
2023 ControlPoint* control_point;
2025 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2026 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2030 control_point->line().remove_point (*control_point);
2034 Editor::edit_control_point (ArdourCanvas::Item* item)
2036 ControlPoint* p = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"));
2039 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2043 ControlPointDialog d (p);
2046 if (d.run () != RESPONSE_ACCEPT) {
2050 p->line().modify_point_y (*p, d.get_y_fraction ());
2054 Editor::edit_notes (TimeAxisViewItem& tavi)
2056 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(&tavi);
2062 MidiRegionView::Selection const & s = mrv->selection();
2068 EditNoteDialog* d = new EditNoteDialog (&(*s.begin())->region_view(), s);
2072 d->signal_response().connect (sigc::bind (sigc::mem_fun (*this, &Editor::note_edit_done), d));
2076 Editor::note_edit_done (int r, EditNoteDialog* d)
2083 Editor::visible_order_range (int* low, int* high) const
2085 *low = TimeAxisView::max_order ();
2088 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
2090 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
2092 if (!rtv->hidden()) {
2094 if (*high < rtv->order()) {
2095 *high = rtv->order ();
2098 if (*low > rtv->order()) {
2099 *low = rtv->order ();
2106 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
2108 /* Either add to or set the set the region selection, unless
2109 this is an alignment click (control used)
2112 if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
2113 TimeAxisView* tv = &rv.get_time_axis_view();
2114 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
2116 if (rtv && rtv->is_track()) {
2117 speed = rtv->track()->speed();
2120 framepos_t where = get_preferred_edit_position();
2124 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
2126 align_region (rv.region(), SyncPoint, (framepos_t) (where * speed));
2128 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
2130 align_region (rv.region(), End, (framepos_t) (where * speed));
2134 align_region (rv.region(), Start, (framepos_t) (where * speed));
2141 Editor::collect_new_region_view (RegionView* rv)
2143 latest_regionviews.push_back (rv);
2147 Editor::collect_and_select_new_region_view (RegionView* rv)
2150 latest_regionviews.push_back (rv);
2154 Editor::cancel_selection ()
2156 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2157 (*i)->hide_selection ();
2160 selection->clear ();
2161 clicked_selection = 0;
2165 Editor::cancel_time_selection ()
2167 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2168 (*i)->hide_selection ();
2170 selection->time.clear ();
2171 clicked_selection = 0;
2175 Editor::point_trim (GdkEvent* event, framepos_t new_bound)
2177 RegionView* rv = clicked_regionview;
2179 /* Choose action dependant on which button was pressed */
2180 switch (event->button.button) {
2182 begin_reversible_command (_("start point trim"));
2184 if (selection->selected (rv)) {
2185 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
2186 i != selection->regions.by_layer().end(); ++i)
2188 if (!(*i)->region()->locked()) {
2189 (*i)->region()->clear_changes ();
2190 (*i)->region()->trim_front (new_bound);
2191 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2196 if (!rv->region()->locked()) {
2197 rv->region()->clear_changes ();
2198 rv->region()->trim_front (new_bound);
2199 _session->add_command(new StatefulDiffCommand (rv->region()));
2203 commit_reversible_command();
2207 begin_reversible_command (_("End point trim"));
2209 if (selection->selected (rv)) {
2211 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
2213 if (!(*i)->region()->locked()) {
2214 (*i)->region()->clear_changes();
2215 (*i)->region()->trim_end (new_bound);
2216 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2222 if (!rv->region()->locked()) {
2223 rv->region()->clear_changes ();
2224 rv->region()->trim_end (new_bound);
2225 _session->add_command (new StatefulDiffCommand (rv->region()));
2229 commit_reversible_command();
2238 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* /*event*/)
2243 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2244 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2248 Location* location = find_location_from_marker (marker, is_start);
2249 location->set_hidden (true, this);
2253 Editor::mouse_rename_region (ArdourCanvas::Item* /*item*/, GdkEvent* /*event*/)
2255 using namespace Gtkmm2ext;
2257 ArdourPrompter prompter (false);
2259 prompter.set_prompt (_("Name for region:"));
2260 prompter.set_initial_text (clicked_regionview->region()->name());
2261 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
2262 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
2263 prompter.show_all ();
2264 switch (prompter.run ()) {
2265 case Gtk::RESPONSE_ACCEPT:
2267 prompter.get_result(str);
2269 clicked_regionview->region()->set_name (str);
2278 Editor::mouse_brush_insert_region (RegionView* rv, framepos_t pos)
2280 /* no brushing without a useful snap setting */
2282 switch (_snap_mode) {
2284 return; /* can't work because it allows region to be placed anywhere */
2289 switch (_snap_type) {
2297 /* don't brush a copy over the original */
2299 if (pos == rv->region()->position()) {
2303 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
2305 if (rtv == 0 || !rtv->is_track()) {
2309 boost::shared_ptr<Playlist> playlist = rtv->playlist();
2310 double speed = rtv->track()->speed();
2312 playlist->clear_changes ();
2313 boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region(), true));
2314 playlist->add_region (new_region, (framepos_t) (pos * speed));
2315 _session->add_command (new StatefulDiffCommand (playlist));
2317 // playlist is frozen, so we have to update manually XXX this is disgusting
2319 playlist->RegionAdded (new_region); /* EMIT SIGNAL */
2323 Editor::track_height_step_timeout ()
2325 if (get_microseconds() - last_track_height_step_timestamp < 250000) {
2326 current_stepping_trackview = 0;
2333 Editor::add_region_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2335 assert (region_view);
2337 if (!region_view->region()->playlist()) {
2341 switch (Config->get_edit_mode()) {
2343 _drags->add (new RegionSpliceDrag (this, item, region_view, selection->regions.by_layer()));
2346 _drags->add (new RegionRippleDrag (this, item, region_view, selection->regions.by_layer()));
2349 _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), false, false));
2356 Editor::add_region_copy_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2358 assert (region_view);
2360 if (!region_view->region()->playlist()) {
2364 _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), false, true));
2368 Editor::add_region_brush_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2370 assert (region_view);
2372 if (!region_view->region()->playlist()) {
2376 if (Config->get_edit_mode() == Splice || Config->get_edit_mode() == Ripple) {
2380 _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), true, false));
2382 begin_reversible_command (Operations::drag_region_brush);
2385 /** Start a grab where a time range is selected, track(s) are selected, and the
2386 * user clicks and drags a region with a modifier in order to create a new region containing
2387 * the section of the clicked region that lies within the time range.
2390 Editor::start_selection_grab (ArdourCanvas::Item* /*item*/, GdkEvent* event)
2392 if (clicked_regionview == 0) {
2396 /* lets try to create new Region for the selection */
2398 vector<boost::shared_ptr<Region> > new_regions;
2399 create_region_from_selection (new_regions);
2401 if (new_regions.empty()) {
2405 /* XXX fix me one day to use all new regions */
2407 boost::shared_ptr<Region> region (new_regions.front());
2409 /* add it to the current stream/playlist.
2411 tricky: the streamview for the track will add a new regionview. we will
2412 catch the signal it sends when it creates the regionview to
2413 set the regionview we want to then drag.
2416 latest_regionviews.clear();
2417 sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
2419 /* A selection grab currently creates two undo/redo operations, one for
2420 creating the new region and another for moving it.
2423 begin_reversible_command (Operations::selection_grab);
2425 boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
2427 playlist->clear_changes ();
2428 clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
2429 _session->add_command(new StatefulDiffCommand (playlist));
2431 commit_reversible_command ();
2435 if (latest_regionviews.empty()) {
2436 /* something went wrong */
2440 /* we need to deselect all other regionviews, and select this one
2441 i'm ignoring undo stuff, because the region creation will take care of it
2443 selection->set (latest_regionviews);
2445 _drags->set (new RegionMoveDrag (this, latest_regionviews.front()->get_canvas_group(), latest_regionviews.front(), latest_regionviews, false, false), event);
2451 if (_drags->active ()) {
2454 selection->clear ();
2461 Editor::set_internal_edit (bool yn)
2463 if (_internal_editing == yn) {
2467 _internal_editing = yn;
2470 pre_internal_mouse_mode = mouse_mode;
2471 pre_internal_snap_type = _snap_type;
2472 pre_internal_snap_mode = _snap_mode;
2474 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2475 (*i)->enter_internal_edit_mode ();
2478 set_snap_to (internal_snap_type);
2479 set_snap_mode (internal_snap_mode);
2483 internal_snap_mode = _snap_mode;
2484 internal_snap_type = _snap_type;
2486 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2487 (*i)->leave_internal_edit_mode ();
2490 if (mouse_mode == MouseDraw && pre_internal_mouse_mode != MouseDraw) {
2491 /* we were drawing .. flip back to something sensible */
2492 set_mouse_mode (pre_internal_mouse_mode);
2495 set_snap_to (pre_internal_snap_type);
2496 set_snap_mode (pre_internal_snap_mode);
2499 reset_canvas_cursor ();
2500 MouseModeChanged ();
2503 /** Update _join_object_range_state which indicate whether we are over the top
2504 * or bottom half of a route view, used by the `join object/range' tool
2505 * mode. Coordinates in canvas space.
2508 Editor::update_join_object_range_location (double y)
2510 if (_internal_editing || !get_smart_mode()) {
2511 _join_object_range_state = JOIN_OBJECT_RANGE_NONE;
2515 JoinObjectRangeState const old = _join_object_range_state;
2517 if (mouse_mode == MouseObject) {
2518 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2519 } else if (mouse_mode == MouseRange) {
2520 _join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
2523 if (entered_regionview) {
2525 ArdourCanvas::Duple const item_space = entered_regionview->get_canvas_group()->canvas_to_item (ArdourCanvas::Duple (0, y));
2526 double const c = item_space.y / entered_regionview->height();
2528 _join_object_range_state = c <= 0.5 ? JOIN_OBJECT_RANGE_RANGE : JOIN_OBJECT_RANGE_OBJECT;
2530 if (_join_object_range_state != old) {
2531 set_canvas_cursor (which_track_cursor ());
2534 } else if (entered_track) {
2536 RouteTimeAxisView* entered_route_view = dynamic_cast<RouteTimeAxisView*> (entered_track);
2538 if (entered_route_view) {
2543 entered_route_view->canvas_display()->canvas_to_item (cx, cy);
2545 double track_height = entered_route_view->view()->child_height();
2546 if (Config->get_show_name_highlight()) {
2547 track_height -= TimeAxisViewItem::NAME_HIGHLIGHT_SIZE;
2549 double const c = cy / track_height;
2553 _join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
2555 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2559 /* Other kinds of tracks use object mode */
2560 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2563 if (_join_object_range_state != old) {
2564 set_canvas_cursor (which_track_cursor ());
2570 Editor::effective_mouse_mode () const
2572 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
2574 } else if (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE) {
2582 Editor::remove_midi_note (ArdourCanvas::Item* item, GdkEvent *)
2584 NoteBase* e = reinterpret_cast<NoteBase*> (item->get_data ("notebase"));
2587 e->region_view().delete_note (e->note ());
2590 /** Obtain the pointer position in canvas coordinates */
2592 Editor::get_pointer_position (double& x, double& y) const
2595 _track_canvas->get_pointer (px, py);
2596 _track_canvas->window_to_canvas (px, py, x, y);