2 Copyright (C) 2000-2001 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
29 #include "pbd/error.h"
30 #include "pbd/enumwriter.h"
31 #include "pbd/memento_command.h"
32 #include "pbd/basename.h"
33 #include "pbd/stateful_diff_command.h"
35 #include "gtkmm2ext/bindings.h"
36 #include "gtkmm2ext/utils.h"
37 #include "gtkmm2ext/tearoff.h"
39 #include "canvas/canvas.h"
41 #include "ardour/audioregion.h"
42 #include "ardour/operations.h"
43 #include "ardour/playlist.h"
44 #include "ardour/profile.h"
45 #include "ardour/region_factory.h"
46 #include "ardour/route.h"
47 #include "ardour/session.h"
48 #include "ardour/types.h"
50 #include "ardour_ui.h"
53 #include "time_axis_view.h"
54 #include "audio_time_axis.h"
55 #include "audio_region_view.h"
56 #include "midi_region_view.h"
58 #include "streamview.h"
59 #include "region_gain_line.h"
60 #include "automation_time_axis.h"
61 #include "control_point.h"
63 #include "selection.h"
66 #include "rgb_macros.h"
67 #include "control_point_dialog.h"
68 #include "editor_drag.h"
69 #include "automation_region_view.h"
70 #include "edit_note_dialog.h"
71 #include "mouse_cursors.h"
72 #include "editor_cursors.h"
73 #include "verbose_cursor.h"
79 using namespace ARDOUR;
82 using namespace Editing;
83 using Gtkmm2ext::Keyboard;
86 Editor::mouse_frame (framepos_t& where, bool& in_track_canvas) const
88 /* gdk_window_get_pointer() has X11's XQueryPointer semantics in that it only
89 pays attentions to subwindows. this means that menu windows are ignored, and
90 if the pointer is in a menu, the return window from the call will be the
91 the regular subwindow *under* the menu.
93 this matters quite a lot if the pointer is moving around in a menu that overlaps
94 the track canvas because we will believe that we are within the track canvas
95 when we are not. therefore, we track enter/leave events for the track canvas
96 and allow that to override the result of gdk_window_get_pointer().
99 if (!within_track_canvas) {
104 Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->_track_canvas->get_window();
106 if (!canvas_window) {
110 Glib::RefPtr<const Gdk::Window> pointer_window = Gdk::Display::get_default()->get_window_at_pointer (x, y);
112 if (!pointer_window) {
116 if (pointer_window != canvas_window) {
117 in_track_canvas = false;
121 in_track_canvas = true;
124 event.type = GDK_BUTTON_RELEASE;
128 where = window_event_sample (&event, 0, 0);
134 Editor::window_event_sample (GdkEvent const * event, double* pcx, double* pcy) const
136 ArdourCanvas::Duple d;
138 if (!gdk_event_get_coords (event, &d.x, &d.y)) {
142 /* event coordinates are in window units, so convert to canvas
145 d = _track_canvas->window_to_canvas (d);
155 return pixel_to_sample (d.x);
159 Editor::canvas_event_sample (GdkEvent const * event, double* pcx, double* pcy) const
164 /* event coordinates are already in canvas units */
166 if (!gdk_event_get_coords (event, &x, &y)) {
167 cerr << "!NO c COORDS for event type " << event->type << endl;
179 /* note that pixel_to_sample_from_event() never returns less than zero, so even if the pixel
180 position is negative (as can be the case with motion events in particular),
181 the frame location is always positive.
184 return pixel_to_sample_from_event (x);
188 Editor::set_current_trimmable (boost::shared_ptr<Trimmable> t)
190 boost::shared_ptr<Trimmable> st = _trimmable.lock();
192 if (!st || st == t) {
198 Editor::set_current_movable (boost::shared_ptr<Movable> m)
200 boost::shared_ptr<Movable> sm = _movable.lock();
202 if (!sm || sm != m) {
208 Editor::mouse_mode_object_range_toggled()
210 MouseMode m = mouse_mode;
212 Glib::RefPtr<Action> act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-range"));
214 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
217 if (tact->get_active()) {
218 m = MouseObject; //Smart mode turned to ON, force editing to Object mode
221 set_mouse_mode(m, true); //call this so the button styles can get updated
225 Editor::set_mouse_mode (MouseMode m, bool force)
227 if (_drags->active ()) {
231 if (!force && m == mouse_mode) {
235 if (ARDOUR::Profile->get_mixbus()) {
236 if ( m == MouseCut) m = MouseObject;
239 Glib::RefPtr<Action> act;
243 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-range"));
247 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-cut"));
251 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-object"));
255 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-draw"));
259 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-timefx"));
263 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-audition"));
269 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
272 /* go there and back to ensure that the toggled handler is called to set up mouse_mode */
273 tact->set_active (false);
274 tact->set_active (true);
276 //NOTE: this will result in a call to mouse_mode_toggled which does the heavy lifting
280 Editor::mouse_mode_toggled (MouseMode m)
282 Glib::RefPtr<Action> act;
283 Glib::RefPtr<ToggleAction> tact;
285 if (ARDOUR::Profile->get_mixbus()) {
286 if ( m == MouseCut) m = MouseObject;
291 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-range"));
295 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-object"));
299 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-cut"));
303 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-draw"));
307 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-timefx"));
311 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-audition"));
317 tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
320 if (!tact->get_active()) {
321 /* this was just the notification that the old mode has been
322 * left. we'll get called again with the new mode active in a
328 if (_session && mouse_mode == MouseAudition) {
329 /* stop transport and reset default speed to avoid oddness with
331 _session->request_transport_speed (0.0, true);
338 /* this should generate a new enter event which will
339 trigger the appropiate cursor.
343 _track_canvas->re_enter ();
346 set_gain_envelope_visibility ();
348 update_time_selection_display ();
350 MouseModeChanged (); /* EMIT SIGNAL */
354 Editor::update_time_selection_display ()
356 switch (mouse_mode) {
358 selection->clear_objects ();
361 selection->clear_time ();
367 Editor::step_mouse_mode (bool next)
369 const int n_mouse_modes = (int)MouseDraw + 1;
370 int current = (int)current_mouse_mode();
372 set_mouse_mode((MouseMode)((current + 1) % n_mouse_modes));
374 set_mouse_mode((MouseMode)((current + n_mouse_modes - 1) % n_mouse_modes));
379 Editor::toggle_internal_editing_from_double_click (GdkEvent* event)
381 if (_drags->active()) {
382 _drags->end_grab (event);
385 ActionManager::do_action ("MouseMode", "toggle-internal-edit");
387 /* prevent reversion of edit cursor on button release */
389 pre_press_cursor = 0;
395 Editor::button_selection (ArdourCanvas::Item* /*item*/, GdkEvent* event, ItemType item_type)
397 /* in object/audition/timefx/gain-automation mode,
398 any button press sets the selection if the object
399 can be selected. this is a bit of hack, because
400 we want to avoid this if the mouse operation is a
403 note: not dbl-click or triple-click
405 Also note that there is no region selection in internal edit mode, otherwise
406 for operations operating on the selection (e.g. cut) it is not obvious whether
407 to cut notes or regions.
410 MouseMode eff_mouse_mode = effective_mouse_mode ();
412 if (eff_mouse_mode == MouseCut) {
413 /* never change selection in cut mode */
417 if (get_smart_mode() && eff_mouse_mode == MouseRange && event->button.button == 3 && item_type == RegionItem) {
418 /* context clicks are always about object properties, even if
419 we're in range mode within smart mode.
421 eff_mouse_mode = MouseObject;
424 /* special case: allow drag of region fade in/out in object mode with join object/range enabled */
425 if (get_smart_mode()) {
427 case FadeInHandleItem:
428 case FadeInTrimHandleItem:
429 case FadeOutHandleItem:
430 case FadeOutTrimHandleItem:
431 eff_mouse_mode = MouseObject;
438 if (((mouse_mode != MouseObject) &&
439 (mouse_mode != MouseAudition || item_type != RegionItem) &&
440 (mouse_mode != MouseTimeFX || item_type != RegionItem) &&
441 (mouse_mode != MouseDraw)) ||
442 ((event->type != GDK_BUTTON_PRESS && event->type != GDK_BUTTON_RELEASE) || event->button.button > 3) ||
443 (internal_editing() && mouse_mode != MouseTimeFX)) {
448 if (event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) {
450 if ((event->button.state & Keyboard::RelevantModifierKeyMask) && event->button.button != 1) {
452 /* almost no selection action on modified button-2 or button-3 events */
454 if (item_type != RegionItem && event->button.button != 2) {
460 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
461 bool press = (event->type == GDK_BUTTON_PRESS);
466 if (eff_mouse_mode != MouseRange) {
467 set_selected_regionview_from_click (press, op);
469 /* don't change the selection unless the
470 clicked track is not currently selected. if
471 so, "collapse" the selection to just this
474 if (!selection->selected (clicked_axisview)) {
475 set_selected_track_as_side_effect (Selection::Set);
479 if (eff_mouse_mode != MouseRange) {
480 set_selected_regionview_from_click (press, op);
485 case RegionViewNameHighlight:
487 case LeftFrameHandle:
488 case RightFrameHandle:
489 case FadeInHandleItem:
490 case FadeInTrimHandleItem:
492 case FadeOutHandleItem:
493 case FadeOutTrimHandleItem:
495 case StartCrossFadeItem:
496 case EndCrossFadeItem:
497 if (get_smart_mode() || eff_mouse_mode != MouseRange) {
498 set_selected_regionview_from_click (press, op);
499 } else if (event->type == GDK_BUTTON_PRESS) {
500 set_selected_track_as_side_effect (op);
504 case ControlPointItem:
505 set_selected_track_as_side_effect (op);
506 if (eff_mouse_mode != MouseRange) {
507 set_selected_control_point_from_click (press, op);
512 /* for context click, select track */
513 if (event->button.button == 3) {
514 selection->clear_tracks ();
515 set_selected_track_as_side_effect (op);
519 case AutomationTrackItem:
520 set_selected_track_as_side_effect (op);
529 Editor::button_press_handler_1 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
531 /* single mouse clicks on any of these item types operate
532 independent of mouse mode, mostly because they are
533 not on the main track canvas or because we want
538 case PlayheadCursorItem:
539 _drags->set (new CursorDrag (this, *playhead_cursor, true), event);
543 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
544 hide_marker (item, event);
546 _drags->set (new MarkerDrag (this, item), event);
550 case TempoMarkerItem:
553 new TempoMarkerDrag (
556 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
563 case MeterMarkerItem:
566 new MeterMarkerDrag (
569 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
577 _drags->set (new VideoTimeLineDrag (this, item), event);
584 case TimecodeRulerItem:
585 case SamplesRulerItem:
586 case MinsecRulerItem:
588 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
589 _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
595 case RangeMarkerBarItem:
596 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier)) {
597 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateSkipMarker), event);
598 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
599 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateRangeMarker), event);
601 _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
606 case CdMarkerBarItem:
607 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
608 _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
610 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateCDMarker), event);
615 case TransportMarkerBarItem:
616 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
617 _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
619 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateTransportMarker), event);
628 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
629 /* special case: allow trim of range selections in joined object mode;
630 in theory eff should equal MouseRange in this case, but it doesn't
631 because entering the range selection canvas item results in entered_regionview
632 being set to 0, so update_join_object_range_location acts as if we aren't
635 if (item_type == StartSelectionTrimItem) {
636 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
637 } else if (item_type == EndSelectionTrimItem) {
638 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
642 Editing::MouseMode eff = effective_mouse_mode ();
644 /* special case: allow drag of region fade in/out in object mode with join object/range enabled */
645 if (get_smart_mode()) {
647 case FadeInHandleItem:
648 case FadeInTrimHandleItem:
649 case FadeOutHandleItem:
650 case FadeOutTrimHandleItem:
658 /* there is no Range mode when in internal edit mode */
659 if (eff == MouseRange && internal_editing()) {
666 case StartSelectionTrimItem:
667 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
670 case EndSelectionTrimItem:
671 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
675 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
676 start_selection_grab (item, event);
678 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
679 /* grab selection for moving */
680 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionMove), event);
682 /* this was debated, but decided the more common action was to
683 make a new selection */
684 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
689 if (internal_editing()) {
690 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
691 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
695 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::RangeSelectModifier)) {
696 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionExtend), event);
698 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
704 case RegionViewNameHighlight:
705 if (!clicked_regionview->region()->locked()) {
706 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
712 if (!internal_editing()) {
713 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::RangeSelectModifier)) {
714 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionExtend), event);
716 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
726 case FadeInHandleItem:
727 case FadeOutHandleItem:
728 case LeftFrameHandle:
729 case RightFrameHandle:
730 case FeatureLineItem:
731 case RegionViewNameHighlight:
734 case AutomationTrackItem:
735 _drags->set (new RegionCutDrag (this, item, canvas_event_sample (event)), event, current_canvas_cursor);
746 /* Existing note: allow trimming/motion */
747 if (internal_editing()) {
748 NoteBase* cn = reinterpret_cast<NoteBase*> (item->get_data ("notebase"));
750 if (cn->big_enough_to_trim() && cn->mouse_near_ends()) {
751 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
753 _drags->set (new NoteDrag (this, item), event);
763 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)) &&
764 event->type == GDK_BUTTON_PRESS) {
766 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
768 } else if (event->type == GDK_BUTTON_PRESS) {
771 case FadeInHandleItem:
773 _drags->set (new FadeInDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), selection->regions), event, _cursors->fade_in);
777 case FadeOutHandleItem:
779 _drags->set (new FadeOutDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), selection->regions), event, _cursors->fade_out);
783 case StartCrossFadeItem:
784 case EndCrossFadeItem:
785 /* we might allow user to grab inside the fade to trim a region with preserve_fade_anchor. for not this is not fully implemented */
786 // if (!clicked_regionview->region()->locked()) {
787 // _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer(), true), event);
792 case FeatureLineItem:
794 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier)) {
795 remove_transient(item);
799 _drags->set (new FeatureLineDrag (this, item), event);
805 if (dynamic_cast<AutomationRegionView*> (clicked_regionview)) {
806 /* click on an automation region view; do nothing here and let the ARV's signal handler
812 if (internal_editing ()) {
816 /* click on a normal region view */
817 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
818 add_region_copy_drag (item, event, clicked_regionview);
819 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
820 add_region_brush_drag (item, event, clicked_regionview);
822 add_region_drag (item, event, clicked_regionview);
826 // if (!internal_editing() && (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE && !selection->regions.empty())) {
827 // _drags->add (new SelectionDrag (this, clicked_axisview->get_selection_rect (clicked_selection)->rect, SelectionDrag::SelectionMove));
830 _drags->start_grab (event);
834 case RegionViewNameHighlight:
835 case LeftFrameHandle:
836 case RightFrameHandle:
837 if (!clicked_regionview->region()->locked()) {
838 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
843 case FadeInTrimHandleItem:
844 case FadeOutTrimHandleItem:
845 if (!clicked_regionview->region()->locked()) {
846 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer(), true), event);
853 /* rename happens on edit clicks */
854 if (clicked_regionview->get_name_highlight()) {
855 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer()), event);
861 case ControlPointItem:
862 _drags->set (new ControlPointDrag (this, item), event);
866 case AutomationLineItem:
867 _drags->set (new LineDrag (this, item), event);
872 if (internal_editing()) {
873 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
874 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
878 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
882 case AutomationTrackItem:
884 TimeAxisView* parent = clicked_axisview->get_parent ();
885 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (clicked_axisview);
887 if (parent && dynamic_cast<MidiTimeAxisView*> (parent) && atv->show_regions ()) {
889 RouteTimeAxisView* p = dynamic_cast<RouteTimeAxisView*> (parent);
891 boost::shared_ptr<Playlist> pl = p->track()->playlist ();
892 if (pl->n_regions() == 0) {
893 /* Parent has no regions; create one so that we have somewhere to put automation */
894 _drags->set (new RegionCreateDrag (this, item, parent), event);
896 /* See if there's a region before the click that we can extend, and extend it if so */
897 framepos_t const t = canvas_event_sample (event);
898 boost::shared_ptr<Region> prev = pl->find_next_region (t, End, -1);
900 _drags->set (new RegionCreateDrag (this, item, parent), event);
902 prev->set_length (t - prev->position ());
906 /* rubberband drag to select automation points */
907 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
931 _drags->set (new LineDrag (this, item), event);
934 case ControlPointItem:
935 _drags->set (new ControlPointDrag (this, item), event);
941 AudioRegionView* arv = dynamic_cast<AudioRegionView *> (clicked_regionview);
943 _drags->set (new AutomationRangeDrag (this, arv, selection->time), event, _cursors->up_down);
945 double const y = event->button.y;
946 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
948 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
950 /* smart "join" mode: drag automation */
951 _drags->set (new AutomationRangeDrag (this, atv, selection->time), event, _cursors->up_down);
959 case AutomationLineItem:
960 _drags->set (new LineDrag (this, item), event);
964 /* Existing note: allow trimming/motion */
965 if (internal_editing()) {
966 /* trim notes if we're in internal edit mode and near the ends of the note */
967 NoteBase* cn = reinterpret_cast<NoteBase*>(item->get_data ("notebase"));
969 if (cn->big_enough_to_trim() && cn->mouse_near_ends()) {
970 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
972 _drags->set (new NoteDrag (this, item), event);
979 if (internal_editing()) {
980 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
981 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
994 if (internal_editing() && item_type == NoteItem ) {
995 /* drag notes if we're in internal edit mode */
996 NoteBase* cn = reinterpret_cast<NoteBase*>(item->get_data ("notebase"));
998 if (cn->big_enough_to_trim()) {
999 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
1002 } else if (clicked_regionview) {
1004 _drags->set (new TimeFXDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1010 _drags->set (new ScrubDrag (this, item), event);
1011 scrub_reversals = 0;
1012 scrub_reverse_distance = 0;
1013 last_scrub_x = event->button.x;
1014 scrubbing_direction = 0;
1015 push_canvas_cursor (_cursors->transparent);
1027 Editor::button_press_handler_2 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1029 Editing::MouseMode const eff = effective_mouse_mode ();
1032 switch (item_type) {
1034 if (internal_editing ()) {
1035 /* no region drags in internal edit mode */
1039 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
1040 add_region_copy_drag (item, event, clicked_regionview);
1042 add_region_drag (item, event, clicked_regionview);
1044 _drags->start_grab (event);
1047 case ControlPointItem:
1048 _drags->set (new ControlPointDrag (this, item), event);
1056 switch (item_type) {
1057 case RegionViewNameHighlight:
1058 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1062 case LeftFrameHandle:
1063 case RightFrameHandle:
1064 if (!internal_editing ()) {
1065 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1070 case RegionViewName:
1071 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer()), event);
1085 /* relax till release */
1097 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1099 if (event->type == GDK_2BUTTON_PRESS) {
1100 _drags->mark_double_click ();
1101 gdk_pointer_ungrab (GDK_CURRENT_TIME);
1105 if (event->type != GDK_BUTTON_PRESS) {
1109 pre_press_cursor = current_canvas_cursor;
1111 _track_canvas->grab_focus();
1113 if (_session && _session->actively_recording()) {
1117 if (internal_editing()) {
1118 bool leave_internal_edit_mode = false;
1120 switch (item_type) {
1125 if (!dynamic_cast<MidiRegionView*> (clicked_regionview) && !dynamic_cast<AutomationRegionView*> (clicked_regionview)) {
1126 leave_internal_edit_mode = true;
1130 case PlayheadCursorItem:
1132 case TempoMarkerItem:
1133 case MeterMarkerItem:
1137 case RangeMarkerBarItem:
1138 case CdMarkerBarItem:
1139 case TransportMarkerBarItem:
1141 case TimecodeRulerItem:
1142 case SamplesRulerItem:
1143 case MinsecRulerItem:
1145 /* button press on these items never does anything to
1146 change the editing mode.
1154 if (leave_internal_edit_mode) {
1155 ActionManager::do_action ("MouseMode", "toggle-internal-edit");
1159 button_selection (item, event, item_type);
1161 if (!_drags->active () &&
1162 (Keyboard::is_delete_event (&event->button) ||
1163 Keyboard::is_context_menu_event (&event->button) ||
1164 Keyboard::is_edit_event (&event->button))) {
1166 /* handled by button release */
1170 //not rolling, range mode click + join_play_range : locate the PH here
1171 if ( !_drags->active () && !_session->transport_rolling() && ( effective_mouse_mode() == MouseRange ) && Config->get_follow_edits() ) {
1172 framepos_t where = canvas_event_sample (event);
1174 _session->request_locate (where, false);
1177 switch (event->button.button) {
1179 return button_press_handler_1 (item, event, item_type);
1183 return button_press_handler_2 (item, event, item_type);
1190 return button_press_dispatch (&event->button);
1199 Editor::button_press_dispatch (GdkEventButton* ev)
1201 /* this function is intended only for buttons 4 and above.
1204 Gtkmm2ext::MouseButton b (ev->state, ev->button);
1205 return button_bindings->activate (b, Gtkmm2ext::Bindings::Press);
1209 Editor::button_release_dispatch (GdkEventButton* ev)
1211 /* this function is intended only for buttons 4 and above.
1214 Gtkmm2ext::MouseButton b (ev->state, ev->button);
1215 return button_bindings->activate (b, Gtkmm2ext::Bindings::Release);
1219 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1221 framepos_t where = canvas_event_sample (event);
1222 AutomationTimeAxisView* atv = 0;
1224 if (pre_press_cursor) {
1225 set_canvas_cursor (pre_press_cursor);
1226 pre_press_cursor = 0;
1229 /* no action if we're recording */
1231 if (_session && _session->actively_recording()) {
1235 /* see if we're finishing a drag */
1237 bool were_dragging = false;
1238 if (_drags->active ()) {
1239 bool const r = _drags->end_grab (event);
1241 /* grab dragged, so do nothing else */
1245 were_dragging = true;
1248 update_region_layering_order_editor ();
1250 /* edit events get handled here */
1252 if (!_drags->active () && Keyboard::is_edit_event (&event->button)) {
1253 switch (item_type) {
1255 show_region_properties ();
1258 case TempoMarkerItem: {
1260 TempoMarker* tempo_marker;
1262 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
1263 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
1264 abort(); /*NOTREACHED*/
1267 if ((tempo_marker = dynamic_cast<TempoMarker*> (marker)) == 0) {
1268 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
1269 abort(); /*NOTREACHED*/
1272 edit_tempo_marker (*tempo_marker);
1276 case MeterMarkerItem: {
1278 MeterMarker* meter_marker;
1280 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
1281 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
1282 abort(); /*NOTREACHED*/
1285 if ((meter_marker = dynamic_cast<MeterMarker*> (marker)) == 0) {
1286 fatal << _("programming error: marker for meter is not a meter marker!") << endmsg;
1287 abort(); /*NOTREACHED*/
1289 edit_meter_marker (*meter_marker);
1293 case RegionViewName:
1294 if (clicked_regionview->name_active()) {
1295 return mouse_rename_region (item, event);
1299 case ControlPointItem:
1300 edit_control_point (item);
1309 /* context menu events get handled here */
1310 if (Keyboard::is_context_menu_event (&event->button)) {
1312 context_click_event = *event;
1314 if (!_drags->active ()) {
1316 /* no matter which button pops up the context menu, tell the menu
1317 widget to use button 1 to drive menu selection.
1320 switch (item_type) {
1322 case FadeInHandleItem:
1323 case FadeInTrimHandleItem:
1324 case StartCrossFadeItem:
1325 popup_xfade_in_context_menu (1, event->button.time, item, item_type);
1329 case FadeOutHandleItem:
1330 case FadeOutTrimHandleItem:
1331 case EndCrossFadeItem:
1332 popup_xfade_out_context_menu (1, event->button.time, item, item_type);
1335 case LeftFrameHandle:
1336 case RightFrameHandle:
1340 popup_track_context_menu (1, event->button.time, item_type, false);
1344 case RegionViewNameHighlight:
1345 case RegionViewName:
1346 popup_track_context_menu (1, event->button.time, item_type, false);
1350 popup_track_context_menu (1, event->button.time, item_type, true);
1353 case AutomationTrackItem:
1354 popup_track_context_menu (1, event->button.time, item_type, false);
1358 case RangeMarkerBarItem:
1359 case TransportMarkerBarItem:
1360 case CdMarkerBarItem:
1364 case TimecodeRulerItem:
1365 case SamplesRulerItem:
1366 case MinsecRulerItem:
1368 popup_ruler_menu (where, item_type);
1372 marker_context_menu (&event->button, item);
1375 case TempoMarkerItem:
1376 tempo_or_meter_marker_context_menu (&event->button, item);
1379 case MeterMarkerItem:
1380 tempo_or_meter_marker_context_menu (&event->button, item);
1383 case CrossfadeViewItem:
1384 popup_track_context_menu (1, event->button.time, item_type, false);
1387 case ControlPointItem:
1388 popup_control_point_context_menu (item, event);
1399 /* delete events get handled here */
1401 Editing::MouseMode const eff = effective_mouse_mode ();
1403 if (!_drags->active () && Keyboard::is_delete_event (&event->button)) {
1405 switch (item_type) {
1406 case TempoMarkerItem:
1407 remove_tempo_marker (item);
1410 case MeterMarkerItem:
1411 remove_meter_marker (item);
1415 remove_marker (*item, event);
1419 if (eff == MouseObject) {
1420 remove_clicked_region ();
1424 case ControlPointItem:
1425 remove_control_point (item);
1429 remove_midi_note (item, event);
1438 switch (event->button.button) {
1441 switch (item_type) {
1442 /* see comments in button_press_handler */
1443 case PlayheadCursorItem:
1446 case AutomationLineItem:
1447 case StartSelectionTrimItem:
1448 case EndSelectionTrimItem:
1452 if (!_dragging_playhead) {
1453 snap_to_with_modifier (where, event, RoundNearest, true);
1454 mouse_add_new_marker (where);
1458 case CdMarkerBarItem:
1459 if (!_dragging_playhead) {
1460 // if we get here then a dragged range wasn't done
1461 snap_to_with_modifier (where, event, RoundNearest, true);
1462 mouse_add_new_marker (where, true);
1467 if (!_dragging_playhead) {
1468 snap_to_with_modifier (where, event);
1469 mouse_add_new_tempo_event (where);
1474 if (!_dragging_playhead) {
1475 mouse_add_new_meter_event (pixel_to_sample (event->button.x));
1480 case TimecodeRulerItem:
1481 case SamplesRulerItem:
1482 case MinsecRulerItem:
1493 switch (item_type) {
1496 /* check that we didn't drag before releasing, since
1497 its really annoying to create new control
1498 points when doing this.
1500 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (clicked_regionview);
1501 if (!were_dragging && arv) {
1502 bool with_guard_points = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
1503 arv->add_gain_point_event (item, event, with_guard_points);
1509 case AutomationTrackItem: {
1510 bool with_guard_points = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
1511 atv = dynamic_cast<AutomationTimeAxisView*>(clicked_axisview);
1513 atv->add_automation_event (event, where, event->button.y, with_guard_points);
1524 pop_canvas_cursor ();
1525 if (scrubbing_direction == 0) {
1526 /* no drag, just a click */
1527 switch (item_type) {
1529 play_selected_region ();
1535 /* make sure we stop */
1536 _session->request_transport_speed (0.0);
1545 /* do any (de)selection operations that should occur on button release */
1546 button_selection (item, event, item_type);
1555 switch (item_type) {
1557 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1559 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier|Keyboard::SecondaryModifier))) {
1562 // Button2 click is unused
1577 // x_style_paste (where, 1.0);
1598 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1605 /* by the time we reach here, entered_regionview and entered trackview
1606 * will have already been set as appropriate. Things are done this
1607 * way because this method isn't passed a pointer to a variable type of
1608 * thing that is entered (which may or may not be canvas item).
1609 * (e.g. the actual entered regionview)
1612 choose_canvas_cursor_on_entry (&event->crossing, item_type);
1614 switch (item_type) {
1615 case ControlPointItem:
1616 if (mouse_mode == MouseDraw || mouse_mode == MouseObject) {
1617 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1620 fraction = 1.0 - (cp->get_y() / cp->line().height());
1622 _verbose_cursor->set (cp->line().get_verbose_cursor_string (fraction));
1623 _verbose_cursor->show ();
1628 if (mouse_mode == MouseDraw) {
1629 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1631 line->set_outline_color (ARDOUR_UI::config()->color ("entered gain line"));
1636 case AutomationLineItem:
1637 if (mouse_mode == MouseDraw || mouse_mode == MouseObject) {
1638 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1640 line->set_outline_color (ARDOUR_UI::config()->color ("entered automation line"));
1645 case AutomationTrackItem:
1646 AutomationTimeAxisView* atv;
1647 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1648 clear_entered_track = false;
1649 set_entered_track (atv);
1654 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1657 entered_marker = marker;
1658 marker->set_color_rgba (ARDOUR_UI::config()->color ("entered marker"));
1660 case MeterMarkerItem:
1661 case TempoMarkerItem:
1664 case FadeInHandleItem:
1665 case FadeInTrimHandleItem:
1666 if (mouse_mode == MouseObject && !internal_editing()) {
1667 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
1669 RegionView* rv = static_cast<RegionView*>(item->get_data ("regionview"));
1670 rect->set_fill_color (rv->get_fill_color());
1675 case FadeOutHandleItem:
1676 case FadeOutTrimHandleItem:
1677 if (mouse_mode == MouseObject && !internal_editing()) {
1678 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
1680 RegionView* rv = static_cast<RegionView*>(item->get_data ("regionview"));
1681 rect->set_fill_color (rv->get_fill_color ());
1686 case FeatureLineItem:
1688 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1689 line->set_outline_color (0xFF0000FF);
1700 /* third pass to handle entered track status in a comprehensible way.
1703 switch (item_type) {
1705 case AutomationLineItem:
1706 case ControlPointItem:
1707 /* these do not affect the current entered track state */
1708 clear_entered_track = false;
1711 case AutomationTrackItem:
1712 /* handled above already */
1724 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent*, ItemType item_type)
1732 reset_canvas_cursor ();
1734 switch (item_type) {
1735 case ControlPointItem:
1736 _verbose_cursor->hide ();
1740 case AutomationLineItem:
1741 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1743 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1745 line->set_outline_color (al->get_line_color());
1751 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1755 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
1756 location_flags_changed (loc);
1759 case MeterMarkerItem:
1760 case TempoMarkerItem:
1763 case FadeInTrimHandleItem:
1764 case FadeOutTrimHandleItem:
1765 case FadeInHandleItem:
1766 case FadeOutHandleItem:
1768 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
1770 rect->set_fill_color (ARDOUR_UI::config()->color ("inactive fade handle"));
1775 case AutomationTrackItem:
1778 case FeatureLineItem:
1780 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1781 line->set_outline_color (ARDOUR_UI::config()->color ("zero line"));
1793 Editor::scrub (framepos_t frame, double current_x)
1797 if (scrubbing_direction == 0) {
1799 _session->request_locate (frame, false);
1800 _session->request_transport_speed (0.1);
1801 scrubbing_direction = 1;
1805 if (last_scrub_x > current_x) {
1807 /* pointer moved to the left */
1809 if (scrubbing_direction > 0) {
1811 /* we reversed direction to go backwards */
1814 scrub_reverse_distance += (int) (last_scrub_x - current_x);
1818 /* still moving to the left (backwards) */
1820 scrub_reversals = 0;
1821 scrub_reverse_distance = 0;
1823 delta = 0.01 * (last_scrub_x - current_x);
1824 _session->request_transport_speed_nonzero (_session->transport_speed() - delta);
1828 /* pointer moved to the right */
1830 if (scrubbing_direction < 0) {
1831 /* we reversed direction to go forward */
1834 scrub_reverse_distance += (int) (current_x - last_scrub_x);
1837 /* still moving to the right */
1839 scrub_reversals = 0;
1840 scrub_reverse_distance = 0;
1842 delta = 0.01 * (current_x - last_scrub_x);
1843 _session->request_transport_speed_nonzero (_session->transport_speed() + delta);
1847 /* if there have been more than 2 opposite motion moves detected, or one that moves
1848 back more than 10 pixels, reverse direction
1851 if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
1853 if (scrubbing_direction > 0) {
1854 /* was forwards, go backwards */
1855 _session->request_transport_speed (-0.1);
1856 scrubbing_direction = -1;
1858 /* was backwards, go forwards */
1859 _session->request_transport_speed (0.1);
1860 scrubbing_direction = 1;
1863 scrub_reverse_distance = 0;
1864 scrub_reversals = 0;
1868 last_scrub_x = current_x;
1872 Editor::motion_handler (ArdourCanvas::Item* /*item*/, GdkEvent* event, bool from_autoscroll)
1874 _last_motion_y = event->motion.y;
1876 if (event->motion.is_hint) {
1879 /* We call this so that MOTION_NOTIFY events continue to be
1880 delivered to the canvas. We need to do this because we set
1881 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
1882 the density of the events, at the expense of a round-trip
1883 to the server. Given that this will mostly occur on cases
1884 where DISPLAY = :0.0, and given the cost of what the motion
1885 event might do, its a good tradeoff.
1888 _track_canvas->get_pointer (x, y);
1891 if (current_stepping_trackview) {
1892 /* don't keep the persistent stepped trackview if the mouse moves */
1893 current_stepping_trackview = 0;
1894 step_timeout.disconnect ();
1897 if (_session && _session->actively_recording()) {
1898 /* Sorry. no dragging stuff around while we record */
1902 update_join_object_range_location (event->motion.y);
1904 if (_drags->active ()) {
1905 return _drags->motion_handler (event, from_autoscroll);
1912 Editor::can_remove_control_point (ArdourCanvas::Item* item)
1914 ControlPoint* control_point;
1916 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
1917 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
1918 abort(); /*NOTREACHED*/
1921 AutomationLine& line = control_point->line ();
1922 if (dynamic_cast<AudioRegionGainLine*> (&line)) {
1923 /* we shouldn't remove the first or last gain point in region gain lines */
1924 if (line.is_last_point(*control_point) || line.is_first_point(*control_point)) {
1933 Editor::remove_control_point (ArdourCanvas::Item* item)
1935 if (!can_remove_control_point (item)) {
1939 ControlPoint* control_point;
1941 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
1942 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
1943 abort(); /*NOTREACHED*/
1946 control_point->line().remove_point (*control_point);
1950 Editor::edit_control_point (ArdourCanvas::Item* item)
1952 ControlPoint* p = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"));
1955 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
1956 abort(); /*NOTREACHED*/
1959 ControlPointDialog d (p);
1962 if (d.run () != RESPONSE_ACCEPT) {
1966 p->line().modify_point_y (*p, d.get_y_fraction ());
1970 Editor::edit_notes (TimeAxisViewItem& tavi)
1972 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(&tavi);
1978 MidiRegionView::Selection const & s = mrv->selection();
1984 EditNoteDialog* d = new EditNoteDialog (&(*s.begin())->region_view(), s);
1988 d->signal_response().connect (sigc::bind (sigc::mem_fun (*this, &Editor::note_edit_done), d));
1992 Editor::note_edit_done (int r, EditNoteDialog* d)
1999 Editor::visible_order_range (int* low, int* high) const
2001 *low = TimeAxisView::max_order ();
2004 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
2006 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
2008 if (!rtv->hidden()) {
2010 if (*high < rtv->order()) {
2011 *high = rtv->order ();
2014 if (*low > rtv->order()) {
2015 *low = rtv->order ();
2022 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
2024 /* Either add to or set the set the region selection, unless
2025 this is an alignment click (control used)
2028 if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
2029 TimeAxisView* tv = &rv.get_time_axis_view();
2030 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
2032 if (rtv && rtv->is_track()) {
2033 speed = rtv->track()->speed();
2036 framepos_t where = get_preferred_edit_position();
2040 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
2042 align_region (rv.region(), SyncPoint, (framepos_t) (where * speed));
2044 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
2046 align_region (rv.region(), End, (framepos_t) (where * speed));
2050 align_region (rv.region(), Start, (framepos_t) (where * speed));
2057 Editor::collect_new_region_view (RegionView* rv)
2059 latest_regionviews.push_back (rv);
2063 Editor::collect_and_select_new_region_view (RegionView* rv)
2066 latest_regionviews.push_back (rv);
2070 Editor::cancel_selection ()
2072 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2073 (*i)->hide_selection ();
2076 selection->clear ();
2077 clicked_selection = 0;
2081 Editor::cancel_time_selection ()
2083 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2084 (*i)->hide_selection ();
2086 selection->time.clear ();
2087 clicked_selection = 0;
2091 Editor::point_trim (GdkEvent* event, framepos_t new_bound)
2093 RegionView* rv = clicked_regionview;
2095 /* Choose action dependant on which button was pressed */
2096 switch (event->button.button) {
2098 begin_reversible_command (_("start point trim"));
2100 if (selection->selected (rv)) {
2101 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
2102 i != selection->regions.by_layer().end(); ++i)
2104 if (!(*i)->region()->locked()) {
2105 (*i)->region()->clear_changes ();
2106 (*i)->region()->trim_front (new_bound);
2107 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2112 if (!rv->region()->locked()) {
2113 rv->region()->clear_changes ();
2114 rv->region()->trim_front (new_bound);
2115 _session->add_command(new StatefulDiffCommand (rv->region()));
2119 commit_reversible_command();
2123 begin_reversible_command (_("End point trim"));
2125 if (selection->selected (rv)) {
2127 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
2129 if (!(*i)->region()->locked()) {
2130 (*i)->region()->clear_changes();
2131 (*i)->region()->trim_end (new_bound);
2132 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2138 if (!rv->region()->locked()) {
2139 rv->region()->clear_changes ();
2140 rv->region()->trim_end (new_bound);
2141 _session->add_command (new StatefulDiffCommand (rv->region()));
2145 commit_reversible_command();
2154 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* /*event*/)
2159 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2160 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2161 abort(); /*NOTREACHED*/
2164 Location* location = find_location_from_marker (marker, is_start);
2165 location->set_hidden (true, this);
2169 Editor::mouse_rename_region (ArdourCanvas::Item* /*item*/, GdkEvent* /*event*/)
2171 using namespace Gtkmm2ext;
2173 ArdourPrompter prompter (false);
2175 prompter.set_prompt (_("Name for region:"));
2176 prompter.set_initial_text (clicked_regionview->region()->name());
2177 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
2178 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
2179 prompter.show_all ();
2180 switch (prompter.run ()) {
2181 case Gtk::RESPONSE_ACCEPT:
2183 prompter.get_result(str);
2185 clicked_regionview->region()->set_name (str);
2194 Editor::mouse_brush_insert_region (RegionView* rv, framepos_t pos)
2196 /* no brushing without a useful snap setting */
2198 switch (_snap_mode) {
2200 return; /* can't work because it allows region to be placed anywhere */
2205 switch (_snap_type) {
2213 /* don't brush a copy over the original */
2215 if (pos == rv->region()->position()) {
2219 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
2221 if (rtv == 0 || !rtv->is_track()) {
2225 boost::shared_ptr<Playlist> playlist = rtv->playlist();
2226 double speed = rtv->track()->speed();
2228 playlist->clear_changes ();
2229 boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region(), true));
2230 playlist->add_region (new_region, (framepos_t) (pos * speed));
2231 _session->add_command (new StatefulDiffCommand (playlist));
2233 // playlist is frozen, so we have to update manually XXX this is disgusting
2235 playlist->RegionAdded (new_region); /* EMIT SIGNAL */
2239 Editor::track_height_step_timeout ()
2241 if (get_microseconds() - last_track_height_step_timestamp < 250000) {
2242 current_stepping_trackview = 0;
2249 Editor::add_region_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2251 assert (region_view);
2253 if (!region_view->region()->playlist()) {
2257 switch (Config->get_edit_mode()) {
2259 _drags->add (new RegionSpliceDrag (this, item, region_view, selection->regions.by_layer()));
2262 _drags->add (new RegionRippleDrag (this, item, region_view, selection->regions.by_layer()));
2265 _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), false, false));
2272 Editor::add_region_copy_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2274 assert (region_view);
2276 if (!region_view->region()->playlist()) {
2280 _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), false, true));
2284 Editor::add_region_brush_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2286 assert (region_view);
2288 if (!region_view->region()->playlist()) {
2292 if (Config->get_edit_mode() == Splice || Config->get_edit_mode() == Ripple) {
2296 _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), true, false));
2298 begin_reversible_command (Operations::drag_region_brush);
2301 /** Start a grab where a time range is selected, track(s) are selected, and the
2302 * user clicks and drags a region with a modifier in order to create a new region containing
2303 * the section of the clicked region that lies within the time range.
2306 Editor::start_selection_grab (ArdourCanvas::Item* /*item*/, GdkEvent* event)
2308 if (clicked_regionview == 0) {
2312 /* lets try to create new Region for the selection */
2314 vector<boost::shared_ptr<Region> > new_regions;
2315 create_region_from_selection (new_regions);
2317 if (new_regions.empty()) {
2321 /* XXX fix me one day to use all new regions */
2323 boost::shared_ptr<Region> region (new_regions.front());
2325 /* add it to the current stream/playlist.
2327 tricky: the streamview for the track will add a new regionview. we will
2328 catch the signal it sends when it creates the regionview to
2329 set the regionview we want to then drag.
2332 latest_regionviews.clear();
2333 sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
2335 /* A selection grab currently creates two undo/redo operations, one for
2336 creating the new region and another for moving it.
2339 begin_reversible_command (Operations::selection_grab);
2341 boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
2343 playlist->clear_changes ();
2344 clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
2345 _session->add_command(new StatefulDiffCommand (playlist));
2349 if (latest_regionviews.empty()) {
2350 /* something went wrong */
2354 /* we need to deselect all other regionviews, and select this one
2355 i'm ignoring undo stuff, because the region creation will take care of it
2358 selection->set (latest_regionviews);
2360 commit_reversible_command ();
2362 _drags->set (new RegionMoveDrag (this, latest_regionviews.front()->get_canvas_group(), latest_regionviews.front(), latest_regionviews, false, false), event);
2368 if (_drags->active ()) {
2371 selection->clear ();
2378 Editor::set_internal_edit (bool yn)
2380 if (_internal_editing == yn) {
2384 _internal_editing = yn;
2387 pre_internal_mouse_mode = mouse_mode;
2388 pre_internal_snap_type = _snap_type;
2389 pre_internal_snap_mode = _snap_mode;
2391 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2392 (*i)->enter_internal_edit_mode ();
2395 set_snap_to (internal_snap_type);
2396 set_snap_mode (internal_snap_mode);
2400 internal_snap_mode = _snap_mode;
2401 internal_snap_type = _snap_type;
2403 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2404 (*i)->leave_internal_edit_mode ();
2407 set_snap_to (pre_internal_snap_type);
2408 set_snap_mode (pre_internal_snap_mode);
2411 reset_canvas_cursor ();
2412 MouseModeChanged ();
2415 /** Update _join_object_range_state which indicate whether we are over the top
2416 * or bottom half of a route view, used by the `join object/range' tool
2417 * mode. Coordinates in canvas space.
2420 Editor::update_join_object_range_location (double y)
2422 if (_internal_editing || !get_smart_mode()) {
2423 _join_object_range_state = JOIN_OBJECT_RANGE_NONE;
2427 JoinObjectRangeState const old = _join_object_range_state;
2429 if (mouse_mode == MouseObject) {
2430 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2431 } else if (mouse_mode == MouseRange) {
2432 _join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
2435 if (entered_regionview) {
2437 ArdourCanvas::Duple const item_space = entered_regionview->get_canvas_group()->canvas_to_item (ArdourCanvas::Duple (0, y));
2438 double const c = item_space.y / entered_regionview->height();
2440 _join_object_range_state = c <= 0.5 ? JOIN_OBJECT_RANGE_RANGE : JOIN_OBJECT_RANGE_OBJECT;
2442 if (_join_object_range_state != old) {
2443 set_canvas_cursor (which_track_cursor ());
2446 } else if (entered_track) {
2448 RouteTimeAxisView* entered_route_view = dynamic_cast<RouteTimeAxisView*> (entered_track);
2450 if (entered_route_view) {
2455 entered_route_view->canvas_display()->canvas_to_item (cx, cy);
2457 double track_height = entered_route_view->view()->child_height();
2458 if (Config->get_show_name_highlight()) {
2459 track_height -= TimeAxisViewItem::NAME_HIGHLIGHT_SIZE;
2461 double const c = cy / track_height;
2465 _join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
2467 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2471 /* Other kinds of tracks use object mode */
2472 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2475 if (_join_object_range_state != old) {
2476 set_canvas_cursor (which_track_cursor ());
2482 Editor::effective_mouse_mode () const
2484 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
2486 } else if (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE) {
2494 Editor::remove_midi_note (ArdourCanvas::Item* item, GdkEvent *)
2496 NoteBase* e = reinterpret_cast<NoteBase*> (item->get_data ("notebase"));
2499 e->region_view().delete_note (e->note ());
2502 /** Obtain the pointer position in canvas coordinates */
2504 Editor::get_pointer_position (double& x, double& y) const
2507 _track_canvas->get_pointer (px, py);
2508 _track_canvas->window_to_canvas (px, py, x, y);