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-timefx"));
263 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-audition"));
269 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
272 /* go there and back to ensure that the toggled handler is called to set up mouse_mode */
273 tact->set_active (false);
274 tact->set_active (true);
276 //NOTE: this will result in a call to mouse_mode_toggled which does the heavy lifting
280 Editor::mouse_mode_toggled (MouseMode m)
282 Glib::RefPtr<Action> act;
283 Glib::RefPtr<ToggleAction> tact;
285 if (ARDOUR::Profile->get_mixbus()) {
286 if ( m == MouseCut) m = MouseObject;
291 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-range"));
295 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-object"));
299 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-cut"));
303 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-draw"));
307 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-timefx"));
311 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-audition"));
317 tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
320 if (!tact->get_active()) {
321 /* this was just the notification that the old mode has been
322 * left. we'll get called again with the new mode active in a
328 if (_session && mouse_mode == MouseAudition) {
329 /* stop transport and reset default speed to avoid oddness with
331 _session->request_transport_speed (0.0, true);
338 /* this should generate a new enter event which will
339 trigger the appropiate cursor.
343 _track_canvas->re_enter ();
346 set_gain_envelope_visibility ();
348 update_time_selection_display ();
350 MouseModeChanged (); /* EMIT SIGNAL */
354 Editor::update_time_selection_display ()
356 switch (mouse_mode) {
358 selection->clear_objects ();
361 selection->clear_time ();
367 Editor::step_mouse_mode (bool next)
369 const int n_mouse_modes = (int)MouseDraw + 1;
370 int current = (int)current_mouse_mode();
372 set_mouse_mode((MouseMode)((current + 1) % n_mouse_modes));
374 set_mouse_mode((MouseMode)((current + n_mouse_modes - 1) % n_mouse_modes));
379 Editor::toggle_internal_editing_from_double_click (GdkEvent* event)
381 if (_drags->active()) {
382 _drags->end_grab (event);
385 ActionManager::do_action ("MouseMode", "toggle-internal-edit");
387 /* prevent reversion of edit cursor on button release */
389 pre_press_cursor = 0;
395 Editor::button_selection (ArdourCanvas::Item* /*item*/, GdkEvent* event, ItemType item_type)
397 /* in object/audition/timefx/gain-automation mode,
398 any button press sets the selection if the object
399 can be selected. this is a bit of hack, because
400 we want to avoid this if the mouse operation is a
403 note: not dbl-click or triple-click
405 Also note that there is no region selection in internal edit mode, otherwise
406 for operations operating on the selection (e.g. cut) it is not obvious whether
407 to cut notes or regions.
410 MouseMode eff_mouse_mode = effective_mouse_mode ();
412 if (eff_mouse_mode == MouseCut) {
413 /* never change selection in cut mode */
417 if (get_smart_mode() && eff_mouse_mode == MouseRange && event->button.button == 3 && item_type == RegionItem) {
418 /* context clicks are always about object properties, even if
419 we're in range mode within smart mode.
421 eff_mouse_mode = MouseObject;
424 /* special case: allow drag of region fade in/out in object mode with join object/range enabled */
425 if (get_smart_mode()) {
427 case FadeInHandleItem:
428 case FadeInTrimHandleItem:
429 case FadeOutHandleItem:
430 case FadeOutTrimHandleItem:
431 eff_mouse_mode = MouseObject;
438 if (((mouse_mode != MouseObject) &&
439 (mouse_mode != MouseAudition || item_type != RegionItem) &&
440 (mouse_mode != MouseTimeFX || item_type != RegionItem) &&
441 (mouse_mode != MouseDraw)) ||
442 ((event->type != GDK_BUTTON_PRESS && event->type != GDK_BUTTON_RELEASE) || event->button.button > 3) ||
443 (internal_editing() && mouse_mode != MouseTimeFX)) {
448 if (event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) {
450 if ((event->button.state & Keyboard::RelevantModifierKeyMask) && event->button.button != 1) {
452 /* almost no selection action on modified button-2 or button-3 events */
454 if (item_type != RegionItem && event->button.button != 2) {
460 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
461 bool press = (event->type == GDK_BUTTON_PRESS);
466 if (eff_mouse_mode != MouseRange) {
467 set_selected_regionview_from_click (press, op);
469 /* don't change the selection unless the
470 clicked track is not currently selected. if
471 so, "collapse" the selection to just this
474 if (!selection->selected (clicked_axisview)) {
475 set_selected_track_as_side_effect (Selection::Set);
479 if (eff_mouse_mode != MouseRange) {
480 set_selected_regionview_from_click (press, op);
485 case RegionViewNameHighlight:
487 case LeftFrameHandle:
488 case RightFrameHandle:
489 case FadeInHandleItem:
490 case FadeInTrimHandleItem:
492 case FadeOutHandleItem:
493 case FadeOutTrimHandleItem:
495 case StartCrossFadeItem:
496 case EndCrossFadeItem:
497 if (get_smart_mode() || eff_mouse_mode != MouseRange) {
498 set_selected_regionview_from_click (press, op);
499 } else if (event->type == GDK_BUTTON_PRESS) {
500 set_selected_track_as_side_effect (op);
504 case ControlPointItem:
505 set_selected_track_as_side_effect (op);
506 if (eff_mouse_mode != MouseRange) {
507 set_selected_control_point_from_click (press, op);
512 /* for context click, select track */
513 if (event->button.button == 3) {
514 selection->clear_tracks ();
515 set_selected_track_as_side_effect (op);
519 case AutomationTrackItem:
520 set_selected_track_as_side_effect (op);
529 Editor::button_press_handler_1 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
531 /* single mouse clicks on any of these item types operate
532 independent of mouse mode, mostly because they are
533 not on the main track canvas or because we want
538 case PlayheadCursorItem:
539 _drags->set (new CursorDrag (this, *playhead_cursor, true), event);
543 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
544 hide_marker (item, event);
546 _drags->set (new MarkerDrag (this, item), event);
550 case TempoMarkerItem:
552 TempoMarker* m = reinterpret_cast<TempoMarker*> (item->get_data ("marker"));
555 new TempoMarkerDrag (
558 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
565 case MeterMarkerItem:
567 MeterMarker* m = reinterpret_cast<MeterMarker*> (item->get_data ("marker"));
570 new MeterMarkerDrag (
573 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
581 _drags->set (new VideoTimeLineDrag (this, item), event);
588 case TimecodeRulerItem:
589 case SamplesRulerItem:
590 case MinsecRulerItem:
592 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
593 _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
599 case RangeMarkerBarItem:
600 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier)) {
601 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateSkipMarker), event);
602 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
603 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateRangeMarker), event);
605 _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
610 case CdMarkerBarItem:
611 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
612 _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
614 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateCDMarker), event);
619 case TransportMarkerBarItem:
620 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
621 _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
623 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateTransportMarker), event);
632 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
633 /* special case: allow trim of range selections in joined object mode;
634 in theory eff should equal MouseRange in this case, but it doesn't
635 because entering the range selection canvas item results in entered_regionview
636 being set to 0, so update_join_object_range_location acts as if we aren't
639 if (item_type == StartSelectionTrimItem) {
640 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
641 } else if (item_type == EndSelectionTrimItem) {
642 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
646 Editing::MouseMode eff = effective_mouse_mode ();
648 /* special case: allow drag of region fade in/out in object mode with join object/range enabled */
649 if (get_smart_mode()) {
651 case FadeInHandleItem:
652 case FadeInTrimHandleItem:
653 case FadeOutHandleItem:
654 case FadeOutTrimHandleItem:
662 /* there is no Range mode when in internal edit mode */
663 if (eff == MouseRange && internal_editing()) {
670 case StartSelectionTrimItem:
671 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
674 case EndSelectionTrimItem:
675 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
679 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
680 start_selection_grab (item, event);
682 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
683 /* grab selection for moving */
684 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionMove), event);
686 /* this was debated, but decided the more common action was to
687 make a new selection */
688 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
693 if (internal_editing()) {
694 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
695 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
699 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::RangeSelectModifier)) {
700 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionExtend), event);
702 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
708 case RegionViewNameHighlight:
709 if (!clicked_regionview->region()->locked()) {
710 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
716 if (!internal_editing()) {
717 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::RangeSelectModifier)) {
718 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionExtend), event);
720 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
730 case FadeInHandleItem:
731 case FadeOutHandleItem:
732 case LeftFrameHandle:
733 case RightFrameHandle:
734 case FeatureLineItem:
735 case RegionViewNameHighlight:
738 case AutomationTrackItem:
739 _drags->set (new RegionCutDrag (this, item, canvas_event_sample (event)), event, current_canvas_cursor);
750 /* Existing note: allow trimming/motion */
751 if (internal_editing()) {
752 NoteBase* cn = reinterpret_cast<NoteBase*> (item->get_data ("notebase"));
754 if (cn->big_enough_to_trim() && cn->mouse_near_ends()) {
755 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
757 _drags->set (new NoteDrag (this, item), event);
767 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)) &&
768 event->type == GDK_BUTTON_PRESS) {
770 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
772 } else if (event->type == GDK_BUTTON_PRESS) {
775 case FadeInHandleItem:
777 _drags->set (new FadeInDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), selection->regions), event, _cursors->fade_in);
781 case FadeOutHandleItem:
783 _drags->set (new FadeOutDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), selection->regions), event, _cursors->fade_out);
787 case StartCrossFadeItem:
788 case EndCrossFadeItem:
789 /* we might allow user to grab inside the fade to trim a region with preserve_fade_anchor. for not this is not fully implemented */
790 // if (!clicked_regionview->region()->locked()) {
791 // _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer(), true), event);
796 case FeatureLineItem:
798 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier)) {
799 remove_transient(item);
803 _drags->set (new FeatureLineDrag (this, item), event);
809 if (dynamic_cast<AutomationRegionView*> (clicked_regionview)) {
810 /* click on an automation region view; do nothing here and let the ARV's signal handler
816 if (internal_editing ()) {
820 /* click on a normal region view */
821 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
822 add_region_copy_drag (item, event, clicked_regionview);
823 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
824 add_region_brush_drag (item, event, clicked_regionview);
826 add_region_drag (item, event, clicked_regionview);
830 // if (!internal_editing() && (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE && !selection->regions.empty())) {
831 // _drags->add (new SelectionDrag (this, clicked_axisview->get_selection_rect (clicked_selection)->rect, SelectionDrag::SelectionMove));
834 _drags->start_grab (event);
838 case RegionViewNameHighlight:
839 case LeftFrameHandle:
840 case RightFrameHandle:
841 if (!clicked_regionview->region()->locked()) {
842 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
847 case FadeInTrimHandleItem:
848 case FadeOutTrimHandleItem:
849 if (!clicked_regionview->region()->locked()) {
850 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer(), true), event);
857 /* rename happens on edit clicks */
858 if (clicked_regionview->get_name_highlight()) {
859 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer()), event);
865 case ControlPointItem:
866 _drags->set (new ControlPointDrag (this, item), event);
870 case AutomationLineItem:
871 _drags->set (new LineDrag (this, item), event);
876 if (internal_editing()) {
877 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
878 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
882 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
886 case AutomationTrackItem:
888 TimeAxisView* parent = clicked_axisview->get_parent ();
889 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (clicked_axisview);
891 if (parent && dynamic_cast<MidiTimeAxisView*> (parent) && atv->show_regions ()) {
893 RouteTimeAxisView* p = dynamic_cast<RouteTimeAxisView*> (parent);
895 boost::shared_ptr<Playlist> pl = p->track()->playlist ();
896 if (pl->n_regions() == 0) {
897 /* Parent has no regions; create one so that we have somewhere to put automation */
898 _drags->set (new RegionCreateDrag (this, item, parent), event);
900 /* See if there's a region before the click that we can extend, and extend it if so */
901 framepos_t const t = canvas_event_sample (event);
902 boost::shared_ptr<Region> prev = pl->find_next_region (t, End, -1);
904 _drags->set (new RegionCreateDrag (this, item, parent), event);
906 prev->set_length (t - prev->position ());
910 /* rubberband drag to select automation points */
911 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
935 _drags->set (new LineDrag (this, item), event);
938 case ControlPointItem:
939 _drags->set (new ControlPointDrag (this, item), event);
945 AudioRegionView* arv = dynamic_cast<AudioRegionView *> (clicked_regionview);
947 _drags->set (new AutomationRangeDrag (this, arv, selection->time), event, _cursors->up_down);
949 double const y = event->button.y;
950 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
952 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
954 /* smart "join" mode: drag automation */
955 _drags->set (new AutomationRangeDrag (this, atv, selection->time), event, _cursors->up_down);
963 case AutomationLineItem:
964 _drags->set (new LineDrag (this, item), event);
968 /* Existing note: allow trimming/motion */
969 if (internal_editing()) {
970 /* trim notes if we're in internal edit mode and near the ends of the note */
971 NoteBase* cn = reinterpret_cast<NoteBase*>(item->get_data ("notebase"));
973 if (cn->big_enough_to_trim() && cn->mouse_near_ends()) {
974 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
976 _drags->set (new NoteDrag (this, item), event);
983 if (internal_editing()) {
984 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
985 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
998 if (internal_editing() && item_type == NoteItem ) {
999 /* drag notes if we're in internal edit mode */
1000 NoteBase* cn = reinterpret_cast<NoteBase*>(item->get_data ("notebase"));
1002 if (cn->big_enough_to_trim()) {
1003 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
1006 } else if (clicked_regionview) {
1008 _drags->set (new TimeFXDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1014 _drags->set (new ScrubDrag (this, item), event);
1015 scrub_reversals = 0;
1016 scrub_reverse_distance = 0;
1017 last_scrub_x = event->button.x;
1018 scrubbing_direction = 0;
1019 push_canvas_cursor (_cursors->transparent);
1031 Editor::button_press_handler_2 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1033 Editing::MouseMode const eff = effective_mouse_mode ();
1036 switch (item_type) {
1038 if (internal_editing ()) {
1039 /* no region drags in internal edit mode */
1043 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
1044 add_region_copy_drag (item, event, clicked_regionview);
1046 add_region_drag (item, event, clicked_regionview);
1048 _drags->start_grab (event);
1051 case ControlPointItem:
1052 _drags->set (new ControlPointDrag (this, item), event);
1060 switch (item_type) {
1061 case RegionViewNameHighlight:
1062 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1066 case LeftFrameHandle:
1067 case RightFrameHandle:
1068 if (!internal_editing ()) {
1069 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1074 case RegionViewName:
1075 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer()), event);
1089 /* relax till release */
1101 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1103 if (event->type == GDK_2BUTTON_PRESS) {
1104 _drags->mark_double_click ();
1105 gdk_pointer_ungrab (GDK_CURRENT_TIME);
1109 if (event->type != GDK_BUTTON_PRESS) {
1113 pre_press_cursor = current_canvas_cursor;
1115 _track_canvas->grab_focus();
1117 if (_session && _session->actively_recording()) {
1121 if (internal_editing()) {
1122 bool leave_internal_edit_mode = false;
1124 switch (item_type) {
1129 if (!dynamic_cast<MidiRegionView*> (clicked_regionview) && !dynamic_cast<AutomationRegionView*> (clicked_regionview)) {
1130 leave_internal_edit_mode = true;
1134 case PlayheadCursorItem:
1136 case TempoMarkerItem:
1137 case MeterMarkerItem:
1141 case RangeMarkerBarItem:
1142 case CdMarkerBarItem:
1143 case TransportMarkerBarItem:
1145 case TimecodeRulerItem:
1146 case SamplesRulerItem:
1147 case MinsecRulerItem:
1149 /* button press on these items never does anything to
1150 change the editing mode.
1158 if (leave_internal_edit_mode) {
1159 ActionManager::do_action ("MouseMode", "toggle-internal-edit");
1163 button_selection (item, event, item_type);
1165 if (!_drags->active () &&
1166 (Keyboard::is_delete_event (&event->button) ||
1167 Keyboard::is_context_menu_event (&event->button) ||
1168 Keyboard::is_edit_event (&event->button))) {
1170 /* handled by button release */
1174 //not rolling, range mode click + join_play_range : locate the PH here
1175 if ( !_drags->active () && !_session->transport_rolling() && ( effective_mouse_mode() == MouseRange ) && Config->get_follow_edits() ) {
1176 framepos_t where = canvas_event_sample (event);
1178 _session->request_locate (where, false);
1181 switch (event->button.button) {
1183 return button_press_handler_1 (item, event, item_type);
1187 return button_press_handler_2 (item, event, item_type);
1194 return button_press_dispatch (&event->button);
1203 Editor::button_press_dispatch (GdkEventButton* ev)
1205 /* this function is intended only for buttons 4 and above.
1208 Gtkmm2ext::MouseButton b (ev->state, ev->button);
1209 return button_bindings->activate (b, Gtkmm2ext::Bindings::Press);
1213 Editor::button_release_dispatch (GdkEventButton* ev)
1215 /* this function is intended only for buttons 4 and above.
1218 Gtkmm2ext::MouseButton b (ev->state, ev->button);
1219 return button_bindings->activate (b, Gtkmm2ext::Bindings::Release);
1223 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1225 framepos_t where = canvas_event_sample (event);
1226 AutomationTimeAxisView* atv = 0;
1228 if (pre_press_cursor) {
1229 set_canvas_cursor (pre_press_cursor);
1230 pre_press_cursor = 0;
1233 /* no action if we're recording */
1235 if (_session && _session->actively_recording()) {
1239 /* see if we're finishing a drag */
1241 bool were_dragging = false;
1242 if (_drags->active ()) {
1243 bool const r = _drags->end_grab (event);
1245 /* grab dragged, so do nothing else */
1249 were_dragging = true;
1252 update_region_layering_order_editor ();
1254 /* edit events get handled here */
1256 if (!_drags->active () && Keyboard::is_edit_event (&event->button)) {
1257 switch (item_type) {
1259 show_region_properties ();
1262 case TempoMarkerItem: {
1264 TempoMarker* tempo_marker;
1266 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
1267 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
1268 abort(); /*NOTREACHED*/
1271 if ((tempo_marker = dynamic_cast<TempoMarker*> (marker)) == 0) {
1272 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
1273 abort(); /*NOTREACHED*/
1276 edit_tempo_marker (*tempo_marker);
1280 case MeterMarkerItem: {
1282 MeterMarker* meter_marker;
1284 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
1285 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
1286 abort(); /*NOTREACHED*/
1289 if ((meter_marker = dynamic_cast<MeterMarker*> (marker)) == 0) {
1290 fatal << _("programming error: marker for meter is not a meter marker!") << endmsg;
1291 abort(); /*NOTREACHED*/
1293 edit_meter_marker (*meter_marker);
1297 case RegionViewName:
1298 if (clicked_regionview->name_active()) {
1299 return mouse_rename_region (item, event);
1303 case ControlPointItem:
1304 edit_control_point (item);
1313 /* context menu events get handled here */
1314 if (Keyboard::is_context_menu_event (&event->button)) {
1316 context_click_event = *event;
1318 if (!_drags->active ()) {
1320 /* no matter which button pops up the context menu, tell the menu
1321 widget to use button 1 to drive menu selection.
1324 switch (item_type) {
1326 case FadeInHandleItem:
1327 case FadeInTrimHandleItem:
1328 case StartCrossFadeItem:
1329 popup_xfade_in_context_menu (1, event->button.time, item, item_type);
1333 case FadeOutHandleItem:
1334 case FadeOutTrimHandleItem:
1335 case EndCrossFadeItem:
1336 popup_xfade_out_context_menu (1, event->button.time, item, item_type);
1339 case LeftFrameHandle:
1340 case RightFrameHandle:
1344 popup_track_context_menu (1, event->button.time, item_type, false);
1348 case RegionViewNameHighlight:
1349 case RegionViewName:
1350 popup_track_context_menu (1, event->button.time, item_type, false);
1354 popup_track_context_menu (1, event->button.time, item_type, true);
1357 case AutomationTrackItem:
1358 popup_track_context_menu (1, event->button.time, item_type, false);
1362 case RangeMarkerBarItem:
1363 case TransportMarkerBarItem:
1364 case CdMarkerBarItem:
1368 case TimecodeRulerItem:
1369 case SamplesRulerItem:
1370 case MinsecRulerItem:
1372 popup_ruler_menu (where, item_type);
1376 marker_context_menu (&event->button, item);
1379 case TempoMarkerItem:
1380 tempo_or_meter_marker_context_menu (&event->button, item);
1383 case MeterMarkerItem:
1384 tempo_or_meter_marker_context_menu (&event->button, item);
1387 case CrossfadeViewItem:
1388 popup_track_context_menu (1, event->button.time, item_type, false);
1391 case ControlPointItem:
1392 popup_control_point_context_menu (item, event);
1403 /* delete events get handled here */
1405 Editing::MouseMode const eff = effective_mouse_mode ();
1407 if (!_drags->active () && Keyboard::is_delete_event (&event->button)) {
1409 switch (item_type) {
1410 case TempoMarkerItem:
1411 remove_tempo_marker (item);
1414 case MeterMarkerItem:
1415 remove_meter_marker (item);
1419 remove_marker (*item, event);
1423 if (eff == MouseObject) {
1424 remove_clicked_region ();
1428 case ControlPointItem:
1429 remove_control_point (item);
1433 remove_midi_note (item, event);
1442 switch (event->button.button) {
1445 switch (item_type) {
1446 /* see comments in button_press_handler */
1447 case PlayheadCursorItem:
1450 case AutomationLineItem:
1451 case StartSelectionTrimItem:
1452 case EndSelectionTrimItem:
1456 if (!_dragging_playhead) {
1457 snap_to_with_modifier (where, event, RoundNearest, true);
1458 mouse_add_new_marker (where);
1462 case CdMarkerBarItem:
1463 if (!_dragging_playhead) {
1464 // if we get here then a dragged range wasn't done
1465 snap_to_with_modifier (where, event, RoundNearest, true);
1466 mouse_add_new_marker (where, true);
1471 if (!_dragging_playhead) {
1472 snap_to_with_modifier (where, event);
1473 mouse_add_new_tempo_event (where);
1478 if (!_dragging_playhead) {
1479 mouse_add_new_meter_event (pixel_to_sample (event->button.x));
1484 case TimecodeRulerItem:
1485 case SamplesRulerItem:
1486 case MinsecRulerItem:
1497 switch (item_type) {
1500 /* check that we didn't drag before releasing, since
1501 its really annoying to create new control
1502 points when doing this.
1504 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (clicked_regionview);
1505 if (!were_dragging && arv) {
1506 bool with_guard_points = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
1507 arv->add_gain_point_event (item, event, with_guard_points);
1513 case AutomationTrackItem: {
1514 bool with_guard_points = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
1515 atv = dynamic_cast<AutomationTimeAxisView*>(clicked_axisview);
1517 atv->add_automation_event (event, where, event->button.y, with_guard_points);
1528 pop_canvas_cursor ();
1529 if (scrubbing_direction == 0) {
1530 /* no drag, just a click */
1531 switch (item_type) {
1533 play_selected_region ();
1539 /* make sure we stop */
1540 _session->request_transport_speed (0.0);
1549 /* do any (de)selection operations that should occur on button release */
1550 button_selection (item, event, item_type);
1559 switch (item_type) {
1561 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1563 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier|Keyboard::SecondaryModifier))) {
1566 // Button2 click is unused
1581 // x_style_paste (where, 1.0);
1602 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1609 /* by the time we reach here, entered_regionview and entered trackview
1610 * will have already been set as appropriate. Things are done this
1611 * way because this method isn't passed a pointer to a variable type of
1612 * thing that is entered (which may or may not be canvas item).
1613 * (e.g. the actual entered regionview)
1616 choose_canvas_cursor_on_entry (&event->crossing, item_type);
1618 switch (item_type) {
1619 case ControlPointItem:
1620 if (mouse_mode == MouseDraw || mouse_mode == MouseObject) {
1621 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1624 fraction = 1.0 - (cp->get_y() / cp->line().height());
1626 _verbose_cursor->set (cp->line().get_verbose_cursor_string (fraction));
1627 _verbose_cursor->show ();
1632 if (mouse_mode == MouseDraw) {
1633 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1635 line->set_outline_color (ARDOUR_UI::config()->get_EnteredGainLine());
1640 case AutomationLineItem:
1641 if (mouse_mode == MouseDraw || mouse_mode == MouseObject) {
1642 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1644 line->set_outline_color (ARDOUR_UI::config()->get_EnteredAutomationLine());
1649 case AutomationTrackItem:
1650 AutomationTimeAxisView* atv;
1651 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1652 clear_entered_track = false;
1653 set_entered_track (atv);
1658 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1661 entered_marker = marker;
1662 marker->set_color_rgba (ARDOUR_UI::config()->get_EnteredMarker());
1664 case MeterMarkerItem:
1665 case TempoMarkerItem:
1668 case FadeInHandleItem:
1669 case FadeInTrimHandleItem:
1670 if (mouse_mode == MouseObject && !internal_editing()) {
1671 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
1673 RegionView* rv = static_cast<RegionView*>(item->get_data ("regionview"));
1674 rect->set_fill_color (rv->get_fill_color());
1679 case FadeOutHandleItem:
1680 case FadeOutTrimHandleItem:
1681 if (mouse_mode == MouseObject && !internal_editing()) {
1682 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
1684 RegionView* rv = static_cast<RegionView*>(item->get_data ("regionview"));
1685 rect->set_fill_color (rv->get_fill_color ());
1690 case FeatureLineItem:
1692 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1693 line->set_outline_color (0xFF0000FF);
1704 /* third pass to handle entered track status in a comprehensible way.
1707 switch (item_type) {
1709 case AutomationLineItem:
1710 case ControlPointItem:
1711 /* these do not affect the current entered track state */
1712 clear_entered_track = false;
1715 case AutomationTrackItem:
1716 /* handled above already */
1728 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent*, ItemType item_type)
1736 reset_canvas_cursor ();
1738 switch (item_type) {
1739 case ControlPointItem:
1740 _verbose_cursor->hide ();
1744 case AutomationLineItem:
1745 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1747 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1749 line->set_outline_color (al->get_line_color());
1755 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1759 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
1760 location_flags_changed (loc);
1763 case MeterMarkerItem:
1764 case TempoMarkerItem:
1767 case FadeInTrimHandleItem:
1768 case FadeOutTrimHandleItem:
1769 case FadeInHandleItem:
1770 case FadeOutHandleItem:
1772 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
1774 rect->set_fill_color (ARDOUR_UI::config()->get_InactiveFadeHandle());
1779 case AutomationTrackItem:
1782 case FeatureLineItem:
1784 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1785 line->set_outline_color (ARDOUR_UI::config()->get_ZeroLine());
1797 Editor::scrub (framepos_t frame, double current_x)
1801 if (scrubbing_direction == 0) {
1803 _session->request_locate (frame, false);
1804 _session->request_transport_speed (0.1);
1805 scrubbing_direction = 1;
1809 if (last_scrub_x > current_x) {
1811 /* pointer moved to the left */
1813 if (scrubbing_direction > 0) {
1815 /* we reversed direction to go backwards */
1818 scrub_reverse_distance += (int) (last_scrub_x - current_x);
1822 /* still moving to the left (backwards) */
1824 scrub_reversals = 0;
1825 scrub_reverse_distance = 0;
1827 delta = 0.01 * (last_scrub_x - current_x);
1828 _session->request_transport_speed_nonzero (_session->transport_speed() - delta);
1832 /* pointer moved to the right */
1834 if (scrubbing_direction < 0) {
1835 /* we reversed direction to go forward */
1838 scrub_reverse_distance += (int) (current_x - last_scrub_x);
1841 /* still moving to the right */
1843 scrub_reversals = 0;
1844 scrub_reverse_distance = 0;
1846 delta = 0.01 * (current_x - last_scrub_x);
1847 _session->request_transport_speed_nonzero (_session->transport_speed() + delta);
1851 /* if there have been more than 2 opposite motion moves detected, or one that moves
1852 back more than 10 pixels, reverse direction
1855 if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
1857 if (scrubbing_direction > 0) {
1858 /* was forwards, go backwards */
1859 _session->request_transport_speed (-0.1);
1860 scrubbing_direction = -1;
1862 /* was backwards, go forwards */
1863 _session->request_transport_speed (0.1);
1864 scrubbing_direction = 1;
1867 scrub_reverse_distance = 0;
1868 scrub_reversals = 0;
1872 last_scrub_x = current_x;
1876 Editor::motion_handler (ArdourCanvas::Item* /*item*/, GdkEvent* event, bool from_autoscroll)
1878 _last_motion_y = event->motion.y;
1880 if (event->motion.is_hint) {
1883 /* We call this so that MOTION_NOTIFY events continue to be
1884 delivered to the canvas. We need to do this because we set
1885 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
1886 the density of the events, at the expense of a round-trip
1887 to the server. Given that this will mostly occur on cases
1888 where DISPLAY = :0.0, and given the cost of what the motion
1889 event might do, its a good tradeoff.
1892 _track_canvas->get_pointer (x, y);
1895 if (current_stepping_trackview) {
1896 /* don't keep the persistent stepped trackview if the mouse moves */
1897 current_stepping_trackview = 0;
1898 step_timeout.disconnect ();
1901 if (_session && _session->actively_recording()) {
1902 /* Sorry. no dragging stuff around while we record */
1906 update_join_object_range_location (event->motion.y);
1908 if (_drags->active ()) {
1909 return _drags->motion_handler (event, from_autoscroll);
1916 Editor::can_remove_control_point (ArdourCanvas::Item* item)
1918 ControlPoint* control_point;
1920 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
1921 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
1922 abort(); /*NOTREACHED*/
1925 AutomationLine& line = control_point->line ();
1926 if (dynamic_cast<AudioRegionGainLine*> (&line)) {
1927 /* we shouldn't remove the first or last gain point in region gain lines */
1928 if (line.is_last_point(*control_point) || line.is_first_point(*control_point)) {
1937 Editor::remove_control_point (ArdourCanvas::Item* item)
1939 if (!can_remove_control_point (item)) {
1943 ControlPoint* control_point;
1945 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
1946 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
1947 abort(); /*NOTREACHED*/
1950 control_point->line().remove_point (*control_point);
1954 Editor::edit_control_point (ArdourCanvas::Item* item)
1956 ControlPoint* p = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"));
1959 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
1960 abort(); /*NOTREACHED*/
1963 ControlPointDialog d (p);
1966 if (d.run () != RESPONSE_ACCEPT) {
1970 p->line().modify_point_y (*p, d.get_y_fraction ());
1974 Editor::edit_notes (TimeAxisViewItem& tavi)
1976 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(&tavi);
1982 MidiRegionView::Selection const & s = mrv->selection();
1988 EditNoteDialog* d = new EditNoteDialog (&(*s.begin())->region_view(), s);
1992 d->signal_response().connect (sigc::bind (sigc::mem_fun (*this, &Editor::note_edit_done), d));
1996 Editor::note_edit_done (int r, EditNoteDialog* d)
2003 Editor::visible_order_range (int* low, int* high) const
2005 *low = TimeAxisView::max_order ();
2008 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
2010 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
2012 if (!rtv->hidden()) {
2014 if (*high < rtv->order()) {
2015 *high = rtv->order ();
2018 if (*low > rtv->order()) {
2019 *low = rtv->order ();
2026 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
2028 /* Either add to or set the set the region selection, unless
2029 this is an alignment click (control used)
2032 if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
2033 TimeAxisView* tv = &rv.get_time_axis_view();
2034 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
2036 if (rtv && rtv->is_track()) {
2037 speed = rtv->track()->speed();
2040 framepos_t where = get_preferred_edit_position();
2044 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
2046 align_region (rv.region(), SyncPoint, (framepos_t) (where * speed));
2048 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
2050 align_region (rv.region(), End, (framepos_t) (where * speed));
2054 align_region (rv.region(), Start, (framepos_t) (where * speed));
2061 Editor::collect_new_region_view (RegionView* rv)
2063 latest_regionviews.push_back (rv);
2067 Editor::collect_and_select_new_region_view (RegionView* rv)
2070 latest_regionviews.push_back (rv);
2074 Editor::cancel_selection ()
2076 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2077 (*i)->hide_selection ();
2080 selection->clear ();
2081 clicked_selection = 0;
2085 Editor::cancel_time_selection ()
2087 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2088 (*i)->hide_selection ();
2090 selection->time.clear ();
2091 clicked_selection = 0;
2095 Editor::point_trim (GdkEvent* event, framepos_t new_bound)
2097 RegionView* rv = clicked_regionview;
2099 /* Choose action dependant on which button was pressed */
2100 switch (event->button.button) {
2102 begin_reversible_command (_("start point trim"));
2104 if (selection->selected (rv)) {
2105 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
2106 i != selection->regions.by_layer().end(); ++i)
2108 if (!(*i)->region()->locked()) {
2109 (*i)->region()->clear_changes ();
2110 (*i)->region()->trim_front (new_bound);
2111 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2116 if (!rv->region()->locked()) {
2117 rv->region()->clear_changes ();
2118 rv->region()->trim_front (new_bound);
2119 _session->add_command(new StatefulDiffCommand (rv->region()));
2123 commit_reversible_command();
2127 begin_reversible_command (_("End point trim"));
2129 if (selection->selected (rv)) {
2131 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
2133 if (!(*i)->region()->locked()) {
2134 (*i)->region()->clear_changes();
2135 (*i)->region()->trim_end (new_bound);
2136 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2142 if (!rv->region()->locked()) {
2143 rv->region()->clear_changes ();
2144 rv->region()->trim_end (new_bound);
2145 _session->add_command (new StatefulDiffCommand (rv->region()));
2149 commit_reversible_command();
2158 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* /*event*/)
2163 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2164 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2165 abort(); /*NOTREACHED*/
2168 Location* location = find_location_from_marker (marker, is_start);
2169 location->set_hidden (true, this);
2173 Editor::mouse_rename_region (ArdourCanvas::Item* /*item*/, GdkEvent* /*event*/)
2175 using namespace Gtkmm2ext;
2177 ArdourPrompter prompter (false);
2179 prompter.set_prompt (_("Name for region:"));
2180 prompter.set_initial_text (clicked_regionview->region()->name());
2181 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
2182 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
2183 prompter.show_all ();
2184 switch (prompter.run ()) {
2185 case Gtk::RESPONSE_ACCEPT:
2187 prompter.get_result(str);
2189 clicked_regionview->region()->set_name (str);
2198 Editor::mouse_brush_insert_region (RegionView* rv, framepos_t pos)
2200 /* no brushing without a useful snap setting */
2202 switch (_snap_mode) {
2204 return; /* can't work because it allows region to be placed anywhere */
2209 switch (_snap_type) {
2217 /* don't brush a copy over the original */
2219 if (pos == rv->region()->position()) {
2223 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
2225 if (rtv == 0 || !rtv->is_track()) {
2229 boost::shared_ptr<Playlist> playlist = rtv->playlist();
2230 double speed = rtv->track()->speed();
2232 playlist->clear_changes ();
2233 boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region(), true));
2234 playlist->add_region (new_region, (framepos_t) (pos * speed));
2235 _session->add_command (new StatefulDiffCommand (playlist));
2237 // playlist is frozen, so we have to update manually XXX this is disgusting
2239 playlist->RegionAdded (new_region); /* EMIT SIGNAL */
2243 Editor::track_height_step_timeout ()
2245 if (get_microseconds() - last_track_height_step_timestamp < 250000) {
2246 current_stepping_trackview = 0;
2253 Editor::add_region_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2255 assert (region_view);
2257 if (!region_view->region()->playlist()) {
2261 switch (Config->get_edit_mode()) {
2263 _drags->add (new RegionSpliceDrag (this, item, region_view, selection->regions.by_layer()));
2266 _drags->add (new RegionRippleDrag (this, item, region_view, selection->regions.by_layer()));
2269 _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), false, false));
2276 Editor::add_region_copy_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2278 assert (region_view);
2280 if (!region_view->region()->playlist()) {
2284 _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), false, true));
2288 Editor::add_region_brush_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2290 assert (region_view);
2292 if (!region_view->region()->playlist()) {
2296 if (Config->get_edit_mode() == Splice || Config->get_edit_mode() == Ripple) {
2300 _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), true, false));
2302 begin_reversible_command (Operations::drag_region_brush);
2305 /** Start a grab where a time range is selected, track(s) are selected, and the
2306 * user clicks and drags a region with a modifier in order to create a new region containing
2307 * the section of the clicked region that lies within the time range.
2310 Editor::start_selection_grab (ArdourCanvas::Item* /*item*/, GdkEvent* event)
2312 if (clicked_regionview == 0) {
2316 /* lets try to create new Region for the selection */
2318 vector<boost::shared_ptr<Region> > new_regions;
2319 create_region_from_selection (new_regions);
2321 if (new_regions.empty()) {
2325 /* XXX fix me one day to use all new regions */
2327 boost::shared_ptr<Region> region (new_regions.front());
2329 /* add it to the current stream/playlist.
2331 tricky: the streamview for the track will add a new regionview. we will
2332 catch the signal it sends when it creates the regionview to
2333 set the regionview we want to then drag.
2336 latest_regionviews.clear();
2337 sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
2339 /* A selection grab currently creates two undo/redo operations, one for
2340 creating the new region and another for moving it.
2343 begin_reversible_command (Operations::selection_grab);
2345 boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
2347 playlist->clear_changes ();
2348 clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
2349 _session->add_command(new StatefulDiffCommand (playlist));
2351 commit_reversible_command ();
2355 if (latest_regionviews.empty()) {
2356 /* something went wrong */
2360 /* we need to deselect all other regionviews, and select this one
2361 i'm ignoring undo stuff, because the region creation will take care of it
2363 selection->set (latest_regionviews);
2365 _drags->set (new RegionMoveDrag (this, latest_regionviews.front()->get_canvas_group(), latest_regionviews.front(), latest_regionviews, false, false), event);
2371 if (_drags->active ()) {
2374 selection->clear ();
2381 Editor::set_internal_edit (bool yn)
2383 if (_internal_editing == yn) {
2387 _internal_editing = yn;
2390 pre_internal_mouse_mode = mouse_mode;
2391 pre_internal_snap_type = _snap_type;
2392 pre_internal_snap_mode = _snap_mode;
2394 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2395 (*i)->enter_internal_edit_mode ();
2398 set_snap_to (internal_snap_type);
2399 set_snap_mode (internal_snap_mode);
2403 internal_snap_mode = _snap_mode;
2404 internal_snap_type = _snap_type;
2406 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2407 (*i)->leave_internal_edit_mode ();
2410 set_snap_to (pre_internal_snap_type);
2411 set_snap_mode (pre_internal_snap_mode);
2414 reset_canvas_cursor ();
2415 MouseModeChanged ();
2418 /** Update _join_object_range_state which indicate whether we are over the top
2419 * or bottom half of a route view, used by the `join object/range' tool
2420 * mode. Coordinates in canvas space.
2423 Editor::update_join_object_range_location (double y)
2425 if (_internal_editing || !get_smart_mode()) {
2426 _join_object_range_state = JOIN_OBJECT_RANGE_NONE;
2430 JoinObjectRangeState const old = _join_object_range_state;
2432 if (mouse_mode == MouseObject) {
2433 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2434 } else if (mouse_mode == MouseRange) {
2435 _join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
2438 if (entered_regionview) {
2440 ArdourCanvas::Duple const item_space = entered_regionview->get_canvas_group()->canvas_to_item (ArdourCanvas::Duple (0, y));
2441 double const c = item_space.y / entered_regionview->height();
2443 _join_object_range_state = c <= 0.5 ? JOIN_OBJECT_RANGE_RANGE : JOIN_OBJECT_RANGE_OBJECT;
2445 if (_join_object_range_state != old) {
2446 set_canvas_cursor (which_track_cursor ());
2449 } else if (entered_track) {
2451 RouteTimeAxisView* entered_route_view = dynamic_cast<RouteTimeAxisView*> (entered_track);
2453 if (entered_route_view) {
2458 entered_route_view->canvas_display()->canvas_to_item (cx, cy);
2460 double track_height = entered_route_view->view()->child_height();
2461 if (Config->get_show_name_highlight()) {
2462 track_height -= TimeAxisViewItem::NAME_HIGHLIGHT_SIZE;
2464 double const c = cy / track_height;
2468 _join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
2470 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2474 /* Other kinds of tracks use object mode */
2475 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2478 if (_join_object_range_state != old) {
2479 set_canvas_cursor (which_track_cursor ());
2485 Editor::effective_mouse_mode () const
2487 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
2489 } else if (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE) {
2497 Editor::remove_midi_note (ArdourCanvas::Item* item, GdkEvent *)
2499 NoteBase* e = reinterpret_cast<NoteBase*> (item->get_data ("notebase"));
2502 e->region_view().delete_note (e->note ());
2505 /** Obtain the pointer position in canvas coordinates */
2507 Editor::get_pointer_position (double& x, double& y) const
2510 _track_canvas->get_pointer (px, py);
2511 _track_canvas->window_to_canvas (px, py, x, y);