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
330 act = ActionManager::get_action (X_("MouseMode"), X_("toggle-internal-edit"));
331 tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
332 tact->set_active (true);
338 if (_session && mouse_mode == MouseAudition) {
339 /* stop transport and reset default speed to avoid oddness with
341 _session->request_transport_speed (0.0, true);
348 /* this should generate a new enter event which will
349 trigger the appropiate cursor.
353 _track_canvas->re_enter ();
356 set_gain_envelope_visibility ();
358 update_time_selection_display ();
360 MouseModeChanged (); /* EMIT SIGNAL */
364 Editor::update_time_selection_display ()
366 if (smart_mode_action->get_active()) {
367 /* not sure what to do here */
368 if (mouse_mode == MouseObject) {
372 switch (mouse_mode) {
374 selection->clear_objects ();
377 selection->clear_time ();
384 Editor::step_mouse_mode (bool next)
386 const int n_mouse_modes = (int)MouseDraw + 1;
387 int current = (int)current_mouse_mode();
389 set_mouse_mode((MouseMode)((current + 1) % n_mouse_modes));
391 set_mouse_mode((MouseMode)((current + n_mouse_modes - 1) % n_mouse_modes));
396 Editor::toggle_internal_editing_from_double_click (GdkEvent* event)
398 if (_drags->active()) {
399 _drags->end_grab (event);
402 ActionManager::do_action ("MouseMode", "toggle-internal-edit");
404 /* prevent reversion of edit cursor on button release */
406 pre_press_cursor = 0;
412 Editor::button_selection (ArdourCanvas::Item* /*item*/, GdkEvent* event, ItemType item_type)
414 /* in object/audition/timefx/gain-automation mode,
415 any button press sets the selection if the object
416 can be selected. this is a bit of hack, because
417 we want to avoid this if the mouse operation is a
420 note: not dbl-click or triple-click
422 Also note that there is no region selection in internal edit mode, otherwise
423 for operations operating on the selection (e.g. cut) it is not obvious whether
424 to cut notes or regions.
427 MouseMode eff_mouse_mode = effective_mouse_mode ();
429 if (eff_mouse_mode == MouseCut) {
430 /* never change selection in cut mode */
434 if (get_smart_mode() && eff_mouse_mode == MouseRange && event->button.button == 3 && item_type == RegionItem) {
435 /* context clicks are always about object properties, even if
436 we're in range mode within smart mode.
438 eff_mouse_mode = MouseObject;
441 /* special case: allow drag of region fade in/out in object mode with join object/range enabled */
442 if (get_smart_mode()) {
444 case FadeInHandleItem:
445 case FadeInTrimHandleItem:
446 case FadeOutHandleItem:
447 case FadeOutTrimHandleItem:
448 eff_mouse_mode = MouseObject;
455 if (((mouse_mode != MouseObject) &&
456 (mouse_mode != MouseAudition || item_type != RegionItem) &&
457 (mouse_mode != MouseTimeFX || item_type != RegionItem) &&
458 (mouse_mode != MouseDraw)) ||
459 ((event->type != GDK_BUTTON_PRESS && event->type != GDK_BUTTON_RELEASE) || event->button.button > 3) ||
460 (internal_editing() && mouse_mode != MouseTimeFX)) {
465 if (event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) {
467 if ((event->button.state & Keyboard::RelevantModifierKeyMask) && event->button.button != 1) {
469 /* almost no selection action on modified button-2 or button-3 events */
471 if (item_type != RegionItem && event->button.button != 2) {
477 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
478 bool press = (event->type == GDK_BUTTON_PRESS);
483 if (eff_mouse_mode != MouseRange) {
484 set_selected_regionview_from_click (press, op);
486 /* don't change the selection unless the
487 clicked track is not currently selected. if
488 so, "collapse" the selection to just this
491 if (!selection->selected (clicked_axisview)) {
492 set_selected_track_as_side_effect (Selection::Set);
496 if (eff_mouse_mode != MouseRange) {
497 set_selected_regionview_from_click (press, op);
502 case RegionViewNameHighlight:
504 case LeftFrameHandle:
505 case RightFrameHandle:
506 case FadeInHandleItem:
507 case FadeInTrimHandleItem:
509 case FadeOutHandleItem:
510 case FadeOutTrimHandleItem:
512 case StartCrossFadeItem:
513 case EndCrossFadeItem:
514 if (get_smart_mode() || eff_mouse_mode != MouseRange) {
515 set_selected_regionview_from_click (press, op);
516 } else if (event->type == GDK_BUTTON_PRESS) {
517 set_selected_track_as_side_effect (op);
521 case ControlPointItem:
522 set_selected_track_as_side_effect (op);
523 if (eff_mouse_mode != MouseRange) {
524 set_selected_control_point_from_click (press, op);
529 /* for context click, select track */
530 if (event->button.button == 3) {
531 selection->clear_tracks ();
532 set_selected_track_as_side_effect (op);
536 case AutomationTrackItem:
537 set_selected_track_as_side_effect (op);
546 Editor::button_press_handler_1 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
548 /* single mouse clicks on any of these item types operate
549 independent of mouse mode, mostly because they are
550 not on the main track canvas or because we want
555 case PlayheadCursorItem:
556 _drags->set (new CursorDrag (this, *playhead_cursor, true), event);
560 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
561 hide_marker (item, event);
563 _drags->set (new MarkerDrag (this, item), event);
567 case TempoMarkerItem:
569 TempoMarker* m = reinterpret_cast<TempoMarker*> (item->get_data ("marker"));
572 new TempoMarkerDrag (
575 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
582 case MeterMarkerItem:
584 MeterMarker* m = reinterpret_cast<MeterMarker*> (item->get_data ("marker"));
587 new MeterMarkerDrag (
590 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
598 _drags->set (new VideoTimeLineDrag (this, item), event);
605 case TimecodeRulerItem:
606 case SamplesRulerItem:
607 case MinsecRulerItem:
609 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
610 _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
616 case RangeMarkerBarItem:
617 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier)) {
618 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateSkipMarker), event);
619 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
620 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateRangeMarker), event);
622 _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
627 case CdMarkerBarItem:
628 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
629 _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
631 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateCDMarker), event);
636 case TransportMarkerBarItem:
637 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
638 _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
640 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateTransportMarker), event);
649 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
650 /* special case: allow trim of range selections in joined object mode;
651 in theory eff should equal MouseRange in this case, but it doesn't
652 because entering the range selection canvas item results in entered_regionview
653 being set to 0, so update_join_object_range_location acts as if we aren't
656 if (item_type == StartSelectionTrimItem) {
657 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
658 } else if (item_type == EndSelectionTrimItem) {
659 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
663 Editing::MouseMode eff = effective_mouse_mode ();
665 /* special case: allow drag of region fade in/out in object mode with join object/range enabled */
666 if (get_smart_mode()) {
668 case FadeInHandleItem:
669 case FadeInTrimHandleItem:
670 case FadeOutHandleItem:
671 case FadeOutTrimHandleItem:
679 /* there is no Range mode when in internal edit mode */
680 if (eff == MouseRange && internal_editing()) {
687 case StartSelectionTrimItem:
688 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
691 case EndSelectionTrimItem:
692 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
696 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
697 start_selection_grab (item, event);
699 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
700 /* grab selection for moving */
701 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionMove), event);
703 /* this was debated, but decided the more common action was to
704 make a new selection */
705 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
710 if (internal_editing()) {
711 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
712 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
716 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::RangeSelectModifier)) {
717 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionExtend), event);
719 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
725 case RegionViewNameHighlight:
726 if (!clicked_regionview->region()->locked()) {
727 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
733 if (!internal_editing()) {
734 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::RangeSelectModifier)) {
735 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionExtend), event);
737 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
747 case FadeInHandleItem:
748 case FadeOutHandleItem:
749 case LeftFrameHandle:
750 case RightFrameHandle:
751 case FeatureLineItem:
752 case RegionViewNameHighlight:
755 case AutomationTrackItem:
756 _drags->set (new RegionCutDrag (this, item, canvas_event_sample (event)), event, current_canvas_cursor);
767 /* Existing note: allow trimming/motion */
768 if (internal_editing()) {
769 NoteBase* cn = reinterpret_cast<NoteBase*> (item->get_data ("notebase"));
771 if (cn->big_enough_to_trim() && cn->mouse_near_ends()) {
772 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
774 _drags->set (new NoteDrag (this, item), event);
784 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)) &&
785 event->type == GDK_BUTTON_PRESS) {
787 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
789 } else if (event->type == GDK_BUTTON_PRESS) {
792 case FadeInHandleItem:
794 _drags->set (new FadeInDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), selection->regions), event, _cursors->fade_in);
798 case FadeOutHandleItem:
800 _drags->set (new FadeOutDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), selection->regions), event, _cursors->fade_out);
804 case StartCrossFadeItem:
805 case EndCrossFadeItem:
806 /* we might allow user to grab inside the fade to trim a region with preserve_fade_anchor. for not this is not fully implemented */
807 // if (!clicked_regionview->region()->locked()) {
808 // _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer(), true), event);
813 case FeatureLineItem:
815 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier)) {
816 remove_transient(item);
820 _drags->set (new FeatureLineDrag (this, item), event);
826 if (dynamic_cast<AutomationRegionView*> (clicked_regionview)) {
827 /* click on an automation region view; do nothing here and let the ARV's signal handler
833 if (internal_editing ()) {
837 /* click on a normal region view */
838 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
839 add_region_copy_drag (item, event, clicked_regionview);
840 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
841 add_region_brush_drag (item, event, clicked_regionview);
843 add_region_drag (item, event, clicked_regionview);
847 // if (!internal_editing() && (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE && !selection->regions.empty())) {
848 // _drags->add (new SelectionDrag (this, clicked_axisview->get_selection_rect (clicked_selection)->rect, SelectionDrag::SelectionMove));
851 _drags->start_grab (event);
855 case RegionViewNameHighlight:
856 case LeftFrameHandle:
857 case RightFrameHandle:
858 if (!clicked_regionview->region()->locked()) {
859 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
864 case FadeInTrimHandleItem:
865 case FadeOutTrimHandleItem:
866 if (!clicked_regionview->region()->locked()) {
867 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer(), true), event);
874 /* rename happens on edit clicks */
875 if (clicked_regionview->get_name_highlight()) {
876 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer()), event);
882 case ControlPointItem:
883 _drags->set (new ControlPointDrag (this, item), event);
887 case AutomationLineItem:
888 _drags->set (new LineDrag (this, item), event);
893 if (internal_editing()) {
894 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
895 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
899 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
903 case AutomationTrackItem:
905 TimeAxisView* parent = clicked_axisview->get_parent ();
906 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (clicked_axisview);
908 if (parent && dynamic_cast<MidiTimeAxisView*> (parent) && atv->show_regions ()) {
910 RouteTimeAxisView* p = dynamic_cast<RouteTimeAxisView*> (parent);
912 boost::shared_ptr<Playlist> pl = p->track()->playlist ();
913 if (pl->n_regions() == 0) {
914 /* Parent has no regions; create one so that we have somewhere to put automation */
915 _drags->set (new RegionCreateDrag (this, item, parent), event);
917 /* See if there's a region before the click that we can extend, and extend it if so */
918 framepos_t const t = canvas_event_sample (event);
919 boost::shared_ptr<Region> prev = pl->find_next_region (t, End, -1);
921 _drags->set (new RegionCreateDrag (this, item, parent), event);
923 prev->set_length (t - prev->position ());
927 /* rubberband drag to select automation points */
928 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
952 _drags->set (new LineDrag (this, item), event);
955 case ControlPointItem:
956 _drags->set (new ControlPointDrag (this, item), event);
962 AudioRegionView* arv = dynamic_cast<AudioRegionView *> (clicked_regionview);
964 _drags->set (new AutomationRangeDrag (this, arv, selection->time), event, _cursors->up_down);
966 double const y = event->button.y;
967 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
969 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
971 /* smart "join" mode: drag automation */
972 _drags->set (new AutomationRangeDrag (this, atv, selection->time), event, _cursors->up_down);
980 case AutomationLineItem:
981 _drags->set (new LineDrag (this, item), event);
985 /* Existing note: allow trimming/motion */
986 if (internal_editing()) {
987 /* trim notes if we're in internal edit mode and near the ends of the note */
988 NoteBase* cn = reinterpret_cast<NoteBase*>(item->get_data ("notebase"));
990 if (cn->big_enough_to_trim() && cn->mouse_near_ends()) {
991 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
993 _drags->set (new NoteDrag (this, item), event);
1000 if (internal_editing()) {
1001 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
1002 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
1015 if (internal_editing() && item_type == NoteItem ) {
1016 /* drag notes if we're in internal edit mode */
1017 NoteBase* cn = reinterpret_cast<NoteBase*>(item->get_data ("notebase"));
1019 if (cn->big_enough_to_trim()) {
1020 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
1023 } else if (clicked_regionview) {
1025 _drags->set (new TimeFXDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1031 _drags->set (new ScrubDrag (this, item), event);
1032 scrub_reversals = 0;
1033 scrub_reverse_distance = 0;
1034 last_scrub_x = event->button.x;
1035 scrubbing_direction = 0;
1036 push_canvas_cursor (_cursors->transparent);
1048 Editor::button_press_handler_2 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1050 Editing::MouseMode const eff = effective_mouse_mode ();
1053 switch (item_type) {
1055 if (internal_editing ()) {
1056 /* no region drags in internal edit mode */
1060 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
1061 add_region_copy_drag (item, event, clicked_regionview);
1063 add_region_drag (item, event, clicked_regionview);
1065 _drags->start_grab (event);
1068 case ControlPointItem:
1069 _drags->set (new ControlPointDrag (this, item), event);
1077 switch (item_type) {
1078 case RegionViewNameHighlight:
1079 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1083 case LeftFrameHandle:
1084 case RightFrameHandle:
1085 if (!internal_editing ()) {
1086 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1091 case RegionViewName:
1092 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer()), event);
1106 /* relax till release */
1118 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1120 if (event->type == GDK_2BUTTON_PRESS) {
1121 _drags->mark_double_click ();
1122 gdk_pointer_ungrab (GDK_CURRENT_TIME);
1126 if (event->type != GDK_BUTTON_PRESS) {
1130 pre_press_cursor = current_canvas_cursor;
1132 _track_canvas->grab_focus();
1134 if (_session && _session->actively_recording()) {
1138 if (internal_editing()) {
1139 bool leave_internal_edit_mode = false;
1141 switch (item_type) {
1146 if (!dynamic_cast<MidiRegionView*> (clicked_regionview) && !dynamic_cast<AutomationRegionView*> (clicked_regionview)) {
1147 leave_internal_edit_mode = true;
1151 case PlayheadCursorItem:
1153 case TempoMarkerItem:
1154 case MeterMarkerItem:
1158 case RangeMarkerBarItem:
1159 case CdMarkerBarItem:
1160 case TransportMarkerBarItem:
1162 case TimecodeRulerItem:
1163 case SamplesRulerItem:
1164 case MinsecRulerItem:
1166 /* button press on these items never does anything to
1167 change the editing mode.
1175 if (leave_internal_edit_mode) {
1176 ActionManager::do_action ("MouseMode", "toggle-internal-edit");
1180 button_selection (item, event, item_type);
1182 if (!_drags->active () &&
1183 (Keyboard::is_delete_event (&event->button) ||
1184 Keyboard::is_context_menu_event (&event->button) ||
1185 Keyboard::is_edit_event (&event->button))) {
1187 /* handled by button release */
1191 //not rolling, range mode click + join_play_range : locate the PH here
1192 if ( !_drags->active () && !_session->transport_rolling() && ( effective_mouse_mode() == MouseRange ) && Config->get_follow_edits() ) {
1193 framepos_t where = canvas_event_sample (event);
1195 _session->request_locate (where, false);
1198 switch (event->button.button) {
1200 return button_press_handler_1 (item, event, item_type);
1204 return button_press_handler_2 (item, event, item_type);
1211 return button_press_dispatch (&event->button);
1220 Editor::button_press_dispatch (GdkEventButton* ev)
1222 /* this function is intended only for buttons 4 and above.
1225 Gtkmm2ext::MouseButton b (ev->state, ev->button);
1226 return button_bindings->activate (b, Gtkmm2ext::Bindings::Press);
1230 Editor::button_release_dispatch (GdkEventButton* ev)
1232 /* this function is intended only for buttons 4 and above.
1235 Gtkmm2ext::MouseButton b (ev->state, ev->button);
1236 return button_bindings->activate (b, Gtkmm2ext::Bindings::Release);
1240 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1242 framepos_t where = canvas_event_sample (event);
1243 AutomationTimeAxisView* atv = 0;
1245 if (pre_press_cursor) {
1246 set_canvas_cursor (pre_press_cursor);
1247 pre_press_cursor = 0;
1250 /* no action if we're recording */
1252 if (_session && _session->actively_recording()) {
1256 /* see if we're finishing a drag */
1258 bool were_dragging = false;
1259 if (_drags->active ()) {
1260 bool const r = _drags->end_grab (event);
1262 /* grab dragged, so do nothing else */
1266 were_dragging = true;
1269 update_region_layering_order_editor ();
1271 /* edit events get handled here */
1273 if (!_drags->active () && Keyboard::is_edit_event (&event->button)) {
1274 switch (item_type) {
1276 show_region_properties ();
1279 case TempoMarkerItem: {
1281 TempoMarker* tempo_marker;
1283 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
1284 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
1285 abort(); /*NOTREACHED*/
1288 if ((tempo_marker = dynamic_cast<TempoMarker*> (marker)) == 0) {
1289 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
1290 abort(); /*NOTREACHED*/
1293 edit_tempo_marker (*tempo_marker);
1297 case MeterMarkerItem: {
1299 MeterMarker* meter_marker;
1301 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
1302 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
1303 abort(); /*NOTREACHED*/
1306 if ((meter_marker = dynamic_cast<MeterMarker*> (marker)) == 0) {
1307 fatal << _("programming error: marker for meter is not a meter marker!") << endmsg;
1308 abort(); /*NOTREACHED*/
1310 edit_meter_marker (*meter_marker);
1314 case RegionViewName:
1315 if (clicked_regionview->name_active()) {
1316 return mouse_rename_region (item, event);
1320 case ControlPointItem:
1321 edit_control_point (item);
1330 /* context menu events get handled here */
1331 if (Keyboard::is_context_menu_event (&event->button)) {
1333 context_click_event = *event;
1335 if (!_drags->active ()) {
1337 /* no matter which button pops up the context menu, tell the menu
1338 widget to use button 1 to drive menu selection.
1341 switch (item_type) {
1343 case FadeInHandleItem:
1344 case FadeInTrimHandleItem:
1345 case StartCrossFadeItem:
1346 popup_xfade_in_context_menu (1, event->button.time, item, item_type);
1350 case FadeOutHandleItem:
1351 case FadeOutTrimHandleItem:
1352 case EndCrossFadeItem:
1353 popup_xfade_out_context_menu (1, event->button.time, item, item_type);
1356 case LeftFrameHandle:
1357 case RightFrameHandle:
1361 popup_track_context_menu (1, event->button.time, item_type, false);
1365 case RegionViewNameHighlight:
1366 case RegionViewName:
1367 popup_track_context_menu (1, event->button.time, item_type, false);
1371 popup_track_context_menu (1, event->button.time, item_type, true);
1374 case AutomationTrackItem:
1375 popup_track_context_menu (1, event->button.time, item_type, false);
1379 case RangeMarkerBarItem:
1380 case TransportMarkerBarItem:
1381 case CdMarkerBarItem:
1385 case TimecodeRulerItem:
1386 case SamplesRulerItem:
1387 case MinsecRulerItem:
1389 popup_ruler_menu (where, item_type);
1393 marker_context_menu (&event->button, item);
1396 case TempoMarkerItem:
1397 tempo_or_meter_marker_context_menu (&event->button, item);
1400 case MeterMarkerItem:
1401 tempo_or_meter_marker_context_menu (&event->button, item);
1404 case CrossfadeViewItem:
1405 popup_track_context_menu (1, event->button.time, item_type, false);
1408 case ControlPointItem:
1409 popup_control_point_context_menu (item, event);
1420 /* delete events get handled here */
1422 Editing::MouseMode const eff = effective_mouse_mode ();
1424 if (!_drags->active () && Keyboard::is_delete_event (&event->button)) {
1426 switch (item_type) {
1427 case TempoMarkerItem:
1428 remove_tempo_marker (item);
1431 case MeterMarkerItem:
1432 remove_meter_marker (item);
1436 remove_marker (*item, event);
1440 if (eff == MouseObject) {
1441 remove_clicked_region ();
1445 case ControlPointItem:
1446 remove_control_point (item);
1450 remove_midi_note (item, event);
1459 switch (event->button.button) {
1462 switch (item_type) {
1463 /* see comments in button_press_handler */
1464 case PlayheadCursorItem:
1467 case AutomationLineItem:
1468 case StartSelectionTrimItem:
1469 case EndSelectionTrimItem:
1473 if (!_dragging_playhead) {
1474 snap_to_with_modifier (where, event, RoundNearest, true);
1475 mouse_add_new_marker (where);
1479 case CdMarkerBarItem:
1480 if (!_dragging_playhead) {
1481 // if we get here then a dragged range wasn't done
1482 snap_to_with_modifier (where, event, RoundNearest, true);
1483 mouse_add_new_marker (where, true);
1488 if (!_dragging_playhead) {
1489 snap_to_with_modifier (where, event);
1490 mouse_add_new_tempo_event (where);
1495 if (!_dragging_playhead) {
1496 mouse_add_new_meter_event (pixel_to_sample (event->button.x));
1501 case TimecodeRulerItem:
1502 case SamplesRulerItem:
1503 case MinsecRulerItem:
1514 switch (item_type) {
1517 /* check that we didn't drag before releasing, since
1518 its really annoying to create new control
1519 points when doing this.
1521 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (clicked_regionview);
1522 if (!were_dragging && arv) {
1523 bool with_guard_points = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
1524 arv->add_gain_point_event (item, event, with_guard_points);
1530 case AutomationTrackItem: {
1531 bool with_guard_points = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
1532 atv = dynamic_cast<AutomationTimeAxisView*>(clicked_axisview);
1534 atv->add_automation_event (event, where, event->button.y, with_guard_points);
1545 pop_canvas_cursor ();
1546 if (scrubbing_direction == 0) {
1547 /* no drag, just a click */
1548 switch (item_type) {
1550 play_selected_region ();
1556 /* make sure we stop */
1557 _session->request_transport_speed (0.0);
1566 /* do any (de)selection operations that should occur on button release */
1567 button_selection (item, event, item_type);
1576 switch (item_type) {
1578 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1580 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier|Keyboard::SecondaryModifier))) {
1583 // Button2 click is unused
1598 // x_style_paste (where, 1.0);
1619 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1626 /* by the time we reach here, entered_regionview and entered trackview
1627 * will have already been set as appropriate. Things are done this
1628 * way because this method isn't passed a pointer to a variable type of
1629 * thing that is entered (which may or may not be canvas item).
1630 * (e.g. the actual entered regionview)
1633 choose_canvas_cursor_on_entry (&event->crossing, item_type);
1635 switch (item_type) {
1636 case ControlPointItem:
1637 if (mouse_mode == MouseDraw || mouse_mode == MouseObject) {
1638 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1641 fraction = 1.0 - (cp->get_y() / cp->line().height());
1643 _verbose_cursor->set (cp->line().get_verbose_cursor_string (fraction));
1644 _verbose_cursor->show ();
1649 if (mouse_mode == MouseDraw) {
1650 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1652 line->set_outline_color (ARDOUR_UI::config()->get_EnteredGainLine());
1657 case AutomationLineItem:
1658 if (mouse_mode == MouseDraw || mouse_mode == MouseObject) {
1659 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1661 line->set_outline_color (ARDOUR_UI::config()->get_EnteredAutomationLine());
1666 case AutomationTrackItem:
1667 AutomationTimeAxisView* atv;
1668 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1669 clear_entered_track = false;
1670 set_entered_track (atv);
1675 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1678 entered_marker = marker;
1679 marker->set_color_rgba (ARDOUR_UI::config()->get_EnteredMarker());
1681 case MeterMarkerItem:
1682 case TempoMarkerItem:
1685 case FadeInHandleItem:
1686 case FadeInTrimHandleItem:
1687 if (mouse_mode == MouseObject && !internal_editing()) {
1688 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
1690 RegionView* rv = static_cast<RegionView*>(item->get_data ("regionview"));
1691 rect->set_fill_color (rv->get_fill_color());
1696 case FadeOutHandleItem:
1697 case FadeOutTrimHandleItem:
1698 if (mouse_mode == MouseObject && !internal_editing()) {
1699 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
1701 RegionView* rv = static_cast<RegionView*>(item->get_data ("regionview"));
1702 rect->set_fill_color (rv->get_fill_color ());
1707 case FeatureLineItem:
1709 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1710 line->set_outline_color (0xFF0000FF);
1721 /* third pass to handle entered track status in a comprehensible way.
1724 switch (item_type) {
1726 case AutomationLineItem:
1727 case ControlPointItem:
1728 /* these do not affect the current entered track state */
1729 clear_entered_track = false;
1732 case AutomationTrackItem:
1733 /* handled above already */
1745 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent*, ItemType item_type)
1753 reset_canvas_cursor ();
1755 switch (item_type) {
1756 case ControlPointItem:
1757 _verbose_cursor->hide ();
1761 case AutomationLineItem:
1762 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1764 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1766 line->set_outline_color (al->get_line_color());
1772 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1776 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
1777 location_flags_changed (loc);
1780 case MeterMarkerItem:
1781 case TempoMarkerItem:
1784 case FadeInTrimHandleItem:
1785 case FadeOutTrimHandleItem:
1786 case FadeInHandleItem:
1787 case FadeOutHandleItem:
1789 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
1791 rect->set_fill_color (ARDOUR_UI::config()->get_InactiveFadeHandle());
1796 case AutomationTrackItem:
1799 case FeatureLineItem:
1801 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1802 line->set_outline_color (ARDOUR_UI::config()->get_ZeroLine());
1814 Editor::scrub (framepos_t frame, double current_x)
1818 if (scrubbing_direction == 0) {
1820 _session->request_locate (frame, false);
1821 _session->request_transport_speed (0.1);
1822 scrubbing_direction = 1;
1826 if (last_scrub_x > current_x) {
1828 /* pointer moved to the left */
1830 if (scrubbing_direction > 0) {
1832 /* we reversed direction to go backwards */
1835 scrub_reverse_distance += (int) (last_scrub_x - current_x);
1839 /* still moving to the left (backwards) */
1841 scrub_reversals = 0;
1842 scrub_reverse_distance = 0;
1844 delta = 0.01 * (last_scrub_x - current_x);
1845 _session->request_transport_speed_nonzero (_session->transport_speed() - delta);
1849 /* pointer moved to the right */
1851 if (scrubbing_direction < 0) {
1852 /* we reversed direction to go forward */
1855 scrub_reverse_distance += (int) (current_x - last_scrub_x);
1858 /* still moving to the right */
1860 scrub_reversals = 0;
1861 scrub_reverse_distance = 0;
1863 delta = 0.01 * (current_x - last_scrub_x);
1864 _session->request_transport_speed_nonzero (_session->transport_speed() + delta);
1868 /* if there have been more than 2 opposite motion moves detected, or one that moves
1869 back more than 10 pixels, reverse direction
1872 if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
1874 if (scrubbing_direction > 0) {
1875 /* was forwards, go backwards */
1876 _session->request_transport_speed (-0.1);
1877 scrubbing_direction = -1;
1879 /* was backwards, go forwards */
1880 _session->request_transport_speed (0.1);
1881 scrubbing_direction = 1;
1884 scrub_reverse_distance = 0;
1885 scrub_reversals = 0;
1889 last_scrub_x = current_x;
1893 Editor::motion_handler (ArdourCanvas::Item* /*item*/, GdkEvent* event, bool from_autoscroll)
1895 _last_motion_y = event->motion.y;
1897 if (event->motion.is_hint) {
1900 /* We call this so that MOTION_NOTIFY events continue to be
1901 delivered to the canvas. We need to do this because we set
1902 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
1903 the density of the events, at the expense of a round-trip
1904 to the server. Given that this will mostly occur on cases
1905 where DISPLAY = :0.0, and given the cost of what the motion
1906 event might do, its a good tradeoff.
1909 _track_canvas->get_pointer (x, y);
1912 if (current_stepping_trackview) {
1913 /* don't keep the persistent stepped trackview if the mouse moves */
1914 current_stepping_trackview = 0;
1915 step_timeout.disconnect ();
1918 if (_session && _session->actively_recording()) {
1919 /* Sorry. no dragging stuff around while we record */
1923 update_join_object_range_location (event->motion.y);
1925 if (_drags->active ()) {
1926 return _drags->motion_handler (event, from_autoscroll);
1933 Editor::can_remove_control_point (ArdourCanvas::Item* item)
1935 ControlPoint* control_point;
1937 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
1938 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
1939 abort(); /*NOTREACHED*/
1942 AutomationLine& line = control_point->line ();
1943 if (dynamic_cast<AudioRegionGainLine*> (&line)) {
1944 /* we shouldn't remove the first or last gain point in region gain lines */
1945 if (line.is_last_point(*control_point) || line.is_first_point(*control_point)) {
1954 Editor::remove_control_point (ArdourCanvas::Item* item)
1956 if (!can_remove_control_point (item)) {
1960 ControlPoint* control_point;
1962 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
1963 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
1964 abort(); /*NOTREACHED*/
1967 control_point->line().remove_point (*control_point);
1971 Editor::edit_control_point (ArdourCanvas::Item* item)
1973 ControlPoint* p = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"));
1976 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
1977 abort(); /*NOTREACHED*/
1980 ControlPointDialog d (p);
1983 if (d.run () != RESPONSE_ACCEPT) {
1987 p->line().modify_point_y (*p, d.get_y_fraction ());
1991 Editor::edit_notes (TimeAxisViewItem& tavi)
1993 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(&tavi);
1999 MidiRegionView::Selection const & s = mrv->selection();
2005 EditNoteDialog* d = new EditNoteDialog (&(*s.begin())->region_view(), s);
2009 d->signal_response().connect (sigc::bind (sigc::mem_fun (*this, &Editor::note_edit_done), d));
2013 Editor::note_edit_done (int r, EditNoteDialog* d)
2020 Editor::visible_order_range (int* low, int* high) const
2022 *low = TimeAxisView::max_order ();
2025 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
2027 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
2029 if (!rtv->hidden()) {
2031 if (*high < rtv->order()) {
2032 *high = rtv->order ();
2035 if (*low > rtv->order()) {
2036 *low = rtv->order ();
2043 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
2045 /* Either add to or set the set the region selection, unless
2046 this is an alignment click (control used)
2049 if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
2050 TimeAxisView* tv = &rv.get_time_axis_view();
2051 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
2053 if (rtv && rtv->is_track()) {
2054 speed = rtv->track()->speed();
2057 framepos_t where = get_preferred_edit_position();
2061 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
2063 align_region (rv.region(), SyncPoint, (framepos_t) (where * speed));
2065 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
2067 align_region (rv.region(), End, (framepos_t) (where * speed));
2071 align_region (rv.region(), Start, (framepos_t) (where * speed));
2078 Editor::collect_new_region_view (RegionView* rv)
2080 latest_regionviews.push_back (rv);
2084 Editor::collect_and_select_new_region_view (RegionView* rv)
2087 latest_regionviews.push_back (rv);
2091 Editor::cancel_selection ()
2093 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2094 (*i)->hide_selection ();
2097 selection->clear ();
2098 clicked_selection = 0;
2102 Editor::cancel_time_selection ()
2104 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2105 (*i)->hide_selection ();
2107 selection->time.clear ();
2108 clicked_selection = 0;
2112 Editor::point_trim (GdkEvent* event, framepos_t new_bound)
2114 RegionView* rv = clicked_regionview;
2116 /* Choose action dependant on which button was pressed */
2117 switch (event->button.button) {
2119 begin_reversible_command (_("start point trim"));
2121 if (selection->selected (rv)) {
2122 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
2123 i != selection->regions.by_layer().end(); ++i)
2125 if (!(*i)->region()->locked()) {
2126 (*i)->region()->clear_changes ();
2127 (*i)->region()->trim_front (new_bound);
2128 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2133 if (!rv->region()->locked()) {
2134 rv->region()->clear_changes ();
2135 rv->region()->trim_front (new_bound);
2136 _session->add_command(new StatefulDiffCommand (rv->region()));
2140 commit_reversible_command();
2144 begin_reversible_command (_("End point trim"));
2146 if (selection->selected (rv)) {
2148 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
2150 if (!(*i)->region()->locked()) {
2151 (*i)->region()->clear_changes();
2152 (*i)->region()->trim_end (new_bound);
2153 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2159 if (!rv->region()->locked()) {
2160 rv->region()->clear_changes ();
2161 rv->region()->trim_end (new_bound);
2162 _session->add_command (new StatefulDiffCommand (rv->region()));
2166 commit_reversible_command();
2175 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* /*event*/)
2180 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2181 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2182 abort(); /*NOTREACHED*/
2185 Location* location = find_location_from_marker (marker, is_start);
2186 location->set_hidden (true, this);
2190 Editor::mouse_rename_region (ArdourCanvas::Item* /*item*/, GdkEvent* /*event*/)
2192 using namespace Gtkmm2ext;
2194 ArdourPrompter prompter (false);
2196 prompter.set_prompt (_("Name for region:"));
2197 prompter.set_initial_text (clicked_regionview->region()->name());
2198 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
2199 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
2200 prompter.show_all ();
2201 switch (prompter.run ()) {
2202 case Gtk::RESPONSE_ACCEPT:
2204 prompter.get_result(str);
2206 clicked_regionview->region()->set_name (str);
2215 Editor::mouse_brush_insert_region (RegionView* rv, framepos_t pos)
2217 /* no brushing without a useful snap setting */
2219 switch (_snap_mode) {
2221 return; /* can't work because it allows region to be placed anywhere */
2226 switch (_snap_type) {
2234 /* don't brush a copy over the original */
2236 if (pos == rv->region()->position()) {
2240 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
2242 if (rtv == 0 || !rtv->is_track()) {
2246 boost::shared_ptr<Playlist> playlist = rtv->playlist();
2247 double speed = rtv->track()->speed();
2249 playlist->clear_changes ();
2250 boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region(), true));
2251 playlist->add_region (new_region, (framepos_t) (pos * speed));
2252 _session->add_command (new StatefulDiffCommand (playlist));
2254 // playlist is frozen, so we have to update manually XXX this is disgusting
2256 playlist->RegionAdded (new_region); /* EMIT SIGNAL */
2260 Editor::track_height_step_timeout ()
2262 if (get_microseconds() - last_track_height_step_timestamp < 250000) {
2263 current_stepping_trackview = 0;
2270 Editor::add_region_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2272 assert (region_view);
2274 if (!region_view->region()->playlist()) {
2278 switch (Config->get_edit_mode()) {
2280 _drags->add (new RegionSpliceDrag (this, item, region_view, selection->regions.by_layer()));
2283 _drags->add (new RegionRippleDrag (this, item, region_view, selection->regions.by_layer()));
2286 _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), false, false));
2293 Editor::add_region_copy_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2295 assert (region_view);
2297 if (!region_view->region()->playlist()) {
2301 _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), false, true));
2305 Editor::add_region_brush_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2307 assert (region_view);
2309 if (!region_view->region()->playlist()) {
2313 if (Config->get_edit_mode() == Splice || Config->get_edit_mode() == Ripple) {
2317 _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), true, false));
2319 begin_reversible_command (Operations::drag_region_brush);
2322 /** Start a grab where a time range is selected, track(s) are selected, and the
2323 * user clicks and drags a region with a modifier in order to create a new region containing
2324 * the section of the clicked region that lies within the time range.
2327 Editor::start_selection_grab (ArdourCanvas::Item* /*item*/, GdkEvent* event)
2329 if (clicked_regionview == 0) {
2333 /* lets try to create new Region for the selection */
2335 vector<boost::shared_ptr<Region> > new_regions;
2336 create_region_from_selection (new_regions);
2338 if (new_regions.empty()) {
2342 /* XXX fix me one day to use all new regions */
2344 boost::shared_ptr<Region> region (new_regions.front());
2346 /* add it to the current stream/playlist.
2348 tricky: the streamview for the track will add a new regionview. we will
2349 catch the signal it sends when it creates the regionview to
2350 set the regionview we want to then drag.
2353 latest_regionviews.clear();
2354 sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
2356 /* A selection grab currently creates two undo/redo operations, one for
2357 creating the new region and another for moving it.
2360 begin_reversible_command (Operations::selection_grab);
2362 boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
2364 playlist->clear_changes ();
2365 clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
2366 _session->add_command(new StatefulDiffCommand (playlist));
2368 commit_reversible_command ();
2372 if (latest_regionviews.empty()) {
2373 /* something went wrong */
2377 /* we need to deselect all other regionviews, and select this one
2378 i'm ignoring undo stuff, because the region creation will take care of it
2380 selection->set (latest_regionviews);
2382 _drags->set (new RegionMoveDrag (this, latest_regionviews.front()->get_canvas_group(), latest_regionviews.front(), latest_regionviews, false, false), event);
2388 if (_drags->active ()) {
2391 selection->clear ();
2398 Editor::set_internal_edit (bool yn)
2400 if (_internal_editing == yn) {
2404 _internal_editing = yn;
2407 pre_internal_mouse_mode = mouse_mode;
2408 pre_internal_snap_type = _snap_type;
2409 pre_internal_snap_mode = _snap_mode;
2411 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2412 (*i)->enter_internal_edit_mode ();
2415 set_snap_to (internal_snap_type);
2416 set_snap_mode (internal_snap_mode);
2420 internal_snap_mode = _snap_mode;
2421 internal_snap_type = _snap_type;
2423 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2424 (*i)->leave_internal_edit_mode ();
2427 set_snap_to (pre_internal_snap_type);
2428 set_snap_mode (pre_internal_snap_mode);
2431 reset_canvas_cursor ();
2432 MouseModeChanged ();
2435 /** Update _join_object_range_state which indicate whether we are over the top
2436 * or bottom half of a route view, used by the `join object/range' tool
2437 * mode. Coordinates in canvas space.
2440 Editor::update_join_object_range_location (double y)
2442 if (_internal_editing || !get_smart_mode()) {
2443 _join_object_range_state = JOIN_OBJECT_RANGE_NONE;
2447 JoinObjectRangeState const old = _join_object_range_state;
2449 if (mouse_mode == MouseObject) {
2450 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2451 } else if (mouse_mode == MouseRange) {
2452 _join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
2455 if (entered_regionview) {
2457 ArdourCanvas::Duple const item_space = entered_regionview->get_canvas_group()->canvas_to_item (ArdourCanvas::Duple (0, y));
2458 double const c = item_space.y / entered_regionview->height();
2460 _join_object_range_state = c <= 0.5 ? JOIN_OBJECT_RANGE_RANGE : JOIN_OBJECT_RANGE_OBJECT;
2462 if (_join_object_range_state != old) {
2463 set_canvas_cursor (which_track_cursor ());
2466 } else if (entered_track) {
2468 RouteTimeAxisView* entered_route_view = dynamic_cast<RouteTimeAxisView*> (entered_track);
2470 if (entered_route_view) {
2475 entered_route_view->canvas_display()->canvas_to_item (cx, cy);
2477 double track_height = entered_route_view->view()->child_height();
2478 if (Config->get_show_name_highlight()) {
2479 track_height -= TimeAxisViewItem::NAME_HIGHLIGHT_SIZE;
2481 double const c = cy / track_height;
2485 _join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
2487 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2491 /* Other kinds of tracks use object mode */
2492 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2495 if (_join_object_range_state != old) {
2496 set_canvas_cursor (which_track_cursor ());
2502 Editor::effective_mouse_mode () const
2504 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
2506 } else if (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE) {
2514 Editor::remove_midi_note (ArdourCanvas::Item* item, GdkEvent *)
2516 NoteBase* e = reinterpret_cast<NoteBase*> (item->get_data ("notebase"));
2519 e->region_view().delete_note (e->note ());
2522 /** Obtain the pointer position in canvas coordinates */
2524 Editor::get_pointer_position (double& x, double& y) const
2527 _track_canvas->get_pointer (px, py);
2528 _track_canvas->window_to_canvas (px, py, x, y);