2 Copyright (C) 2000-2001 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
29 #include "pbd/error.h"
30 #include "pbd/enumwriter.h"
31 #include "pbd/memento_command.h"
32 #include "pbd/basename.h"
33 #include "pbd/stateful_diff_command.h"
35 #include "gtkmm2ext/bindings.h"
36 #include "gtkmm2ext/utils.h"
37 #include "gtkmm2ext/tearoff.h"
39 #include "canvas/canvas.h"
41 #include "ardour/audioregion.h"
42 #include "ardour/operations.h"
43 #include "ardour/playlist.h"
44 #include "ardour/profile.h"
45 #include "ardour/region_factory.h"
46 #include "ardour/route.h"
47 #include "ardour/session.h"
48 #include "ardour/types.h"
50 #include "ardour_ui.h"
53 #include "time_axis_view.h"
54 #include "audio_time_axis.h"
55 #include "audio_region_view.h"
56 #include "midi_region_view.h"
58 #include "streamview.h"
59 #include "region_gain_line.h"
60 #include "automation_time_axis.h"
61 #include "control_point.h"
63 #include "selection.h"
66 #include "rgb_macros.h"
67 #include "control_point_dialog.h"
68 #include "editor_drag.h"
69 #include "automation_region_view.h"
70 #include "edit_note_dialog.h"
71 #include "mouse_cursors.h"
72 #include "editor_cursors.h"
73 #include "verbose_cursor.h"
79 using namespace ARDOUR;
82 using namespace Editing;
83 using Gtkmm2ext::Keyboard;
86 Editor::mouse_frame (framepos_t& where, bool& in_track_canvas) const
88 /* gdk_window_get_pointer() has X11's XQueryPointer semantics in that it only
89 pays attentions to subwindows. this means that menu windows are ignored, and
90 if the pointer is in a menu, the return window from the call will be the
91 the regular subwindow *under* the menu.
93 this matters quite a lot if the pointer is moving around in a menu that overlaps
94 the track canvas because we will believe that we are within the track canvas
95 when we are not. therefore, we track enter/leave events for the track canvas
96 and allow that to override the result of gdk_window_get_pointer().
99 if (!within_track_canvas) {
104 Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->_track_canvas->get_window();
106 if (!canvas_window) {
110 Glib::RefPtr<const Gdk::Window> pointer_window = Gdk::Display::get_default()->get_window_at_pointer (x, y);
112 if (!pointer_window) {
116 if (pointer_window != canvas_window) {
117 in_track_canvas = false;
121 in_track_canvas = true;
124 event.type = GDK_BUTTON_RELEASE;
128 where = window_event_sample (&event, 0, 0);
134 Editor::window_event_sample (GdkEvent const * event, double* pcx, double* pcy) const
136 ArdourCanvas::Duple d;
138 if (!gdk_event_get_coords (event, &d.x, &d.y)) {
142 /* event coordinates are in window units, so convert to canvas
145 d = _track_canvas->window_to_canvas (d);
155 return pixel_to_sample (d.x);
159 Editor::canvas_event_sample (GdkEvent const * event, double* pcx, double* pcy) const
164 /* event coordinates are already in canvas units */
166 if (!gdk_event_get_coords (event, &x, &y)) {
167 cerr << "!NO c COORDS for event type " << event->type << endl;
179 /* note that pixel_to_sample_from_event() never returns less than zero, so even if the pixel
180 position is negative (as can be the case with motion events in particular),
181 the frame location is always positive.
184 return pixel_to_sample_from_event (x);
188 Editor::set_current_trimmable (boost::shared_ptr<Trimmable> t)
190 boost::shared_ptr<Trimmable> st = _trimmable.lock();
192 if (!st || st == t) {
198 Editor::set_current_movable (boost::shared_ptr<Movable> m)
200 boost::shared_ptr<Movable> sm = _movable.lock();
202 if (!sm || sm != m) {
208 Editor::mouse_mode_object_range_toggled()
210 MouseMode m = mouse_mode;
212 Glib::RefPtr<Action> act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-range"));
214 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
217 if (tact->get_active()) {
218 m = MouseObject; //Smart mode turned to ON, force editing to Object mode
221 set_mouse_mode(m, true); //call this so the button styles can get updated
225 Editor::set_mouse_mode (MouseMode m, bool force)
227 if (_drags->active ()) {
231 if (!force && m == mouse_mode) {
235 if (ARDOUR::Profile->get_mixbus()) {
236 if ( m == MouseCut) m = MouseObject;
239 Glib::RefPtr<Action> act;
243 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-range"));
247 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-cut"));
251 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-object"));
255 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-draw"));
259 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-gain"));
263 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-timefx"));
267 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-audition"));
273 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
276 /* go there and back to ensure that the toggled handler is called to set up mouse_mode */
277 tact->set_active (false);
278 tact->set_active (true);
280 //NOTE: this will result in a call to mouse_mode_toggled which does the heavy lifting
284 Editor::mouse_mode_toggled (MouseMode m)
286 Glib::RefPtr<Action> act;
287 Glib::RefPtr<ToggleAction> tact;
289 if (ARDOUR::Profile->get_mixbus()) {
290 if ( m == MouseCut) m = MouseObject;
295 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-range"));
299 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-object"));
303 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-cut"));
307 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-draw"));
311 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-gain"));
315 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-timefx"));
319 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-audition"));
325 tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
328 if (!tact->get_active()) {
329 /* this was just the notification that the old mode has been
330 * left. we'll get called again with the new mode active in a
338 act = ActionManager::get_action (X_("MouseMode"), X_("toggle-internal-edit"));
339 tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
340 tact->set_active (true);
346 if (_session && mouse_mode == MouseAudition) {
347 /* stop transport and reset default speed to avoid oddness with
349 _session->request_transport_speed (0.0, true);
356 /* this should generate a new enter event which will
357 trigger the appropiate cursor.
361 _track_canvas->re_enter ();
364 set_gain_envelope_visibility ();
366 update_time_selection_display ();
368 MouseModeChanged (); /* EMIT SIGNAL */
372 Editor::update_time_selection_display ()
374 if (smart_mode_action->get_active()) {
375 /* not sure what to do here */
376 if (mouse_mode == MouseObject) {
380 switch (mouse_mode) {
382 selection->clear_objects ();
385 selection->clear_time ();
392 Editor::step_mouse_mode (bool next)
394 switch (current_mouse_mode()) {
397 set_mouse_mode (MouseRange);
399 set_mouse_mode (MouseTimeFX);
404 if (next) set_mouse_mode (MouseDraw);
405 else set_mouse_mode (MouseCut);
409 if (next) set_mouse_mode (MouseRange);
410 else set_mouse_mode (MouseDraw);
414 if (next) set_mouse_mode (MouseCut);
415 else set_mouse_mode (MouseRange);
419 if (next) set_mouse_mode (MouseTimeFX);
420 else set_mouse_mode (MouseDraw);
425 set_mouse_mode (MouseAudition);
427 set_mouse_mode (MouseGain);
432 if (next) set_mouse_mode (MouseObject);
433 else set_mouse_mode (MouseTimeFX);
439 Editor::toggle_internal_editing_from_double_click (GdkEvent* event)
441 if (_drags->active()) {
442 _drags->end_grab (event);
445 ActionManager::do_action ("MouseMode", "toggle-internal-edit");
447 /* prevent reversion of edit cursor on button release */
449 pre_press_cursor = 0;
455 Editor::button_selection (ArdourCanvas::Item* /*item*/, GdkEvent* event, ItemType item_type)
457 /* in object/audition/timefx/gain-automation mode,
458 any button press sets the selection if the object
459 can be selected. this is a bit of hack, because
460 we want to avoid this if the mouse operation is a
463 note: not dbl-click or triple-click
465 Also note that there is no region selection in internal edit mode, otherwise
466 for operations operating on the selection (e.g. cut) it is not obvious whether
467 to cut notes or regions.
470 MouseMode eff_mouse_mode = effective_mouse_mode ();
472 if (eff_mouse_mode == MouseCut) {
473 /* never change selection in cut mode */
477 if (get_smart_mode() && eff_mouse_mode == MouseRange && event->button.button == 3 && item_type == RegionItem) {
478 /* context clicks are always about object properties, even if
479 we're in range mode within smart mode.
481 eff_mouse_mode = MouseObject;
484 /* special case: allow drag of region fade in/out in object mode with join object/range enabled */
485 if (get_smart_mode()) {
487 case FadeInHandleItem:
488 case FadeInTrimHandleItem:
489 case FadeOutHandleItem:
490 case FadeOutTrimHandleItem:
491 eff_mouse_mode = MouseObject;
498 if (((mouse_mode != MouseObject) &&
499 (mouse_mode != MouseAudition || item_type != RegionItem) &&
500 (mouse_mode != MouseTimeFX || item_type != RegionItem) &&
501 (mouse_mode != MouseGain) &&
502 (mouse_mode != MouseDraw)) ||
503 ((event->type != GDK_BUTTON_PRESS && event->type != GDK_BUTTON_RELEASE) || event->button.button > 3) ||
504 (internal_editing() && mouse_mode != MouseTimeFX)) {
509 if (event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) {
511 if ((event->button.state & Keyboard::RelevantModifierKeyMask) && event->button.button != 1) {
513 /* almost no selection action on modified button-2 or button-3 events */
515 if (item_type != RegionItem && event->button.button != 2) {
521 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
522 bool press = (event->type == GDK_BUTTON_PRESS);
527 if (eff_mouse_mode != MouseRange) {
528 set_selected_regionview_from_click (press, op);
530 /* don't change the selection unless the
531 clicked track is not currently selected. if
532 so, "collapse" the selection to just this
535 if (!selection->selected (clicked_axisview)) {
536 set_selected_track_as_side_effect (Selection::Set);
540 if (eff_mouse_mode != MouseRange) {
541 set_selected_regionview_from_click (press, op);
546 case RegionViewNameHighlight:
548 case LeftFrameHandle:
549 case RightFrameHandle:
550 case FadeInHandleItem:
551 case FadeInTrimHandleItem:
553 case FadeOutHandleItem:
554 case FadeOutTrimHandleItem:
556 case StartCrossFadeItem:
557 case EndCrossFadeItem:
558 if (get_smart_mode() || eff_mouse_mode != MouseRange) {
559 set_selected_regionview_from_click (press, op);
560 } else if (event->type == GDK_BUTTON_PRESS) {
561 set_selected_track_as_side_effect (op);
565 case ControlPointItem:
566 set_selected_track_as_side_effect (op);
567 if (eff_mouse_mode != MouseRange) {
568 set_selected_control_point_from_click (press, op);
573 /* for context click, select track */
574 if (event->button.button == 3) {
575 selection->clear_tracks ();
576 set_selected_track_as_side_effect (op);
580 case AutomationTrackItem:
581 set_selected_track_as_side_effect (op);
590 Editor::button_press_handler_1 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
592 /* single mouse clicks on any of these item types operate
593 independent of mouse mode, mostly because they are
594 not on the main track canvas or because we want
599 case PlayheadCursorItem:
600 _drags->set (new CursorDrag (this, *playhead_cursor, true), event);
604 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
605 hide_marker (item, event);
607 _drags->set (new MarkerDrag (this, item), event);
611 case TempoMarkerItem:
613 TempoMarker* m = reinterpret_cast<TempoMarker*> (item->get_data ("marker"));
616 new TempoMarkerDrag (
619 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
626 case MeterMarkerItem:
628 MeterMarker* m = reinterpret_cast<MeterMarker*> (item->get_data ("marker"));
631 new MeterMarkerDrag (
634 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
642 _drags->set (new VideoTimeLineDrag (this, item), event);
649 case TimecodeRulerItem:
650 case SamplesRulerItem:
651 case MinsecRulerItem:
653 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
654 _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
660 case RangeMarkerBarItem:
661 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier)) {
662 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateSkipMarker), event);
663 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
664 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateRangeMarker), event);
666 _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
671 case CdMarkerBarItem:
672 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
673 _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
675 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateCDMarker), event);
680 case TransportMarkerBarItem:
681 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
682 _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
684 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateTransportMarker), event);
693 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
694 /* special case: allow trim of range selections in joined object mode;
695 in theory eff should equal MouseRange in this case, but it doesn't
696 because entering the range selection canvas item results in entered_regionview
697 being set to 0, so update_join_object_range_location acts as if we aren't
700 if (item_type == StartSelectionTrimItem) {
701 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
702 } else if (item_type == EndSelectionTrimItem) {
703 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
707 Editing::MouseMode eff = effective_mouse_mode ();
709 /* special case: allow drag of region fade in/out in object mode with join object/range enabled */
710 if (get_smart_mode()) {
712 case FadeInHandleItem:
713 case FadeInTrimHandleItem:
714 case FadeOutHandleItem:
715 case FadeOutTrimHandleItem:
723 /* there is no Range mode when in internal edit mode */
724 if (eff == MouseRange && internal_editing()) {
731 case StartSelectionTrimItem:
732 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
735 case EndSelectionTrimItem:
736 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
740 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
741 start_selection_grab (item, event);
743 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
744 /* grab selection for moving */
745 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionMove), event);
747 /* this was debated, but decided the more common action was to
748 make a new selection */
749 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
754 if (internal_editing()) {
755 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
756 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
760 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::RangeSelectModifier)) {
761 cerr << "extend selection\n";
762 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionExtend), event);
764 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
770 case RegionViewNameHighlight:
771 if (!clicked_regionview->region()->locked()) {
772 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
778 if (!internal_editing()) {
779 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::RangeSelectModifier)) {
780 cerr << "extend selection 2\n";
781 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionExtend), event);
783 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
793 /* Existing note: allow trimming/motion */
794 if (internal_editing()) {
795 /* trim notes if we're in internal edit mode and near the ends of the note */
796 NoteBase* cn = reinterpret_cast<NoteBase*>(item->get_data ("notebase"));
798 if (cn->big_enough_to_trim() && cn->mouse_near_ends()) {
799 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
801 _drags->set (new NoteDrag (this, item), event);
807 if (internal_editing()) {
808 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
809 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
823 case FadeInHandleItem:
824 case FadeOutHandleItem:
825 case LeftFrameHandle:
826 case RightFrameHandle:
827 case FeatureLineItem:
828 case RegionViewNameHighlight:
831 case AutomationTrackItem:
832 _drags->set (new RegionCutDrag (this, item, canvas_event_sample (event)), event, current_canvas_cursor);
843 /* Existing note: allow trimming/motion */
844 if (internal_editing()) {
845 NoteBase* cn = reinterpret_cast<NoteBase*> (item->get_data ("notebase"));
847 if (cn->big_enough_to_trim() && cn->mouse_near_ends()) {
848 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
850 _drags->set (new NoteDrag (this, item), event);
860 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)) &&
861 event->type == GDK_BUTTON_PRESS) {
863 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
865 } else if (event->type == GDK_BUTTON_PRESS) {
868 case FadeInHandleItem:
870 _drags->set (new FadeInDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), selection->regions), event, _cursors->fade_in);
874 case FadeOutHandleItem:
876 _drags->set (new FadeOutDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), selection->regions), event, _cursors->fade_out);
880 case StartCrossFadeItem:
881 case EndCrossFadeItem:
882 /* we might allow user to grab inside the fade to trim a region with preserve_fade_anchor. for not this is not fully implemented */
883 // if (!clicked_regionview->region()->locked()) {
884 // _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer(), true), event);
889 case FeatureLineItem:
891 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier)) {
892 remove_transient(item);
896 _drags->set (new FeatureLineDrag (this, item), event);
902 if (dynamic_cast<AutomationRegionView*> (clicked_regionview)) {
903 /* click on an automation region view; do nothing here and let the ARV's signal handler
909 if (internal_editing ()) {
913 /* click on a normal region view */
914 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
915 add_region_copy_drag (item, event, clicked_regionview);
916 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
917 add_region_brush_drag (item, event, clicked_regionview);
919 add_region_drag (item, event, clicked_regionview);
923 // if (!internal_editing() && (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE && !selection->regions.empty())) {
924 // _drags->add (new SelectionDrag (this, clicked_axisview->get_selection_rect (clicked_selection)->rect, SelectionDrag::SelectionMove));
927 _drags->start_grab (event);
931 case RegionViewNameHighlight:
932 case LeftFrameHandle:
933 case RightFrameHandle:
934 if (!clicked_regionview->region()->locked()) {
935 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
940 case FadeInTrimHandleItem:
941 case FadeOutTrimHandleItem:
942 if (!clicked_regionview->region()->locked()) {
943 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer(), true), event);
950 /* rename happens on edit clicks */
951 if (clicked_regionview->get_name_highlight()) {
952 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer()), event);
958 case ControlPointItem:
959 _drags->set (new ControlPointDrag (this, item), event);
963 case AutomationLineItem:
964 _drags->set (new LineDrag (this, item), event);
969 if (internal_editing()) {
970 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
971 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
975 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
979 case AutomationTrackItem:
981 TimeAxisView* parent = clicked_axisview->get_parent ();
982 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (clicked_axisview);
984 if (parent && dynamic_cast<MidiTimeAxisView*> (parent) && atv->show_regions ()) {
986 RouteTimeAxisView* p = dynamic_cast<RouteTimeAxisView*> (parent);
988 boost::shared_ptr<Playlist> pl = p->track()->playlist ();
989 if (pl->n_regions() == 0) {
990 /* Parent has no regions; create one so that we have somewhere to put automation */
991 _drags->set (new RegionCreateDrag (this, item, parent), event);
993 /* See if there's a region before the click that we can extend, and extend it if so */
994 framepos_t const t = canvas_event_sample (event);
995 boost::shared_ptr<Region> prev = pl->find_next_region (t, End, -1);
997 _drags->set (new RegionCreateDrag (this, item, parent), event);
999 prev->set_length (t - prev->position ());
1003 /* rubberband drag to select automation points */
1004 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
1026 switch (item_type) {
1028 _drags->set (new LineDrag (this, item), event);
1031 case ControlPointItem:
1032 _drags->set (new ControlPointDrag (this, item), event);
1038 AudioRegionView* arv = dynamic_cast<AudioRegionView *> (clicked_regionview);
1040 _drags->set (new AutomationRangeDrag (this, arv, selection->time), event, _cursors->up_down);
1042 double const y = event->button.y;
1043 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
1045 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
1047 /* smart "join" mode: drag automation */
1048 _drags->set (new AutomationRangeDrag (this, atv, selection->time), event, _cursors->up_down);
1056 case AutomationLineItem:
1057 _drags->set (new LineDrag (this, item), event);
1067 if (internal_editing() && item_type == NoteItem ) {
1068 /* drag notes if we're in internal edit mode */
1069 NoteBase* cn = reinterpret_cast<NoteBase*>(item->get_data ("notebase"));
1071 if (cn->big_enough_to_trim()) {
1072 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
1075 } else if (clicked_regionview) {
1077 _drags->set (new TimeFXDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1083 _drags->set (new ScrubDrag (this, item), event);
1084 scrub_reversals = 0;
1085 scrub_reverse_distance = 0;
1086 last_scrub_x = event->button.x;
1087 scrubbing_direction = 0;
1088 push_canvas_cursor (_cursors->transparent);
1100 Editor::button_press_handler_2 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1102 Editing::MouseMode const eff = effective_mouse_mode ();
1105 switch (item_type) {
1107 if (internal_editing ()) {
1108 /* no region drags in internal edit mode */
1112 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
1113 add_region_copy_drag (item, event, clicked_regionview);
1115 add_region_drag (item, event, clicked_regionview);
1117 _drags->start_grab (event);
1120 case ControlPointItem:
1121 _drags->set (new ControlPointDrag (this, item), event);
1129 switch (item_type) {
1130 case RegionViewNameHighlight:
1131 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1135 case LeftFrameHandle:
1136 case RightFrameHandle:
1137 if (!internal_editing ()) {
1138 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1143 case RegionViewName:
1144 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer()), event);
1158 /* relax till release */
1170 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1172 if (event->type == GDK_2BUTTON_PRESS) {
1173 _drags->mark_double_click ();
1174 gdk_pointer_ungrab (GDK_CURRENT_TIME);
1178 if (event->type != GDK_BUTTON_PRESS) {
1182 pre_press_cursor = current_canvas_cursor;
1184 _track_canvas->grab_focus();
1186 if (_session && _session->actively_recording()) {
1190 if (internal_editing()) {
1191 bool leave_internal_edit_mode = false;
1193 switch (item_type) {
1198 if (!dynamic_cast<MidiRegionView*> (clicked_regionview) && !dynamic_cast<AutomationRegionView*> (clicked_regionview)) {
1199 leave_internal_edit_mode = true;
1203 case PlayheadCursorItem:
1205 case TempoMarkerItem:
1206 case MeterMarkerItem:
1210 case RangeMarkerBarItem:
1211 case CdMarkerBarItem:
1212 case TransportMarkerBarItem:
1214 case TimecodeRulerItem:
1215 case SamplesRulerItem:
1216 case MinsecRulerItem:
1218 /* button press on these items never does anything to
1219 change the editing mode.
1227 if (leave_internal_edit_mode) {
1228 ActionManager::do_action ("MouseMode", "toggle-internal-edit");
1232 button_selection (item, event, item_type);
1234 if (!_drags->active () &&
1235 (Keyboard::is_delete_event (&event->button) ||
1236 Keyboard::is_context_menu_event (&event->button) ||
1237 Keyboard::is_edit_event (&event->button))) {
1239 /* handled by button release */
1243 //not rolling, range mode click + join_play_range : locate the PH here
1244 if ( !_drags->active () && !_session->transport_rolling() && ( effective_mouse_mode() == MouseRange ) && Config->get_follow_edits() ) {
1245 framepos_t where = canvas_event_sample (event);
1247 _session->request_locate (where, false);
1250 switch (event->button.button) {
1252 return button_press_handler_1 (item, event, item_type);
1256 return button_press_handler_2 (item, event, item_type);
1263 return button_press_dispatch (&event->button);
1272 Editor::button_press_dispatch (GdkEventButton* ev)
1274 /* this function is intended only for buttons 4 and above.
1277 Gtkmm2ext::MouseButton b (ev->state, ev->button);
1278 return button_bindings->activate (b, Gtkmm2ext::Bindings::Press);
1282 Editor::button_release_dispatch (GdkEventButton* ev)
1284 /* this function is intended only for buttons 4 and above.
1287 Gtkmm2ext::MouseButton b (ev->state, ev->button);
1288 return button_bindings->activate (b, Gtkmm2ext::Bindings::Release);
1292 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1294 framepos_t where = canvas_event_sample (event);
1295 AutomationTimeAxisView* atv = 0;
1297 if (pre_press_cursor) {
1298 set_canvas_cursor (pre_press_cursor);
1299 pre_press_cursor = 0;
1302 /* no action if we're recording */
1304 if (_session && _session->actively_recording()) {
1308 /* see if we're finishing a drag */
1310 bool were_dragging = false;
1311 if (_drags->active ()) {
1312 bool const r = _drags->end_grab (event);
1314 /* grab dragged, so do nothing else */
1318 were_dragging = true;
1321 update_region_layering_order_editor ();
1323 /* edit events get handled here */
1325 if (!_drags->active () && Keyboard::is_edit_event (&event->button)) {
1326 switch (item_type) {
1328 show_region_properties ();
1331 case TempoMarkerItem: {
1333 TempoMarker* tempo_marker;
1335 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
1336 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
1337 abort(); /*NOTREACHED*/
1340 if ((tempo_marker = dynamic_cast<TempoMarker*> (marker)) == 0) {
1341 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
1342 abort(); /*NOTREACHED*/
1345 edit_tempo_marker (*tempo_marker);
1349 case MeterMarkerItem: {
1351 MeterMarker* meter_marker;
1353 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
1354 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
1355 abort(); /*NOTREACHED*/
1358 if ((meter_marker = dynamic_cast<MeterMarker*> (marker)) == 0) {
1359 fatal << _("programming error: marker for meter is not a meter marker!") << endmsg;
1360 abort(); /*NOTREACHED*/
1362 edit_meter_marker (*meter_marker);
1366 case RegionViewName:
1367 if (clicked_regionview->name_active()) {
1368 return mouse_rename_region (item, event);
1372 case ControlPointItem:
1373 edit_control_point (item);
1382 /* context menu events get handled here */
1383 if (Keyboard::is_context_menu_event (&event->button)) {
1385 context_click_event = *event;
1387 if (!_drags->active ()) {
1389 /* no matter which button pops up the context menu, tell the menu
1390 widget to use button 1 to drive menu selection.
1393 switch (item_type) {
1395 case FadeInHandleItem:
1396 case FadeInTrimHandleItem:
1397 case StartCrossFadeItem:
1398 popup_xfade_in_context_menu (1, event->button.time, item, item_type);
1402 case FadeOutHandleItem:
1403 case FadeOutTrimHandleItem:
1404 case EndCrossFadeItem:
1405 popup_xfade_out_context_menu (1, event->button.time, item, item_type);
1408 case LeftFrameHandle:
1409 case RightFrameHandle:
1413 popup_track_context_menu (1, event->button.time, item_type, false);
1417 case RegionViewNameHighlight:
1418 case RegionViewName:
1419 popup_track_context_menu (1, event->button.time, item_type, false);
1423 popup_track_context_menu (1, event->button.time, item_type, true);
1426 case AutomationTrackItem:
1427 popup_track_context_menu (1, event->button.time, item_type, false);
1431 case RangeMarkerBarItem:
1432 case TransportMarkerBarItem:
1433 case CdMarkerBarItem:
1437 case TimecodeRulerItem:
1438 case SamplesRulerItem:
1439 case MinsecRulerItem:
1441 popup_ruler_menu (where, item_type);
1445 marker_context_menu (&event->button, item);
1448 case TempoMarkerItem:
1449 tempo_or_meter_marker_context_menu (&event->button, item);
1452 case MeterMarkerItem:
1453 tempo_or_meter_marker_context_menu (&event->button, item);
1456 case CrossfadeViewItem:
1457 popup_track_context_menu (1, event->button.time, item_type, false);
1460 case ControlPointItem:
1461 popup_control_point_context_menu (item, event);
1472 /* delete events get handled here */
1474 Editing::MouseMode const eff = effective_mouse_mode ();
1476 if (!_drags->active () && Keyboard::is_delete_event (&event->button)) {
1478 switch (item_type) {
1479 case TempoMarkerItem:
1480 remove_tempo_marker (item);
1483 case MeterMarkerItem:
1484 remove_meter_marker (item);
1488 remove_marker (*item, event);
1492 if (eff == MouseObject) {
1493 remove_clicked_region ();
1497 case ControlPointItem:
1498 remove_control_point (item);
1502 remove_midi_note (item, event);
1511 switch (event->button.button) {
1514 switch (item_type) {
1515 /* see comments in button_press_handler */
1516 case PlayheadCursorItem:
1519 case AutomationLineItem:
1520 case StartSelectionTrimItem:
1521 case EndSelectionTrimItem:
1525 if (!_dragging_playhead) {
1526 snap_to_with_modifier (where, event, RoundNearest, true);
1527 mouse_add_new_marker (where);
1531 case CdMarkerBarItem:
1532 if (!_dragging_playhead) {
1533 // if we get here then a dragged range wasn't done
1534 snap_to_with_modifier (where, event, RoundNearest, true);
1535 mouse_add_new_marker (where, true);
1540 if (!_dragging_playhead) {
1541 snap_to_with_modifier (where, event);
1542 mouse_add_new_tempo_event (where);
1547 if (!_dragging_playhead) {
1548 mouse_add_new_meter_event (pixel_to_sample (event->button.x));
1553 case TimecodeRulerItem:
1554 case SamplesRulerItem:
1555 case MinsecRulerItem:
1566 switch (item_type) {
1567 case AutomationTrackItem:
1568 atv = dynamic_cast<AutomationTimeAxisView*>(clicked_axisview);
1570 bool with_guard_points = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
1571 atv->add_automation_event (event, where, event->button.y, with_guard_points);
1581 switch (item_type) {
1584 /* check that we didn't drag before releasing, since
1585 its really annoying to create new control
1586 points when doing this.
1588 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (clicked_regionview);
1589 if (!were_dragging && arv) {
1590 bool with_guard_points = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
1591 arv->add_gain_point_event (item, event, with_guard_points);
1597 case AutomationTrackItem: {
1598 bool with_guard_points = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
1599 dynamic_cast<AutomationTimeAxisView*>(clicked_axisview)->
1600 add_automation_event (event, where, event->button.y, with_guard_points);
1610 pop_canvas_cursor ();
1611 if (scrubbing_direction == 0) {
1612 /* no drag, just a click */
1613 switch (item_type) {
1615 play_selected_region ();
1621 /* make sure we stop */
1622 _session->request_transport_speed (0.0);
1631 /* do any (de)selection operations that should occur on button release */
1632 button_selection (item, event, item_type);
1641 switch (item_type) {
1643 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1645 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier|Keyboard::SecondaryModifier))) {
1648 // Button2 click is unused
1663 // x_style_paste (where, 1.0);
1684 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1691 /* by the time we reach here, entered_regionview and entered trackview
1692 * will have already been set as appropriate. Things are done this
1693 * way because this method isn't passed a pointer to a variable type of
1694 * thing that is entered (which may or may not be canvas item).
1695 * (e.g. the actual entered regionview)
1698 choose_canvas_cursor_on_entry (&event->crossing, item_type);
1700 switch (item_type) {
1701 case ControlPointItem:
1702 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1703 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1706 fraction = 1.0 - (cp->get_y() / cp->line().height());
1708 _verbose_cursor->set (cp->line().get_verbose_cursor_string (fraction));
1709 _verbose_cursor->show ();
1714 if (mouse_mode == MouseGain) {
1715 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1717 line->set_outline_color (ARDOUR_UI::config()->get_EnteredGainLine());
1722 case AutomationLineItem:
1723 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1724 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1726 line->set_outline_color (ARDOUR_UI::config()->get_EnteredAutomationLine());
1731 case AutomationTrackItem:
1732 AutomationTimeAxisView* atv;
1733 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1734 clear_entered_track = false;
1735 set_entered_track (atv);
1740 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1743 entered_marker = marker;
1744 marker->set_color_rgba (ARDOUR_UI::config()->get_EnteredMarker());
1746 case MeterMarkerItem:
1747 case TempoMarkerItem:
1750 case FadeInHandleItem:
1751 case FadeInTrimHandleItem:
1752 if (mouse_mode == MouseObject && !internal_editing()) {
1753 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
1755 RegionView* rv = static_cast<RegionView*>(item->get_data ("regionview"));
1756 rect->set_fill_color (rv->get_fill_color());
1761 case FadeOutHandleItem:
1762 case FadeOutTrimHandleItem:
1763 if (mouse_mode == MouseObject && !internal_editing()) {
1764 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
1766 RegionView* rv = static_cast<RegionView*>(item->get_data ("regionview"));
1767 rect->set_fill_color (rv->get_fill_color ());
1772 case FeatureLineItem:
1774 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1775 line->set_outline_color (0xFF0000FF);
1786 /* third pass to handle entered track status in a comprehensible way.
1789 switch (item_type) {
1791 case AutomationLineItem:
1792 case ControlPointItem:
1793 /* these do not affect the current entered track state */
1794 clear_entered_track = false;
1797 case AutomationTrackItem:
1798 /* handled above already */
1810 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent*, ItemType item_type)
1818 reset_canvas_cursor ();
1820 switch (item_type) {
1821 case ControlPointItem:
1822 _verbose_cursor->hide ();
1826 case AutomationLineItem:
1827 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1829 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1831 line->set_outline_color (al->get_line_color());
1837 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1841 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
1842 location_flags_changed (loc);
1845 case MeterMarkerItem:
1846 case TempoMarkerItem:
1849 case FadeInTrimHandleItem:
1850 case FadeOutTrimHandleItem:
1851 case FadeInHandleItem:
1852 case FadeOutHandleItem:
1854 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
1856 rect->set_fill_color (ARDOUR_UI::config()->get_InactiveFadeHandle());
1861 case AutomationTrackItem:
1864 case FeatureLineItem:
1866 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1867 line->set_outline_color (ARDOUR_UI::config()->get_ZeroLine());
1879 Editor::scrub (framepos_t frame, double current_x)
1883 if (scrubbing_direction == 0) {
1885 _session->request_locate (frame, false);
1886 _session->request_transport_speed (0.1);
1887 scrubbing_direction = 1;
1891 if (last_scrub_x > current_x) {
1893 /* pointer moved to the left */
1895 if (scrubbing_direction > 0) {
1897 /* we reversed direction to go backwards */
1900 scrub_reverse_distance += (int) (last_scrub_x - current_x);
1904 /* still moving to the left (backwards) */
1906 scrub_reversals = 0;
1907 scrub_reverse_distance = 0;
1909 delta = 0.01 * (last_scrub_x - current_x);
1910 _session->request_transport_speed_nonzero (_session->transport_speed() - delta);
1914 /* pointer moved to the right */
1916 if (scrubbing_direction < 0) {
1917 /* we reversed direction to go forward */
1920 scrub_reverse_distance += (int) (current_x - last_scrub_x);
1923 /* still moving to the right */
1925 scrub_reversals = 0;
1926 scrub_reverse_distance = 0;
1928 delta = 0.01 * (current_x - last_scrub_x);
1929 _session->request_transport_speed_nonzero (_session->transport_speed() + delta);
1933 /* if there have been more than 2 opposite motion moves detected, or one that moves
1934 back more than 10 pixels, reverse direction
1937 if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
1939 if (scrubbing_direction > 0) {
1940 /* was forwards, go backwards */
1941 _session->request_transport_speed (-0.1);
1942 scrubbing_direction = -1;
1944 /* was backwards, go forwards */
1945 _session->request_transport_speed (0.1);
1946 scrubbing_direction = 1;
1949 scrub_reverse_distance = 0;
1950 scrub_reversals = 0;
1954 last_scrub_x = current_x;
1958 Editor::motion_handler (ArdourCanvas::Item* /*item*/, GdkEvent* event, bool from_autoscroll)
1960 _last_motion_y = event->motion.y;
1962 if (event->motion.is_hint) {
1965 /* We call this so that MOTION_NOTIFY events continue to be
1966 delivered to the canvas. We need to do this because we set
1967 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
1968 the density of the events, at the expense of a round-trip
1969 to the server. Given that this will mostly occur on cases
1970 where DISPLAY = :0.0, and given the cost of what the motion
1971 event might do, its a good tradeoff.
1974 _track_canvas->get_pointer (x, y);
1977 if (current_stepping_trackview) {
1978 /* don't keep the persistent stepped trackview if the mouse moves */
1979 current_stepping_trackview = 0;
1980 step_timeout.disconnect ();
1983 if (_session && _session->actively_recording()) {
1984 /* Sorry. no dragging stuff around while we record */
1988 update_join_object_range_location (event->motion.y);
1990 if (_drags->active ()) {
1991 return _drags->motion_handler (event, from_autoscroll);
1998 Editor::can_remove_control_point (ArdourCanvas::Item* item)
2000 ControlPoint* control_point;
2002 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2003 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2004 abort(); /*NOTREACHED*/
2007 AutomationLine& line = control_point->line ();
2008 if (dynamic_cast<AudioRegionGainLine*> (&line)) {
2009 /* we shouldn't remove the first or last gain point in region gain lines */
2010 if (line.is_last_point(*control_point) || line.is_first_point(*control_point)) {
2019 Editor::remove_control_point (ArdourCanvas::Item* item)
2021 if (!can_remove_control_point (item)) {
2025 ControlPoint* control_point;
2027 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2028 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2029 abort(); /*NOTREACHED*/
2032 control_point->line().remove_point (*control_point);
2036 Editor::edit_control_point (ArdourCanvas::Item* item)
2038 ControlPoint* p = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"));
2041 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2042 abort(); /*NOTREACHED*/
2045 ControlPointDialog d (p);
2048 if (d.run () != RESPONSE_ACCEPT) {
2052 p->line().modify_point_y (*p, d.get_y_fraction ());
2056 Editor::edit_notes (TimeAxisViewItem& tavi)
2058 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(&tavi);
2064 MidiRegionView::Selection const & s = mrv->selection();
2070 EditNoteDialog* d = new EditNoteDialog (&(*s.begin())->region_view(), s);
2074 d->signal_response().connect (sigc::bind (sigc::mem_fun (*this, &Editor::note_edit_done), d));
2078 Editor::note_edit_done (int r, EditNoteDialog* d)
2085 Editor::visible_order_range (int* low, int* high) const
2087 *low = TimeAxisView::max_order ();
2090 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
2092 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
2094 if (!rtv->hidden()) {
2096 if (*high < rtv->order()) {
2097 *high = rtv->order ();
2100 if (*low > rtv->order()) {
2101 *low = rtv->order ();
2108 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
2110 /* Either add to or set the set the region selection, unless
2111 this is an alignment click (control used)
2114 if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
2115 TimeAxisView* tv = &rv.get_time_axis_view();
2116 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
2118 if (rtv && rtv->is_track()) {
2119 speed = rtv->track()->speed();
2122 framepos_t where = get_preferred_edit_position();
2126 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
2128 align_region (rv.region(), SyncPoint, (framepos_t) (where * speed));
2130 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
2132 align_region (rv.region(), End, (framepos_t) (where * speed));
2136 align_region (rv.region(), Start, (framepos_t) (where * speed));
2143 Editor::collect_new_region_view (RegionView* rv)
2145 latest_regionviews.push_back (rv);
2149 Editor::collect_and_select_new_region_view (RegionView* rv)
2152 latest_regionviews.push_back (rv);
2156 Editor::cancel_selection ()
2158 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2159 (*i)->hide_selection ();
2162 selection->clear ();
2163 clicked_selection = 0;
2167 Editor::cancel_time_selection ()
2169 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2170 (*i)->hide_selection ();
2172 selection->time.clear ();
2173 clicked_selection = 0;
2177 Editor::point_trim (GdkEvent* event, framepos_t new_bound)
2179 RegionView* rv = clicked_regionview;
2181 /* Choose action dependant on which button was pressed */
2182 switch (event->button.button) {
2184 begin_reversible_command (_("start point trim"));
2186 if (selection->selected (rv)) {
2187 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
2188 i != selection->regions.by_layer().end(); ++i)
2190 if (!(*i)->region()->locked()) {
2191 (*i)->region()->clear_changes ();
2192 (*i)->region()->trim_front (new_bound);
2193 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2198 if (!rv->region()->locked()) {
2199 rv->region()->clear_changes ();
2200 rv->region()->trim_front (new_bound);
2201 _session->add_command(new StatefulDiffCommand (rv->region()));
2205 commit_reversible_command();
2209 begin_reversible_command (_("End point trim"));
2211 if (selection->selected (rv)) {
2213 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
2215 if (!(*i)->region()->locked()) {
2216 (*i)->region()->clear_changes();
2217 (*i)->region()->trim_end (new_bound);
2218 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2224 if (!rv->region()->locked()) {
2225 rv->region()->clear_changes ();
2226 rv->region()->trim_end (new_bound);
2227 _session->add_command (new StatefulDiffCommand (rv->region()));
2231 commit_reversible_command();
2240 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* /*event*/)
2245 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2246 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2247 abort(); /*NOTREACHED*/
2250 Location* location = find_location_from_marker (marker, is_start);
2251 location->set_hidden (true, this);
2255 Editor::mouse_rename_region (ArdourCanvas::Item* /*item*/, GdkEvent* /*event*/)
2257 using namespace Gtkmm2ext;
2259 ArdourPrompter prompter (false);
2261 prompter.set_prompt (_("Name for region:"));
2262 prompter.set_initial_text (clicked_regionview->region()->name());
2263 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
2264 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
2265 prompter.show_all ();
2266 switch (prompter.run ()) {
2267 case Gtk::RESPONSE_ACCEPT:
2269 prompter.get_result(str);
2271 clicked_regionview->region()->set_name (str);
2280 Editor::mouse_brush_insert_region (RegionView* rv, framepos_t pos)
2282 /* no brushing without a useful snap setting */
2284 switch (_snap_mode) {
2286 return; /* can't work because it allows region to be placed anywhere */
2291 switch (_snap_type) {
2299 /* don't brush a copy over the original */
2301 if (pos == rv->region()->position()) {
2305 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
2307 if (rtv == 0 || !rtv->is_track()) {
2311 boost::shared_ptr<Playlist> playlist = rtv->playlist();
2312 double speed = rtv->track()->speed();
2314 playlist->clear_changes ();
2315 boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region(), true));
2316 playlist->add_region (new_region, (framepos_t) (pos * speed));
2317 _session->add_command (new StatefulDiffCommand (playlist));
2319 // playlist is frozen, so we have to update manually XXX this is disgusting
2321 playlist->RegionAdded (new_region); /* EMIT SIGNAL */
2325 Editor::track_height_step_timeout ()
2327 if (get_microseconds() - last_track_height_step_timestamp < 250000) {
2328 current_stepping_trackview = 0;
2335 Editor::add_region_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2337 assert (region_view);
2339 if (!region_view->region()->playlist()) {
2343 switch (Config->get_edit_mode()) {
2345 _drags->add (new RegionSpliceDrag (this, item, region_view, selection->regions.by_layer()));
2348 _drags->add (new RegionRippleDrag (this, item, region_view, selection->regions.by_layer()));
2351 _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), false, false));
2358 Editor::add_region_copy_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2360 assert (region_view);
2362 if (!region_view->region()->playlist()) {
2366 _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), false, true));
2370 Editor::add_region_brush_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2372 assert (region_view);
2374 if (!region_view->region()->playlist()) {
2378 if (Config->get_edit_mode() == Splice || Config->get_edit_mode() == Ripple) {
2382 _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), true, false));
2384 begin_reversible_command (Operations::drag_region_brush);
2387 /** Start a grab where a time range is selected, track(s) are selected, and the
2388 * user clicks and drags a region with a modifier in order to create a new region containing
2389 * the section of the clicked region that lies within the time range.
2392 Editor::start_selection_grab (ArdourCanvas::Item* /*item*/, GdkEvent* event)
2394 if (clicked_regionview == 0) {
2398 /* lets try to create new Region for the selection */
2400 vector<boost::shared_ptr<Region> > new_regions;
2401 create_region_from_selection (new_regions);
2403 if (new_regions.empty()) {
2407 /* XXX fix me one day to use all new regions */
2409 boost::shared_ptr<Region> region (new_regions.front());
2411 /* add it to the current stream/playlist.
2413 tricky: the streamview for the track will add a new regionview. we will
2414 catch the signal it sends when it creates the regionview to
2415 set the regionview we want to then drag.
2418 latest_regionviews.clear();
2419 sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
2421 /* A selection grab currently creates two undo/redo operations, one for
2422 creating the new region and another for moving it.
2425 begin_reversible_command (Operations::selection_grab);
2427 boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
2429 playlist->clear_changes ();
2430 clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
2431 _session->add_command(new StatefulDiffCommand (playlist));
2433 commit_reversible_command ();
2437 if (latest_regionviews.empty()) {
2438 /* something went wrong */
2442 /* we need to deselect all other regionviews, and select this one
2443 i'm ignoring undo stuff, because the region creation will take care of it
2445 selection->set (latest_regionviews);
2447 _drags->set (new RegionMoveDrag (this, latest_regionviews.front()->get_canvas_group(), latest_regionviews.front(), latest_regionviews, false, false), event);
2453 if (_drags->active ()) {
2456 selection->clear ();
2463 Editor::set_internal_edit (bool yn)
2465 if (_internal_editing == yn) {
2469 _internal_editing = yn;
2472 pre_internal_mouse_mode = mouse_mode;
2473 pre_internal_snap_type = _snap_type;
2474 pre_internal_snap_mode = _snap_mode;
2476 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2477 (*i)->enter_internal_edit_mode ();
2480 set_snap_to (internal_snap_type);
2481 set_snap_mode (internal_snap_mode);
2485 internal_snap_mode = _snap_mode;
2486 internal_snap_type = _snap_type;
2488 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2489 (*i)->leave_internal_edit_mode ();
2492 if (mouse_mode == MouseDraw && pre_internal_mouse_mode != MouseDraw) {
2493 /* we were drawing .. flip back to something sensible */
2494 set_mouse_mode (pre_internal_mouse_mode);
2497 set_snap_to (pre_internal_snap_type);
2498 set_snap_mode (pre_internal_snap_mode);
2501 reset_canvas_cursor ();
2502 MouseModeChanged ();
2505 /** Update _join_object_range_state which indicate whether we are over the top
2506 * or bottom half of a route view, used by the `join object/range' tool
2507 * mode. Coordinates in canvas space.
2510 Editor::update_join_object_range_location (double y)
2512 if (_internal_editing || !get_smart_mode()) {
2513 _join_object_range_state = JOIN_OBJECT_RANGE_NONE;
2517 JoinObjectRangeState const old = _join_object_range_state;
2519 if (mouse_mode == MouseObject) {
2520 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2521 } else if (mouse_mode == MouseRange) {
2522 _join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
2525 if (entered_regionview) {
2527 ArdourCanvas::Duple const item_space = entered_regionview->get_canvas_group()->canvas_to_item (ArdourCanvas::Duple (0, y));
2528 double const c = item_space.y / entered_regionview->height();
2530 _join_object_range_state = c <= 0.5 ? JOIN_OBJECT_RANGE_RANGE : JOIN_OBJECT_RANGE_OBJECT;
2532 if (_join_object_range_state != old) {
2533 set_canvas_cursor (which_track_cursor ());
2536 } else if (entered_track) {
2538 RouteTimeAxisView* entered_route_view = dynamic_cast<RouteTimeAxisView*> (entered_track);
2540 if (entered_route_view) {
2545 entered_route_view->canvas_display()->canvas_to_item (cx, cy);
2547 double track_height = entered_route_view->view()->child_height();
2548 if (Config->get_show_name_highlight()) {
2549 track_height -= TimeAxisViewItem::NAME_HIGHLIGHT_SIZE;
2551 double const c = cy / track_height;
2555 _join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
2557 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2561 /* Other kinds of tracks use object mode */
2562 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2565 if (_join_object_range_state != old) {
2566 set_canvas_cursor (which_track_cursor ());
2572 Editor::effective_mouse_mode () const
2574 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
2576 } else if (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE) {
2584 Editor::remove_midi_note (ArdourCanvas::Item* item, GdkEvent *)
2586 NoteBase* e = reinterpret_cast<NoteBase*> (item->get_data ("notebase"));
2589 e->region_view().delete_note (e->note ());
2592 /** Obtain the pointer position in canvas coordinates */
2594 Editor::get_pointer_position (double& x, double& y) const
2597 _track_canvas->get_pointer (px, py);
2598 _track_canvas->window_to_canvas (px, py, x, y);