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 == MouseZoom) m = MouseObject;
237 if ( m == MouseCut) m = MouseObject;
240 Glib::RefPtr<Action> act;
244 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-range"));
248 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-cut"));
252 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-object"));
256 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-draw"));
260 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-gain"));
264 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-zoom"));
268 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-timefx"));
272 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-audition"));
278 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
281 /* go there and back to ensure that the toggled handler is called to set up mouse_mode */
282 tact->set_active (false);
283 tact->set_active (true);
285 //NOTE: this will result in a call to mouse_mode_toggled which does the heavy lifting
289 Editor::mouse_mode_toggled (MouseMode m)
291 Glib::RefPtr<Action> act;
292 Glib::RefPtr<ToggleAction> tact;
294 if (ARDOUR::Profile->get_mixbus()) {
295 if ( m == MouseZoom) m = MouseObject;
296 if ( m == MouseCut) m = MouseObject;
301 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-range"));
305 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-object"));
309 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-cut"));
313 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-draw"));
317 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-gain"));
321 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-zoom"));
325 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-timefx"));
329 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-audition"));
335 tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
338 if (!tact->get_active()) {
339 /* this was just the notification that the old mode has been
340 * left. we'll get called again with the new mode active in a
348 act = ActionManager::get_action (X_("MouseMode"), X_("toggle-internal-edit"));
349 tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
350 tact->set_active (true);
356 if (_session && mouse_mode == MouseAudition) {
357 /* stop transport and reset default speed to avoid oddness with
359 _session->request_transport_speed (0.0, true);
366 /* this should generate a new enter event which will
367 trigger the appropiate cursor.
371 _track_canvas->re_enter ();
374 set_gain_envelope_visibility ();
376 update_time_selection_display ();
378 MouseModeChanged (); /* EMIT SIGNAL */
382 Editor::update_time_selection_display ()
384 if (smart_mode_action->get_active()) {
385 /* not sure what to do here */
386 if (mouse_mode == MouseObject) {
390 switch (mouse_mode) {
392 selection->clear_objects ();
395 selection->clear_time ();
402 Editor::step_mouse_mode (bool next)
404 switch (current_mouse_mode()) {
407 if (Profile->get_sae()) {
408 set_mouse_mode (MouseZoom);
410 set_mouse_mode (MouseRange);
413 set_mouse_mode (MouseTimeFX);
418 if (next) set_mouse_mode (MouseDraw);
419 else set_mouse_mode (MouseCut);
423 if (next) set_mouse_mode (MouseRange);
424 else set_mouse_mode (MouseDraw);
428 if (next) set_mouse_mode (MouseCut);
429 else set_mouse_mode (MouseRange);
434 if (Profile->get_sae()) {
435 set_mouse_mode (MouseTimeFX);
437 set_mouse_mode (MouseGain);
440 if (Profile->get_sae()) {
441 set_mouse_mode (MouseObject);
443 set_mouse_mode (MouseDraw);
449 if (next) set_mouse_mode (MouseTimeFX);
450 else set_mouse_mode (MouseZoom);
455 set_mouse_mode (MouseAudition);
457 if (Profile->get_sae()) {
458 set_mouse_mode (MouseZoom);
460 set_mouse_mode (MouseGain);
466 if (next) set_mouse_mode (MouseObject);
467 else set_mouse_mode (MouseTimeFX);
473 Editor::toggle_internal_editing_from_double_click (GdkEvent* event)
475 if (_drags->active()) {
476 _drags->end_grab (event);
479 ActionManager::do_action ("MouseMode", "toggle-internal-edit");
481 /* prevent reversion of edit cursor on button release */
483 pre_press_cursor = 0;
489 Editor::button_selection (ArdourCanvas::Item* /*item*/, GdkEvent* event, ItemType item_type)
491 /* in object/audition/timefx/gain-automation mode,
492 any button press sets the selection if the object
493 can be selected. this is a bit of hack, because
494 we want to avoid this if the mouse operation is a
497 note: not dbl-click or triple-click
499 Also note that there is no region selection in internal edit mode, otherwise
500 for operations operating on the selection (e.g. cut) it is not obvious whether
501 to cut notes or regions.
504 MouseMode eff_mouse_mode = effective_mouse_mode ();
506 if (eff_mouse_mode == MouseCut) {
507 /* never change selection in cut mode */
511 if (get_smart_mode() && eff_mouse_mode == MouseRange && event->button.button == 3 && item_type == RegionItem) {
512 /* context clicks are always about object properties, even if
513 we're in range mode within smart mode.
515 eff_mouse_mode = MouseObject;
518 /* special case: allow drag of region fade in/out in object mode with join object/range enabled */
519 if (get_smart_mode()) {
521 case FadeInHandleItem:
522 case FadeInTrimHandleItem:
523 case FadeOutHandleItem:
524 case FadeOutTrimHandleItem:
525 eff_mouse_mode = MouseObject;
532 if (((mouse_mode != MouseObject) &&
533 (mouse_mode != MouseAudition || item_type != RegionItem) &&
534 (mouse_mode != MouseTimeFX || item_type != RegionItem) &&
535 (mouse_mode != MouseGain) &&
536 (mouse_mode != MouseDraw)) ||
537 ((event->type != GDK_BUTTON_PRESS && event->type != GDK_BUTTON_RELEASE) || event->button.button > 3) ||
538 (internal_editing() && mouse_mode != MouseTimeFX)) {
543 if (event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) {
545 if ((event->button.state & Keyboard::RelevantModifierKeyMask) && event->button.button != 1) {
547 /* almost no selection action on modified button-2 or button-3 events */
549 if (item_type != RegionItem && event->button.button != 2) {
555 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
556 bool press = (event->type == GDK_BUTTON_PRESS);
561 if (eff_mouse_mode != MouseRange) {
562 set_selected_regionview_from_click (press, op);
564 /* don't change the selection unless the
565 clicked track is not currently selected. if
566 so, "collapse" the selection to just this
569 if (!selection->selected (clicked_axisview)) {
570 set_selected_track_as_side_effect (Selection::Set);
574 if (eff_mouse_mode != MouseRange) {
575 set_selected_regionview_from_click (press, op);
580 case RegionViewNameHighlight:
582 case LeftFrameHandle:
583 case RightFrameHandle:
584 case FadeInHandleItem:
585 case FadeInTrimHandleItem:
587 case FadeOutHandleItem:
588 case FadeOutTrimHandleItem:
590 case StartCrossFadeItem:
591 case EndCrossFadeItem:
592 if (get_smart_mode() || eff_mouse_mode != MouseRange) {
593 set_selected_regionview_from_click (press, op);
594 } else if (event->type == GDK_BUTTON_PRESS) {
595 set_selected_track_as_side_effect (op);
599 case ControlPointItem:
600 set_selected_track_as_side_effect (op);
601 if (eff_mouse_mode != MouseRange) {
602 set_selected_control_point_from_click (press, op);
607 /* for context click, select track */
608 if (event->button.button == 3) {
609 selection->clear_tracks ();
610 set_selected_track_as_side_effect (op);
614 case AutomationTrackItem:
615 set_selected_track_as_side_effect (op);
624 Editor::button_press_handler_1 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
626 /* single mouse clicks on any of these item types operate
627 independent of mouse mode, mostly because they are
628 not on the main track canvas or because we want
633 case PlayheadCursorItem:
634 _drags->set (new CursorDrag (this, *playhead_cursor, true), event);
638 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
639 hide_marker (item, event);
641 _drags->set (new MarkerDrag (this, item), event);
645 case TempoMarkerItem:
647 TempoMarker* m = reinterpret_cast<TempoMarker*> (item->get_data ("marker"));
650 new TempoMarkerDrag (
653 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
660 case MeterMarkerItem:
662 MeterMarker* m = reinterpret_cast<MeterMarker*> (item->get_data ("marker"));
665 new MeterMarkerDrag (
668 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
676 _drags->set (new VideoTimeLineDrag (this, item), event);
683 case TimecodeRulerItem:
684 case SamplesRulerItem:
685 case MinsecRulerItem:
687 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
688 _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
694 case RangeMarkerBarItem:
695 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
696 _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
698 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateRangeMarker), event);
703 case CdMarkerBarItem:
704 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
705 _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
707 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateCDMarker), event);
712 case TransportMarkerBarItem:
713 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
714 _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
716 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateTransportMarker), event);
725 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
726 /* special case: allow trim of range selections in joined object mode;
727 in theory eff should equal MouseRange in this case, but it doesn't
728 because entering the range selection canvas item results in entered_regionview
729 being set to 0, so update_join_object_range_location acts as if we aren't
732 if (item_type == StartSelectionTrimItem) {
733 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
734 } else if (item_type == EndSelectionTrimItem) {
735 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
739 Editing::MouseMode eff = effective_mouse_mode ();
741 /* special case: allow drag of region fade in/out in object mode with join object/range enabled */
742 if (get_smart_mode()) {
744 case FadeInHandleItem:
745 case FadeInTrimHandleItem:
746 case FadeOutHandleItem:
747 case FadeOutTrimHandleItem:
755 /* there is no Range mode when in internal edit mode */
756 if (eff == MouseRange && internal_editing()) {
763 case StartSelectionTrimItem:
764 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
767 case EndSelectionTrimItem:
768 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
772 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
773 start_selection_grab (item, event);
775 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
776 /* grab selection for moving */
777 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionMove), event);
779 double const y = event->button.y;
780 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
782 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
783 if ( get_smart_mode() && atv) {
784 /* smart "join" mode: drag automation */
785 _drags->set (new AutomationRangeDrag (this, atv, selection->time), event, _cursors->up_down);
787 /* this was debated, but decided the more common action was to
788 make a new selection */
789 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
796 if (internal_editing()) {
797 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
798 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
802 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::RangeSelectModifier)) {
803 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionExtend), event);
805 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
811 case RegionViewNameHighlight:
812 if (!clicked_regionview->region()->locked()) {
813 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
819 if (!internal_editing()) {
820 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::RangeSelectModifier)) {
821 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionExtend), event);
823 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
833 /* Existing note: allow trimming/motion */
834 if (internal_editing()) {
835 /* trim notes if we're in internal edit mode and near the ends of the note */
836 NoteBase* cn = reinterpret_cast<NoteBase*>(item->get_data ("notebase"));
838 if (cn->big_enough_to_trim() && cn->mouse_near_ends()) {
839 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
841 _drags->set (new NoteDrag (this, item), event);
847 if (internal_editing()) {
848 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
849 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
863 case FadeInHandleItem:
864 case FadeOutHandleItem:
865 case LeftFrameHandle:
866 case RightFrameHandle:
867 case FeatureLineItem:
868 case RegionViewNameHighlight:
871 case AutomationTrackItem:
872 _drags->set (new RegionCutDrag (this, item, canvas_event_sample (event)), event, current_canvas_cursor);
883 /* Existing note: allow trimming/motion */
884 if (internal_editing()) {
885 NoteBase* cn = reinterpret_cast<NoteBase*> (item->get_data ("notebase"));
887 if (cn->big_enough_to_trim() && cn->mouse_near_ends()) {
888 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
890 _drags->set (new NoteDrag (this, item), event);
900 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)) &&
901 event->type == GDK_BUTTON_PRESS) {
903 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
905 } else if (event->type == GDK_BUTTON_PRESS) {
908 case FadeInHandleItem:
910 _drags->set (new FadeInDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), selection->regions), event, _cursors->fade_in);
914 case FadeOutHandleItem:
916 _drags->set (new FadeOutDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), selection->regions), event, _cursors->fade_out);
920 case StartCrossFadeItem:
921 case EndCrossFadeItem:
922 /* we might allow user to grab inside the fade to trim a region with preserve_fade_anchor. for not this is not fully implemented */
923 // if (!clicked_regionview->region()->locked()) {
924 // _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer(), true), event);
929 case FeatureLineItem:
931 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier)) {
932 remove_transient(item);
936 _drags->set (new FeatureLineDrag (this, item), event);
942 if (dynamic_cast<AutomationRegionView*> (clicked_regionview)) {
943 /* click on an automation region view; do nothing here and let the ARV's signal handler
949 if (internal_editing ()) {
953 /* click on a normal region view */
954 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
955 add_region_copy_drag (item, event, clicked_regionview);
956 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
957 add_region_brush_drag (item, event, clicked_regionview);
959 add_region_drag (item, event, clicked_regionview);
963 // if (!internal_editing() && (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE && !selection->regions.empty())) {
964 // _drags->add (new SelectionDrag (this, clicked_axisview->get_selection_rect (clicked_selection)->rect, SelectionDrag::SelectionMove));
967 _drags->start_grab (event);
971 case RegionViewNameHighlight:
972 case LeftFrameHandle:
973 case RightFrameHandle:
974 if (!clicked_regionview->region()->locked()) {
975 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
980 case FadeInTrimHandleItem:
981 case FadeOutTrimHandleItem:
982 if (!clicked_regionview->region()->locked()) {
983 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer(), true), event);
990 /* rename happens on edit clicks */
991 if (clicked_regionview->get_name_highlight()) {
992 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer()), event);
998 case ControlPointItem:
999 _drags->set (new ControlPointDrag (this, item), event);
1003 case AutomationLineItem:
1004 _drags->set (new LineDrag (this, item), event);
1009 if (internal_editing()) {
1010 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
1011 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
1015 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
1019 case AutomationTrackItem:
1021 TimeAxisView* parent = clicked_axisview->get_parent ();
1022 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (clicked_axisview);
1024 if (parent && dynamic_cast<MidiTimeAxisView*> (parent) && atv->show_regions ()) {
1026 RouteTimeAxisView* p = dynamic_cast<RouteTimeAxisView*> (parent);
1028 boost::shared_ptr<Playlist> pl = p->track()->playlist ();
1029 if (pl->n_regions() == 0) {
1030 /* Parent has no regions; create one so that we have somewhere to put automation */
1031 _drags->set (new RegionCreateDrag (this, item, parent), event);
1033 /* See if there's a region before the click that we can extend, and extend it if so */
1034 framepos_t const t = canvas_event_sample (event);
1035 boost::shared_ptr<Region> prev = pl->find_next_region (t, End, -1);
1037 _drags->set (new RegionCreateDrag (this, item, parent), event);
1039 prev->set_length (t - prev->position ());
1043 /* rubberband drag to select automation points */
1044 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
1051 if ( get_smart_mode() ) {
1052 /* we're in "smart" joined mode, and we've clicked on a Selection */
1053 double const y = event->button.y;
1054 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
1056 /* if we're over an automation track, start a drag of its data */
1057 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
1059 _drags->set (new AutomationRangeDrag (this, atv, selection->time), event, _cursors->up_down);
1062 /* if we're over a track and a region, and in the `object' part of a region,
1063 put a selection around the region and drag both
1065 /* RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1066 if (rtv && _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
1067 boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track> (rtv->route ());
1069 boost::shared_ptr<Playlist> pl = t->playlist ();
1072 boost::shared_ptr<Region> r = pl->top_region_at (canvas_event_sample (event));
1074 RegionView* rv = rtv->view()->find_view (r);
1075 clicked_selection = select_range (rv->region()->position(),
1076 rv->region()->last_frame()+1);
1077 _drags->add (new SelectionDrag (this, item, SelectionDrag::SelectionMove));
1078 list<RegionView*> rvs;
1080 _drags->add (new RegionMoveDrag (this, item, rv, rvs, false, false));
1081 _drags->start_grab (event);
1105 switch (item_type) {
1107 _drags->set (new LineDrag (this, item), event);
1110 case ControlPointItem:
1111 _drags->set (new ControlPointDrag (this, item), event);
1117 AudioRegionView* arv = dynamic_cast<AudioRegionView *> (clicked_regionview);
1119 _drags->set (new AutomationRangeDrag (this, arv, selection->time), event, _cursors->up_down);
1120 _drags->start_grab (event);
1126 case AutomationLineItem:
1127 _drags->set (new LineDrag (this, item), event);
1137 if (event->type == GDK_BUTTON_PRESS) {
1138 _drags->set (new MouseZoomDrag (this, item), event);
1145 if (internal_editing() && item_type == NoteItem ) {
1146 /* drag notes if we're in internal edit mode */
1147 NoteBase* cn = reinterpret_cast<NoteBase*>(item->get_data ("notebase"));
1149 if (cn->big_enough_to_trim()) {
1150 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
1153 } else if (clicked_regionview) {
1155 _drags->set (new TimeFXDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1161 _drags->set (new ScrubDrag (this, item), event);
1162 scrub_reversals = 0;
1163 scrub_reverse_distance = 0;
1164 last_scrub_x = event->button.x;
1165 scrubbing_direction = 0;
1166 push_canvas_cursor (_cursors->transparent);
1178 Editor::button_press_handler_2 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1180 Editing::MouseMode const eff = effective_mouse_mode ();
1183 switch (item_type) {
1185 if (internal_editing ()) {
1186 /* no region drags in internal edit mode */
1190 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
1191 add_region_copy_drag (item, event, clicked_regionview);
1193 add_region_drag (item, event, clicked_regionview);
1195 _drags->start_grab (event);
1198 case ControlPointItem:
1199 _drags->set (new ControlPointDrag (this, item), event);
1207 switch (item_type) {
1208 case RegionViewNameHighlight:
1209 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1213 case LeftFrameHandle:
1214 case RightFrameHandle:
1215 if (!internal_editing ()) {
1216 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1221 case RegionViewName:
1222 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer()), event);
1236 /* relax till release */
1242 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1243 temporal_zoom_to_frame (false, canvas_event_sample (event));
1245 temporal_zoom_to_frame (true, canvas_event_sample(event));
1258 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1260 if (event->type == GDK_2BUTTON_PRESS) {
1261 _drags->mark_double_click ();
1262 gdk_pointer_ungrab (GDK_CURRENT_TIME);
1266 if (event->type != GDK_BUTTON_PRESS) {
1270 pre_press_cursor = current_canvas_cursor;
1272 _track_canvas->grab_focus();
1274 if (_session && _session->actively_recording()) {
1278 if (internal_editing()) {
1279 bool leave_internal_edit_mode = false;
1281 switch (item_type) {
1286 if (!dynamic_cast<MidiRegionView*> (clicked_regionview) && !dynamic_cast<AutomationRegionView*> (clicked_regionview)) {
1287 leave_internal_edit_mode = true;
1291 case PlayheadCursorItem:
1293 case TempoMarkerItem:
1294 case MeterMarkerItem:
1298 case RangeMarkerBarItem:
1299 case CdMarkerBarItem:
1300 case TransportMarkerBarItem:
1302 case TimecodeRulerItem:
1303 case SamplesRulerItem:
1304 case MinsecRulerItem:
1306 /* button press on these items never does anything to
1307 change the editing mode.
1315 if (leave_internal_edit_mode) {
1316 ActionManager::do_action ("MouseMode", "toggle-internal-edit");
1320 button_selection (item, event, item_type);
1322 if (!_drags->active () &&
1323 (Keyboard::is_delete_event (&event->button) ||
1324 Keyboard::is_context_menu_event (&event->button) ||
1325 Keyboard::is_edit_event (&event->button))) {
1327 /* handled by button release */
1331 //not rolling, range mode click + join_play_range : locate the PH here
1332 if ( !_drags->active () && !_session->transport_rolling() && ( effective_mouse_mode() == MouseRange ) && Config->get_follow_edits() ) {
1333 framepos_t where = canvas_event_sample (event);
1335 _session->request_locate (where, false);
1338 switch (event->button.button) {
1340 return button_press_handler_1 (item, event, item_type);
1344 return button_press_handler_2 (item, event, item_type);
1351 return button_press_dispatch (&event->button);
1360 Editor::button_press_dispatch (GdkEventButton* ev)
1362 /* this function is intended only for buttons 4 and above.
1365 Gtkmm2ext::MouseButton b (ev->state, ev->button);
1366 return button_bindings->activate (b, Gtkmm2ext::Bindings::Press);
1370 Editor::button_release_dispatch (GdkEventButton* ev)
1372 /* this function is intended only for buttons 4 and above.
1375 Gtkmm2ext::MouseButton b (ev->state, ev->button);
1376 return button_bindings->activate (b, Gtkmm2ext::Bindings::Release);
1380 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1382 framepos_t where = canvas_event_sample (event);
1383 AutomationTimeAxisView* atv = 0;
1385 if (pre_press_cursor) {
1386 set_canvas_cursor (pre_press_cursor);
1387 pre_press_cursor = 0;
1390 /* no action if we're recording */
1392 if (_session && _session->actively_recording()) {
1396 /* see if we're finishing a drag */
1398 bool were_dragging = false;
1399 if (_drags->active ()) {
1400 bool const r = _drags->end_grab (event);
1402 /* grab dragged, so do nothing else */
1406 were_dragging = true;
1409 update_region_layering_order_editor ();
1411 /* edit events get handled here */
1413 if (!_drags->active () && Keyboard::is_edit_event (&event->button)) {
1414 switch (item_type) {
1416 show_region_properties ();
1419 case TempoMarkerItem: {
1421 TempoMarker* tempo_marker;
1423 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
1424 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
1428 if ((tempo_marker = dynamic_cast<TempoMarker*> (marker)) == 0) {
1429 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
1433 edit_tempo_marker (*tempo_marker);
1437 case MeterMarkerItem: {
1439 MeterMarker* meter_marker;
1441 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
1442 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
1446 if ((meter_marker = dynamic_cast<MeterMarker*> (marker)) == 0) {
1447 fatal << _("programming error: marker for meter is not a meter marker!") << endmsg;
1450 edit_meter_marker (*meter_marker);
1454 case RegionViewName:
1455 if (clicked_regionview->name_active()) {
1456 return mouse_rename_region (item, event);
1460 case ControlPointItem:
1461 edit_control_point (item);
1470 /* context menu events get handled here */
1471 if (Keyboard::is_context_menu_event (&event->button)) {
1473 context_click_event = *event;
1475 if (!_drags->active ()) {
1477 /* no matter which button pops up the context menu, tell the menu
1478 widget to use button 1 to drive menu selection.
1481 switch (item_type) {
1483 case FadeInHandleItem:
1484 case FadeInTrimHandleItem:
1485 case StartCrossFadeItem:
1486 case LeftFrameHandle:
1487 popup_xfade_in_context_menu (1, event->button.time, item, item_type);
1491 case FadeOutHandleItem:
1492 case FadeOutTrimHandleItem:
1493 case EndCrossFadeItem:
1494 case RightFrameHandle:
1495 popup_xfade_out_context_menu (1, event->button.time, item, item_type);
1499 popup_track_context_menu (1, event->button.time, item_type, false);
1503 case RegionViewNameHighlight:
1504 case RegionViewName:
1505 popup_track_context_menu (1, event->button.time, item_type, false);
1509 popup_track_context_menu (1, event->button.time, item_type, true);
1512 case AutomationTrackItem:
1513 popup_track_context_menu (1, event->button.time, item_type, false);
1517 case RangeMarkerBarItem:
1518 case TransportMarkerBarItem:
1519 case CdMarkerBarItem:
1523 case TimecodeRulerItem:
1524 case SamplesRulerItem:
1525 case MinsecRulerItem:
1527 popup_ruler_menu (where, item_type);
1531 marker_context_menu (&event->button, item);
1534 case TempoMarkerItem:
1535 tempo_or_meter_marker_context_menu (&event->button, item);
1538 case MeterMarkerItem:
1539 tempo_or_meter_marker_context_menu (&event->button, item);
1542 case CrossfadeViewItem:
1543 popup_track_context_menu (1, event->button.time, item_type, false);
1546 case ControlPointItem:
1547 popup_control_point_context_menu (item, event);
1558 /* delete events get handled here */
1560 Editing::MouseMode const eff = effective_mouse_mode ();
1562 if (!_drags->active () && Keyboard::is_delete_event (&event->button)) {
1564 switch (item_type) {
1565 case TempoMarkerItem:
1566 remove_tempo_marker (item);
1569 case MeterMarkerItem:
1570 remove_meter_marker (item);
1574 remove_marker (*item, event);
1578 if (eff == MouseObject) {
1579 remove_clicked_region ();
1583 case ControlPointItem:
1584 remove_control_point (item);
1588 remove_midi_note (item, event);
1597 switch (event->button.button) {
1600 switch (item_type) {
1601 /* see comments in button_press_handler */
1602 case PlayheadCursorItem:
1605 case AutomationLineItem:
1606 case StartSelectionTrimItem:
1607 case EndSelectionTrimItem:
1611 if (!_dragging_playhead) {
1612 snap_to_with_modifier (where, event, 0, true);
1613 mouse_add_new_marker (where);
1617 case CdMarkerBarItem:
1618 if (!_dragging_playhead) {
1619 // if we get here then a dragged range wasn't done
1620 snap_to_with_modifier (where, event, 0, true);
1621 mouse_add_new_marker (where, true);
1626 if (!_dragging_playhead) {
1627 snap_to_with_modifier (where, event);
1628 mouse_add_new_tempo_event (where);
1633 if (!_dragging_playhead) {
1634 mouse_add_new_meter_event (pixel_to_sample (event->button.x));
1639 case TimecodeRulerItem:
1640 case SamplesRulerItem:
1641 case MinsecRulerItem:
1652 switch (item_type) {
1653 case AutomationTrackItem:
1654 atv = dynamic_cast<AutomationTimeAxisView*>(clicked_axisview);
1656 bool with_guard_points = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
1657 atv->add_automation_event (event, where, event->button.y, with_guard_points);
1667 switch (item_type) {
1670 /* check that we didn't drag before releasing, since
1671 its really annoying to create new control
1672 points when doing this.
1674 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (clicked_regionview);
1675 if (!were_dragging && arv) {
1676 bool with_guard_points = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
1677 arv->add_gain_point_event (item, event, with_guard_points);
1683 case AutomationTrackItem: {
1684 bool with_guard_points = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
1685 dynamic_cast<AutomationTimeAxisView*>(clicked_axisview)->
1686 add_automation_event (event, where, event->button.y, with_guard_points);
1696 pop_canvas_cursor ();
1697 if (scrubbing_direction == 0) {
1698 /* no drag, just a click */
1699 switch (item_type) {
1701 play_selected_region ();
1707 /* make sure we stop */
1708 _session->request_transport_speed (0.0);
1717 /* do any (de)selection operations that should occur on button release */
1718 button_selection (item, event, item_type);
1727 switch (item_type) {
1729 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1731 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier|Keyboard::SecondaryModifier))) {
1734 // Button2 click is unused
1749 // x_style_paste (where, 1.0);
1770 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1777 /* by the time we reach here, entered_regionview and entered trackview
1778 * will have already been set as appropriate. Things are done this
1779 * way because this method isn't passed a pointer to a variable type of
1780 * thing that is entered (which may or may not be canvas item).
1781 * (e.g. the actual entered regionview)
1784 choose_canvas_cursor_on_entry (&event->crossing, item_type);
1786 switch (item_type) {
1787 case ControlPointItem:
1788 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1789 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1792 fraction = 1.0 - (cp->get_y() / cp->line().height());
1794 _verbose_cursor->set (cp->line().get_verbose_cursor_string (fraction));
1795 _verbose_cursor->show ();
1800 if (mouse_mode == MouseGain) {
1801 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1803 line->set_outline_color (ARDOUR_UI::config()->get_canvasvar_EnteredGainLine());
1808 case AutomationLineItem:
1809 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1810 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1812 line->set_outline_color (ARDOUR_UI::config()->get_canvasvar_EnteredAutomationLine());
1817 case AutomationTrackItem:
1818 AutomationTimeAxisView* atv;
1819 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1820 clear_entered_track = false;
1821 set_entered_track (atv);
1826 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1829 entered_marker = marker;
1830 marker->set_color_rgba (ARDOUR_UI::config()->get_canvasvar_EnteredMarker());
1832 case MeterMarkerItem:
1833 case TempoMarkerItem:
1836 case FadeInHandleItem:
1837 case FadeInTrimHandleItem:
1838 if (mouse_mode == MouseObject && !internal_editing()) {
1839 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
1841 RegionView* rv = static_cast<RegionView*>(item->get_data ("regionview"));
1842 rect->set_fill_color (rv->get_fill_color());
1847 case FadeOutHandleItem:
1848 case FadeOutTrimHandleItem:
1849 if (mouse_mode == MouseObject && !internal_editing()) {
1850 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
1852 RegionView* rv = static_cast<RegionView*>(item->get_data ("regionview"));
1853 rect->set_fill_color (rv->get_fill_color ());
1858 case FeatureLineItem:
1860 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1861 line->set_outline_color (0xFF0000FF);
1872 /* third pass to handle entered track status in a comprehensible way.
1875 switch (item_type) {
1877 case AutomationLineItem:
1878 case ControlPointItem:
1879 /* these do not affect the current entered track state */
1880 clear_entered_track = false;
1883 case AutomationTrackItem:
1884 /* handled above already */
1896 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent*, ItemType item_type)
1904 switch (item_type) {
1905 case ControlPointItem:
1906 _verbose_cursor->hide ();
1910 case AutomationLineItem:
1911 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1913 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1915 line->set_outline_color (al->get_line_color());
1921 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1925 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
1926 location_flags_changed (loc, this);
1929 case MeterMarkerItem:
1930 case TempoMarkerItem:
1933 case FadeInTrimHandleItem:
1934 case FadeOutTrimHandleItem:
1935 case FadeInHandleItem:
1936 case FadeOutHandleItem:
1938 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
1940 rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_InactiveFadeHandle());
1945 case AutomationTrackItem:
1948 case FeatureLineItem:
1950 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1951 line->set_outline_color (ARDOUR_UI::config()->get_canvasvar_ZeroLine());
1963 Editor::scrub (framepos_t frame, double current_x)
1967 if (scrubbing_direction == 0) {
1969 _session->request_locate (frame, false);
1970 _session->request_transport_speed (0.1);
1971 scrubbing_direction = 1;
1975 if (last_scrub_x > current_x) {
1977 /* pointer moved to the left */
1979 if (scrubbing_direction > 0) {
1981 /* we reversed direction to go backwards */
1984 scrub_reverse_distance += (int) (last_scrub_x - current_x);
1988 /* still moving to the left (backwards) */
1990 scrub_reversals = 0;
1991 scrub_reverse_distance = 0;
1993 delta = 0.01 * (last_scrub_x - current_x);
1994 _session->request_transport_speed_nonzero (_session->transport_speed() - delta);
1998 /* pointer moved to the right */
2000 if (scrubbing_direction < 0) {
2001 /* we reversed direction to go forward */
2004 scrub_reverse_distance += (int) (current_x - last_scrub_x);
2007 /* still moving to the right */
2009 scrub_reversals = 0;
2010 scrub_reverse_distance = 0;
2012 delta = 0.01 * (current_x - last_scrub_x);
2013 _session->request_transport_speed_nonzero (_session->transport_speed() + delta);
2017 /* if there have been more than 2 opposite motion moves detected, or one that moves
2018 back more than 10 pixels, reverse direction
2021 if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
2023 if (scrubbing_direction > 0) {
2024 /* was forwards, go backwards */
2025 _session->request_transport_speed (-0.1);
2026 scrubbing_direction = -1;
2028 /* was backwards, go forwards */
2029 _session->request_transport_speed (0.1);
2030 scrubbing_direction = 1;
2033 scrub_reverse_distance = 0;
2034 scrub_reversals = 0;
2038 last_scrub_x = current_x;
2042 Editor::motion_handler (ArdourCanvas::Item* /*item*/, GdkEvent* event, bool from_autoscroll)
2044 _last_motion_y = event->motion.y;
2046 if (event->motion.is_hint) {
2049 /* We call this so that MOTION_NOTIFY events continue to be
2050 delivered to the canvas. We need to do this because we set
2051 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
2052 the density of the events, at the expense of a round-trip
2053 to the server. Given that this will mostly occur on cases
2054 where DISPLAY = :0.0, and given the cost of what the motion
2055 event might do, its a good tradeoff.
2058 _track_canvas->get_pointer (x, y);
2061 if (current_stepping_trackview) {
2062 /* don't keep the persistent stepped trackview if the mouse moves */
2063 current_stepping_trackview = 0;
2064 step_timeout.disconnect ();
2067 if (_session && _session->actively_recording()) {
2068 /* Sorry. no dragging stuff around while we record */
2072 update_join_object_range_location (event->motion.y);
2074 if (_drags->active ()) {
2075 return _drags->motion_handler (event, from_autoscroll);
2082 Editor::can_remove_control_point (ArdourCanvas::Item* item)
2084 ControlPoint* control_point;
2086 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2087 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2091 AutomationLine& line = control_point->line ();
2092 if (dynamic_cast<AudioRegionGainLine*> (&line)) {
2093 /* we shouldn't remove the first or last gain point in region gain lines */
2094 if (line.is_last_point(*control_point) || line.is_first_point(*control_point)) {
2103 Editor::remove_control_point (ArdourCanvas::Item* item)
2105 if (!can_remove_control_point (item)) {
2109 ControlPoint* control_point;
2111 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2112 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2116 control_point->line().remove_point (*control_point);
2120 Editor::edit_control_point (ArdourCanvas::Item* item)
2122 ControlPoint* p = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"));
2125 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2129 ControlPointDialog d (p);
2132 if (d.run () != RESPONSE_ACCEPT) {
2136 p->line().modify_point_y (*p, d.get_y_fraction ());
2140 Editor::edit_notes (TimeAxisViewItem& tavi)
2142 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(&tavi);
2148 MidiRegionView::Selection const & s = mrv->selection();
2154 EditNoteDialog* d = new EditNoteDialog (&(*s.begin())->region_view(), s);
2158 d->signal_response().connect (sigc::bind (sigc::mem_fun (*this, &Editor::note_edit_done), d));
2162 Editor::note_edit_done (int r, EditNoteDialog* d)
2169 Editor::visible_order_range (int* low, int* high) const
2171 *low = TimeAxisView::max_order ();
2174 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
2176 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
2178 if (!rtv->hidden()) {
2180 if (*high < rtv->order()) {
2181 *high = rtv->order ();
2184 if (*low > rtv->order()) {
2185 *low = rtv->order ();
2192 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
2194 /* Either add to or set the set the region selection, unless
2195 this is an alignment click (control used)
2198 if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
2199 TimeAxisView* tv = &rv.get_time_axis_view();
2200 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
2202 if (rtv && rtv->is_track()) {
2203 speed = rtv->track()->speed();
2206 framepos_t where = get_preferred_edit_position();
2210 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
2212 align_region (rv.region(), SyncPoint, (framepos_t) (where * speed));
2214 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
2216 align_region (rv.region(), End, (framepos_t) (where * speed));
2220 align_region (rv.region(), Start, (framepos_t) (where * speed));
2227 Editor::collect_new_region_view (RegionView* rv)
2229 latest_regionviews.push_back (rv);
2233 Editor::collect_and_select_new_region_view (RegionView* rv)
2236 latest_regionviews.push_back (rv);
2240 Editor::cancel_selection ()
2242 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2243 (*i)->hide_selection ();
2246 selection->clear ();
2247 clicked_selection = 0;
2251 Editor::cancel_time_selection ()
2253 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2254 (*i)->hide_selection ();
2256 selection->time.clear ();
2257 clicked_selection = 0;
2261 Editor::point_trim (GdkEvent* event, framepos_t new_bound)
2263 RegionView* rv = clicked_regionview;
2265 /* Choose action dependant on which button was pressed */
2266 switch (event->button.button) {
2268 begin_reversible_command (_("start point trim"));
2270 if (selection->selected (rv)) {
2271 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
2272 i != selection->regions.by_layer().end(); ++i)
2274 if (!(*i)->region()->locked()) {
2275 (*i)->region()->clear_changes ();
2276 (*i)->region()->trim_front (new_bound);
2277 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2282 if (!rv->region()->locked()) {
2283 rv->region()->clear_changes ();
2284 rv->region()->trim_front (new_bound);
2285 _session->add_command(new StatefulDiffCommand (rv->region()));
2289 commit_reversible_command();
2293 begin_reversible_command (_("End point trim"));
2295 if (selection->selected (rv)) {
2297 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
2299 if (!(*i)->region()->locked()) {
2300 (*i)->region()->clear_changes();
2301 (*i)->region()->trim_end (new_bound);
2302 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2308 if (!rv->region()->locked()) {
2309 rv->region()->clear_changes ();
2310 rv->region()->trim_end (new_bound);
2311 _session->add_command (new StatefulDiffCommand (rv->region()));
2315 commit_reversible_command();
2324 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* /*event*/)
2329 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2330 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2334 Location* location = find_location_from_marker (marker, is_start);
2335 location->set_hidden (true, this);
2340 Editor::reposition_zoom_rect (framepos_t start, framepos_t end)
2342 double x1 = sample_to_pixel (start);
2343 double x2 = sample_to_pixel (end);
2344 double y2 = _full_canvas_height - 1.0;
2346 zoom_rect->set (ArdourCanvas::Rect (x1, 1.0, x2, y2));
2351 Editor::mouse_rename_region (ArdourCanvas::Item* /*item*/, GdkEvent* /*event*/)
2353 using namespace Gtkmm2ext;
2355 ArdourPrompter prompter (false);
2357 prompter.set_prompt (_("Name for region:"));
2358 prompter.set_initial_text (clicked_regionview->region()->name());
2359 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
2360 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
2361 prompter.show_all ();
2362 switch (prompter.run ()) {
2363 case Gtk::RESPONSE_ACCEPT:
2365 prompter.get_result(str);
2367 clicked_regionview->region()->set_name (str);
2376 Editor::mouse_brush_insert_region (RegionView* rv, framepos_t pos)
2378 /* no brushing without a useful snap setting */
2380 switch (_snap_mode) {
2382 return; /* can't work because it allows region to be placed anywhere */
2387 switch (_snap_type) {
2395 /* don't brush a copy over the original */
2397 if (pos == rv->region()->position()) {
2401 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
2403 if (rtv == 0 || !rtv->is_track()) {
2407 boost::shared_ptr<Playlist> playlist = rtv->playlist();
2408 double speed = rtv->track()->speed();
2410 playlist->clear_changes ();
2411 boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region(), true));
2412 playlist->add_region (new_region, (framepos_t) (pos * speed));
2413 _session->add_command (new StatefulDiffCommand (playlist));
2415 // playlist is frozen, so we have to update manually XXX this is disgusting
2417 playlist->RegionAdded (new_region); /* EMIT SIGNAL */
2421 Editor::track_height_step_timeout ()
2423 if (get_microseconds() - last_track_height_step_timestamp < 250000) {
2424 current_stepping_trackview = 0;
2431 Editor::add_region_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2433 assert (region_view);
2435 if (!region_view->region()->playlist()) {
2439 switch (Config->get_edit_mode()) {
2441 _drags->add (new RegionSpliceDrag (this, item, region_view, selection->regions.by_layer()));
2444 _drags->add (new RegionRippleDrag (this, item, region_view, selection->regions.by_layer()));
2447 _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), false, false));
2454 Editor::add_region_copy_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2456 assert (region_view);
2458 if (!region_view->region()->playlist()) {
2462 _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), false, true));
2466 Editor::add_region_brush_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2468 assert (region_view);
2470 if (!region_view->region()->playlist()) {
2474 if (Config->get_edit_mode() == Splice || Config->get_edit_mode() == Ripple) {
2478 _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), true, false));
2480 begin_reversible_command (Operations::drag_region_brush);
2483 /** Start a grab where a time range is selected, track(s) are selected, and the
2484 * user clicks and drags a region with a modifier in order to create a new region containing
2485 * the section of the clicked region that lies within the time range.
2488 Editor::start_selection_grab (ArdourCanvas::Item* /*item*/, GdkEvent* event)
2490 if (clicked_regionview == 0) {
2494 /* lets try to create new Region for the selection */
2496 vector<boost::shared_ptr<Region> > new_regions;
2497 create_region_from_selection (new_regions);
2499 if (new_regions.empty()) {
2503 /* XXX fix me one day to use all new regions */
2505 boost::shared_ptr<Region> region (new_regions.front());
2507 /* add it to the current stream/playlist.
2509 tricky: the streamview for the track will add a new regionview. we will
2510 catch the signal it sends when it creates the regionview to
2511 set the regionview we want to then drag.
2514 latest_regionviews.clear();
2515 sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
2517 /* A selection grab currently creates two undo/redo operations, one for
2518 creating the new region and another for moving it.
2521 begin_reversible_command (Operations::selection_grab);
2523 boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
2525 playlist->clear_changes ();
2526 clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
2527 _session->add_command(new StatefulDiffCommand (playlist));
2529 commit_reversible_command ();
2533 if (latest_regionviews.empty()) {
2534 /* something went wrong */
2538 /* we need to deselect all other regionviews, and select this one
2539 i'm ignoring undo stuff, because the region creation will take care of it
2541 selection->set (latest_regionviews);
2543 _drags->set (new RegionMoveDrag (this, latest_regionviews.front()->get_canvas_group(), latest_regionviews.front(), latest_regionviews, false, false), event);
2549 if (_drags->active ()) {
2552 selection->clear ();
2557 Editor::set_internal_edit (bool yn)
2559 if (_internal_editing == yn) {
2563 _internal_editing = yn;
2566 pre_internal_mouse_mode = mouse_mode;
2567 pre_internal_snap_type = _snap_type;
2568 pre_internal_snap_mode = _snap_mode;
2570 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2571 (*i)->enter_internal_edit_mode ();
2574 set_snap_to (internal_snap_type);
2575 set_snap_mode (internal_snap_mode);
2579 internal_snap_mode = _snap_mode;
2580 internal_snap_type = _snap_type;
2582 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2583 (*i)->leave_internal_edit_mode ();
2586 if (mouse_mode == MouseDraw && pre_internal_mouse_mode != MouseDraw) {
2587 /* we were drawing .. flip back to something sensible */
2588 set_mouse_mode (pre_internal_mouse_mode);
2591 set_snap_to (pre_internal_snap_type);
2592 set_snap_mode (pre_internal_snap_mode);
2595 reset_canvas_cursor ();
2598 /** Update _join_object_range_state which indicate whether we are over the top
2599 * or bottom half of a route view, used by the `join object/range' tool
2600 * mode. Coordinates in canvas space.
2603 Editor::update_join_object_range_location (double y)
2605 if (_internal_editing || !get_smart_mode()) {
2606 _join_object_range_state = JOIN_OBJECT_RANGE_NONE;
2610 JoinObjectRangeState const old = _join_object_range_state;
2612 if (mouse_mode == MouseObject) {
2613 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2614 } else if (mouse_mode == MouseRange) {
2615 _join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
2618 if (entered_regionview) {
2620 ArdourCanvas::Duple const item_space = entered_regionview->get_canvas_group()->canvas_to_item (ArdourCanvas::Duple (0, y));
2621 double const c = item_space.y / entered_regionview->height();
2623 _join_object_range_state = c <= 0.5 ? JOIN_OBJECT_RANGE_RANGE : JOIN_OBJECT_RANGE_OBJECT;
2625 if (_join_object_range_state != old) {
2626 set_canvas_cursor (which_track_cursor ());
2629 } else if (entered_track) {
2631 RouteTimeAxisView* entered_route_view = dynamic_cast<RouteTimeAxisView*> (entered_track);
2633 if (entered_route_view) {
2638 entered_route_view->canvas_display()->canvas_to_item (cx, cy);
2640 double track_height = entered_route_view->view()->child_height();
2641 if (Config->get_show_name_highlight()) {
2642 track_height -= TimeAxisViewItem::NAME_HIGHLIGHT_SIZE;
2644 double const c = cy / track_height;
2648 _join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
2650 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2654 /* Other kinds of tracks use object mode */
2655 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2658 if (_join_object_range_state != old) {
2659 set_canvas_cursor (which_track_cursor ());
2665 Editor::effective_mouse_mode () const
2667 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
2669 } else if (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE) {
2677 Editor::remove_midi_note (ArdourCanvas::Item* item, GdkEvent *)
2679 NoteBase* e = reinterpret_cast<NoteBase*> (item->get_data ("notebase"));
2682 e->region_view().delete_note (e->note ());
2685 /** Obtain the pointer position in canvas coordinates */
2687 Editor::get_pointer_position (double& x, double& y) const
2690 _track_canvas->get_pointer (px, py);
2691 _track_canvas->window_to_canvas (px, py, x, y);