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 Glib::RefPtr<Action> act;
239 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-range"));
243 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-object"));
247 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-draw"));
251 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-gain"));
255 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-zoom"));
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;
287 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-range"));
291 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-object"));
295 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-draw"));
299 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-gain"));
303 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-zoom"));
307 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-timefx"));
311 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-audition"));
317 tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
320 if (!tact->get_active()) {
321 /* this was just the notification that the old mode has been
322 * left. we'll get called again with the new mode active in a
330 act = ActionManager::get_action (X_("MouseMode"), X_("toggle-internal-edit"));
331 tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
332 tact->set_active (true);
338 if (_session && mouse_mode == MouseAudition) {
339 /* stop transport and reset default speed to avoid oddness with
341 _session->request_transport_speed (0.0, true);
348 //TODO: set button styles for smart buttons
350 if ( smart_mode_action->get_active() ) {
351 if( mouse_mode == MouseObject ) { //smart active and object active
352 smart_mode_button.set_active(1);
353 smart_mode_button.set_name("smart mode button");
354 mouse_move_button.set_name("smart mode button");
355 } else { //smart active but object inactive
356 smart_mode_button.set_active(0);
357 smart_mode_button.set_name("smart mode button");
358 mouse_move_button.set_name("mouse mode button");
361 smart_mode_button.set_active(0);
362 smart_mode_button.set_name("mouse mode button");
363 mouse_move_button.set_name("mouse mode button");
367 reset_canvas_cursor ();
368 set_gain_envelope_visibility ();
370 update_time_selection_display ();
372 MouseModeChanged (); /* EMIT SIGNAL */
376 Editor::update_time_selection_display ()
378 if (smart_mode_action->get_active()) {
379 /* not sure what to do here */
380 if (mouse_mode == MouseObject) {
384 switch (mouse_mode) {
386 selection->clear_objects ();
389 selection->clear_time ();
396 Editor::step_mouse_mode (bool next)
398 switch (current_mouse_mode()) {
401 if (Profile->get_sae()) {
402 set_mouse_mode (MouseZoom);
404 set_mouse_mode (MouseRange);
407 set_mouse_mode (MouseTimeFX);
412 if (next) set_mouse_mode (MouseDraw);
413 else set_mouse_mode (MouseObject);
417 if (next) set_mouse_mode (MouseZoom);
418 else set_mouse_mode (MouseRange);
423 if (Profile->get_sae()) {
424 set_mouse_mode (MouseTimeFX);
426 set_mouse_mode (MouseGain);
429 if (Profile->get_sae()) {
430 set_mouse_mode (MouseObject);
432 set_mouse_mode (MouseDraw);
438 if (next) set_mouse_mode (MouseTimeFX);
439 else set_mouse_mode (MouseZoom);
444 set_mouse_mode (MouseAudition);
446 if (Profile->get_sae()) {
447 set_mouse_mode (MouseZoom);
449 set_mouse_mode (MouseGain);
455 if (next) set_mouse_mode (MouseObject);
456 else set_mouse_mode (MouseTimeFX);
462 Editor::toggle_internal_editing_from_double_click (GdkEvent* event)
464 if (_drags->active()) {
465 _drags->end_grab (event);
468 ActionManager::do_action ("MouseMode", "toggle-internal-edit");
470 /* prevent reversion of edit cursor on button release */
472 pre_press_cursor = 0;
478 Editor::button_selection (ArdourCanvas::Item* /*item*/, GdkEvent* event, ItemType item_type)
480 /* in object/audition/timefx/gain-automation mode,
481 any button press sets the selection if the object
482 can be selected. this is a bit of hack, because
483 we want to avoid this if the mouse operation is a
486 note: not dbl-click or triple-click
488 Also note that there is no region selection in internal edit mode, otherwise
489 for operations operating on the selection (e.g. cut) it is not obvious whether
490 to cut notes or regions.
493 MouseMode eff_mouse_mode = effective_mouse_mode ();
495 if (get_smart_mode() && eff_mouse_mode == MouseRange && event->button.button == 3 && item_type == RegionItem) {
496 /* context clicks are always about object properties, even if
497 we're in range mode within smart mode.
499 eff_mouse_mode = MouseObject;
502 /* special case: allow drag of region fade in/out in object mode with join object/range enabled */
503 if (get_smart_mode()) {
505 case FadeInHandleItem:
506 case FadeInTrimHandleItem:
507 case FadeOutHandleItem:
508 case FadeOutTrimHandleItem:
509 eff_mouse_mode = MouseObject;
516 if (((mouse_mode != MouseObject) &&
517 (mouse_mode != MouseAudition || item_type != RegionItem) &&
518 (mouse_mode != MouseTimeFX || item_type != RegionItem) &&
519 (mouse_mode != MouseGain) &&
520 (mouse_mode != MouseDraw)) ||
521 ((event->type != GDK_BUTTON_PRESS && event->type != GDK_BUTTON_RELEASE) || event->button.button > 3) ||
522 (internal_editing() && mouse_mode != MouseTimeFX)) {
527 if (event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) {
529 if ((event->button.state & Keyboard::RelevantModifierKeyMask) && event->button.button != 1) {
531 /* almost no selection action on modified button-2 or button-3 events */
533 if (item_type != RegionItem && event->button.button != 2) {
539 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
540 bool press = (event->type == GDK_BUTTON_PRESS);
545 if (eff_mouse_mode != MouseRange) {
546 set_selected_regionview_from_click (press, op);
548 /* don't change the selection unless the
549 clicked track is not currently selected. if
550 so, "collapse" the selection to just this
553 if (!selection->selected (clicked_axisview)) {
554 set_selected_track_as_side_effect (Selection::Set);
558 if (eff_mouse_mode != MouseRange) {
559 set_selected_regionview_from_click (press, op);
564 case RegionViewNameHighlight:
566 case LeftFrameHandle:
567 case RightFrameHandle:
568 if (eff_mouse_mode != MouseRange) {
569 set_selected_regionview_from_click (press, op);
570 } else if (event->type == GDK_BUTTON_PRESS) {
571 set_selected_track_as_side_effect (op);
575 case FadeInHandleItem:
576 case FadeInTrimHandleItem:
578 case FadeOutHandleItem:
579 case FadeOutTrimHandleItem:
581 case StartCrossFadeItem:
582 case EndCrossFadeItem:
583 if (eff_mouse_mode != MouseRange) {
584 set_selected_regionview_from_click (press, op);
585 } else if (event->type == GDK_BUTTON_PRESS) {
586 set_selected_track_as_side_effect (op);
590 case ControlPointItem:
591 set_selected_track_as_side_effect (op);
592 if (eff_mouse_mode != MouseRange) {
593 set_selected_control_point_from_click (press, op);
598 /* for context click, select track */
599 if (event->button.button == 3) {
600 selection->clear_tracks ();
601 set_selected_track_as_side_effect (op);
605 case AutomationTrackItem:
606 set_selected_track_as_side_effect (op);
615 Editor::button_press_handler_1 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
617 /* single mouse clicks on any of these item types operate
618 independent of mouse mode, mostly because they are
619 not on the main track canvas or because we want
624 case PlayheadCursorItem:
625 _drags->set (new CursorDrag (this, *playhead_cursor, true), event);
629 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
630 hide_marker (item, event);
632 _drags->set (new MarkerDrag (this, item), event);
636 case TempoMarkerItem:
638 TempoMarker* m = reinterpret_cast<TempoMarker*> (item->get_data ("marker"));
641 new TempoMarkerDrag (
644 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
651 case MeterMarkerItem:
653 MeterMarker* m = reinterpret_cast<MeterMarker*> (item->get_data ("marker"));
656 new MeterMarkerDrag (
659 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
667 _drags->set (new VideoTimeLineDrag (this, item), event);
674 case TimecodeRulerItem:
675 case SamplesRulerItem:
676 case MinsecRulerItem:
678 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
679 _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
685 case RangeMarkerBarItem:
686 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
687 _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
689 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateRangeMarker), event);
694 case CdMarkerBarItem:
695 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
696 _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
698 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateCDMarker), event);
703 case TransportMarkerBarItem:
704 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
705 _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
707 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateTransportMarker), event);
716 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
717 /* special case: allow trim of range selections in joined object mode;
718 in theory eff should equal MouseRange in this case, but it doesn't
719 because entering the range selection canvas item results in entered_regionview
720 being set to 0, so update_join_object_range_location acts as if we aren't
723 if (item_type == StartSelectionTrimItem) {
724 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
725 } else if (item_type == EndSelectionTrimItem) {
726 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
730 Editing::MouseMode eff = effective_mouse_mode ();
732 /* special case: allow drag of region fade in/out in object mode with join object/range enabled */
733 if (get_smart_mode()) {
735 case FadeInHandleItem:
736 case FadeInTrimHandleItem:
737 case FadeOutHandleItem:
738 case FadeOutTrimHandleItem:
746 /* there is no Range mode when in internal edit mode */
747 if (eff == MouseRange && internal_editing()) {
754 case StartSelectionTrimItem:
755 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
758 case EndSelectionTrimItem:
759 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
763 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
764 start_selection_grab (item, event);
766 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
767 /* grab selection for moving */
768 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionMove), event);
770 double const y = event->button.y;
771 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
773 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
774 if ( get_smart_mode() && atv) {
775 /* smart "join" mode: drag automation */
776 _drags->set (new AutomationRangeDrag (this, atv, selection->time), event, _cursors->up_down);
778 /* this was debated, but decided the more common action was to
779 make a new selection */
780 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
787 if (internal_editing()) {
788 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
789 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
793 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::RangeSelectModifier)) {
794 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionExtend), event);
796 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
802 case RegionViewNameHighlight:
803 if (!clicked_regionview->region()->locked()) {
804 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
810 if (!internal_editing()) {
811 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::RangeSelectModifier)) {
812 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionExtend), event);
814 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
824 /* Existing note: allow trimming/motion */
825 if (internal_editing()) {
826 /* trim notes if we're in internal edit mode and near the ends of the note */
827 NoteBase* cn = reinterpret_cast<NoteBase*>(item->get_data ("notebase"));
829 if (cn->big_enough_to_trim() && cn->mouse_near_ends()) {
830 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
832 _drags->set (new NoteDrag (this, item), event);
838 if (internal_editing()) {
839 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
840 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
854 /* Existing note: allow trimming/motion */
855 if (internal_editing()) {
856 NoteBase* cn = reinterpret_cast<NoteBase*> (item->get_data ("notebase"));
858 if (cn->big_enough_to_trim() && cn->mouse_near_ends()) {
859 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
861 _drags->set (new NoteDrag (this, item), event);
871 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)) &&
872 event->type == GDK_BUTTON_PRESS) {
874 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
876 } else if (event->type == GDK_BUTTON_PRESS) {
879 case FadeInHandleItem:
881 _drags->set (new FadeInDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), selection->regions), event, _cursors->fade_in);
885 case FadeOutHandleItem:
887 _drags->set (new FadeOutDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), selection->regions), event, _cursors->fade_out);
891 case StartCrossFadeItem:
892 case EndCrossFadeItem:
893 /* we might allow user to grab inside the fade to trim a region with preserve_fade_anchor. for not this is not fully implemented */
894 // if (!clicked_regionview->region()->locked()) {
895 // _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer(), true), event);
900 case FeatureLineItem:
902 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier)) {
903 remove_transient(item);
907 _drags->set (new FeatureLineDrag (this, item), event);
913 if (dynamic_cast<AutomationRegionView*> (clicked_regionview)) {
914 /* click on an automation region view; do nothing here and let the ARV's signal handler
920 if (internal_editing ()) {
924 /* click on a normal region view */
925 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
926 add_region_copy_drag (item, event, clicked_regionview);
927 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
928 add_region_brush_drag (item, event, clicked_regionview);
930 add_region_drag (item, event, clicked_regionview);
934 // if (!internal_editing() && (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE && !selection->regions.empty())) {
935 // _drags->add (new SelectionDrag (this, clicked_axisview->get_selection_rect (clicked_selection)->rect, SelectionDrag::SelectionMove));
938 _drags->start_grab (event);
942 case RegionViewNameHighlight:
943 case LeftFrameHandle:
944 case RightFrameHandle:
945 if (!clicked_regionview->region()->locked()) {
946 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
951 case FadeInTrimHandleItem:
952 case FadeOutTrimHandleItem:
953 if (!clicked_regionview->region()->locked()) {
954 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer(), true), event);
961 /* rename happens on edit clicks */
962 if (clicked_regionview->get_name_highlight()) {
963 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer()), event);
969 case ControlPointItem:
970 _drags->set (new ControlPointDrag (this, item), event);
974 case AutomationLineItem:
975 _drags->set (new LineDrag (this, item), event);
980 if (internal_editing()) {
981 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
982 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
986 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
990 case AutomationTrackItem:
992 TimeAxisView* parent = clicked_axisview->get_parent ();
993 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (clicked_axisview);
995 if (parent && dynamic_cast<MidiTimeAxisView*> (parent) && atv->show_regions ()) {
997 RouteTimeAxisView* p = dynamic_cast<RouteTimeAxisView*> (parent);
999 boost::shared_ptr<Playlist> pl = p->track()->playlist ();
1000 if (pl->n_regions() == 0) {
1001 /* Parent has no regions; create one so that we have somewhere to put automation */
1002 _drags->set (new RegionCreateDrag (this, item, parent), event);
1004 /* See if there's a region before the click that we can extend, and extend it if so */
1005 framepos_t const t = canvas_event_sample (event);
1006 boost::shared_ptr<Region> prev = pl->find_next_region (t, End, -1);
1008 _drags->set (new RegionCreateDrag (this, item, parent), event);
1010 prev->set_length (t - prev->position ());
1014 /* rubberband drag to select automation points */
1015 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
1022 if ( get_smart_mode() ) {
1023 /* we're in "smart" joined mode, and we've clicked on a Selection */
1024 double const y = event->button.y;
1025 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
1027 /* if we're over an automation track, start a drag of its data */
1028 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
1030 _drags->set (new AutomationRangeDrag (this, atv, selection->time), event, _cursors->up_down);
1033 /* if we're over a track and a region, and in the `object' part of a region,
1034 put a selection around the region and drag both
1036 /* RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1037 if (rtv && _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
1038 boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track> (rtv->route ());
1040 boost::shared_ptr<Playlist> pl = t->playlist ();
1043 boost::shared_ptr<Region> r = pl->top_region_at (canvas_event_sample (event));
1045 RegionView* rv = rtv->view()->find_view (r);
1046 clicked_selection = select_range (rv->region()->position(),
1047 rv->region()->last_frame()+1);
1048 _drags->add (new SelectionDrag (this, item, SelectionDrag::SelectionMove));
1049 list<RegionView*> rvs;
1051 _drags->add (new RegionMoveDrag (this, item, rv, rvs, false, false));
1052 _drags->start_grab (event);
1076 switch (item_type) {
1078 _drags->set (new LineDrag (this, item), event);
1081 case ControlPointItem:
1082 _drags->set (new ControlPointDrag (this, item), event);
1088 AudioRegionView* arv = dynamic_cast<AudioRegionView *> (clicked_regionview);
1090 _drags->set (new AutomationRangeDrag (this, arv, selection->time), event, _cursors->up_down);
1091 _drags->start_grab (event);
1097 case AutomationLineItem:
1098 _drags->set (new LineDrag (this, item), event);
1108 if (event->type == GDK_BUTTON_PRESS) {
1109 _drags->set (new MouseZoomDrag (this, item), event);
1116 if (internal_editing() && item_type == NoteItem ) {
1117 /* drag notes if we're in internal edit mode */
1118 NoteBase* cn = reinterpret_cast<NoteBase*>(item->get_data ("notebase"));
1120 if (cn->big_enough_to_trim()) {
1121 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
1124 } else if (clicked_regionview) {
1126 _drags->set (new TimeFXDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1132 _drags->set (new ScrubDrag (this, item), event);
1133 scrub_reversals = 0;
1134 scrub_reverse_distance = 0;
1135 last_scrub_x = event->button.x;
1136 scrubbing_direction = 0;
1137 push_canvas_cursor (_cursors->transparent);
1149 Editor::button_press_handler_2 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1151 Editing::MouseMode const eff = effective_mouse_mode ();
1154 switch (item_type) {
1156 if (internal_editing ()) {
1157 /* no region drags in internal edit mode */
1161 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
1162 add_region_copy_drag (item, event, clicked_regionview);
1164 add_region_drag (item, event, clicked_regionview);
1166 _drags->start_grab (event);
1169 case ControlPointItem:
1170 _drags->set (new ControlPointDrag (this, item), event);
1178 switch (item_type) {
1179 case RegionViewNameHighlight:
1180 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1184 case LeftFrameHandle:
1185 case RightFrameHandle:
1186 if (!internal_editing ()) {
1187 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1192 case RegionViewName:
1193 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer()), event);
1207 /* relax till release */
1213 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1214 temporal_zoom_to_frame (false, canvas_event_sample (event));
1216 temporal_zoom_to_frame (true, canvas_event_sample(event));
1229 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1231 if (event->type == GDK_2BUTTON_PRESS) {
1232 _drags->mark_double_click ();
1233 gdk_pointer_ungrab (GDK_CURRENT_TIME);
1237 if (event->type != GDK_BUTTON_PRESS) {
1241 pre_press_cursor = current_canvas_cursor;
1243 _track_canvas->grab_focus();
1245 if (_session && _session->actively_recording()) {
1249 if (internal_editing()) {
1250 bool leave_internal_edit_mode = false;
1252 switch (item_type) {
1257 if (!dynamic_cast<MidiRegionView*> (clicked_regionview) && !dynamic_cast<AutomationRegionView*> (clicked_regionview)) {
1258 leave_internal_edit_mode = true;
1262 case PlayheadCursorItem:
1264 case TempoMarkerItem:
1265 case MeterMarkerItem:
1269 case RangeMarkerBarItem:
1270 case CdMarkerBarItem:
1271 case TransportMarkerBarItem:
1273 case TimecodeRulerItem:
1274 case SamplesRulerItem:
1275 case MinsecRulerItem:
1277 /* button press on these items never does anything to
1278 change the editing mode.
1286 if (leave_internal_edit_mode) {
1287 ActionManager::do_action ("MouseMode", "toggle-internal-edit");
1291 button_selection (item, event, item_type);
1293 if (!_drags->active () &&
1294 (Keyboard::is_delete_event (&event->button) ||
1295 Keyboard::is_context_menu_event (&event->button) ||
1296 Keyboard::is_edit_event (&event->button))) {
1298 /* handled by button release */
1302 //not rolling, range mode click + join_play_range : locate the PH here
1303 if ( !_drags->active () && !_session->transport_rolling() && ( effective_mouse_mode() == MouseRange ) && Config->get_always_play_range() ) {
1304 framepos_t where = canvas_event_sample (event);
1306 _session->request_locate (where, false);
1309 switch (event->button.button) {
1311 return button_press_handler_1 (item, event, item_type);
1315 return button_press_handler_2 (item, event, item_type);
1322 return button_press_dispatch (&event->button);
1331 Editor::button_press_dispatch (GdkEventButton* ev)
1333 /* this function is intended only for buttons 4 and above.
1336 Gtkmm2ext::MouseButton b (ev->state, ev->button);
1337 return button_bindings->activate (b, Gtkmm2ext::Bindings::Press);
1341 Editor::button_release_dispatch (GdkEventButton* ev)
1343 /* this function is intended only for buttons 4 and above.
1346 Gtkmm2ext::MouseButton b (ev->state, ev->button);
1347 return button_bindings->activate (b, Gtkmm2ext::Bindings::Release);
1351 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1353 framepos_t where = canvas_event_sample (event);
1354 AutomationTimeAxisView* atv = 0;
1356 if (pre_press_cursor) {
1357 set_canvas_cursor (pre_press_cursor);
1358 pre_press_cursor = 0;
1361 /* no action if we're recording */
1363 if (_session && _session->actively_recording()) {
1367 /* see if we're finishing a drag */
1369 bool were_dragging = false;
1370 if (_drags->active ()) {
1371 bool const r = _drags->end_grab (event);
1373 /* grab dragged, so do nothing else */
1377 were_dragging = true;
1380 update_region_layering_order_editor ();
1382 /* edit events get handled here */
1384 if (!_drags->active () && Keyboard::is_edit_event (&event->button)) {
1385 switch (item_type) {
1387 show_region_properties ();
1390 case TempoMarkerItem: {
1392 TempoMarker* tempo_marker;
1394 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
1395 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
1399 if ((tempo_marker = dynamic_cast<TempoMarker*> (marker)) == 0) {
1400 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
1404 edit_tempo_marker (*tempo_marker);
1408 case MeterMarkerItem: {
1410 MeterMarker* meter_marker;
1412 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
1413 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
1417 if ((meter_marker = dynamic_cast<MeterMarker*> (marker)) == 0) {
1418 fatal << _("programming error: marker for meter is not a meter marker!") << endmsg;
1421 edit_meter_marker (*meter_marker);
1425 case RegionViewName:
1426 if (clicked_regionview->name_active()) {
1427 return mouse_rename_region (item, event);
1431 case ControlPointItem:
1432 edit_control_point (item);
1441 /* context menu events get handled here */
1442 if (Keyboard::is_context_menu_event (&event->button)) {
1444 context_click_event = *event;
1446 if (!_drags->active ()) {
1448 /* no matter which button pops up the context menu, tell the menu
1449 widget to use button 1 to drive menu selection.
1452 switch (item_type) {
1454 case FadeInHandleItem:
1455 case FadeInTrimHandleItem:
1456 case StartCrossFadeItem:
1457 popup_xfade_in_context_menu (1, event->button.time, item, item_type);
1461 case FadeOutHandleItem:
1462 case FadeOutTrimHandleItem:
1463 case EndCrossFadeItem:
1464 popup_xfade_out_context_menu (1, event->button.time, item, item_type);
1468 popup_track_context_menu (1, event->button.time, item_type, false);
1472 case RegionViewNameHighlight:
1473 case LeftFrameHandle:
1474 case RightFrameHandle:
1475 case RegionViewName:
1476 popup_track_context_menu (1, event->button.time, item_type, false);
1480 popup_track_context_menu (1, event->button.time, item_type, true);
1483 case AutomationTrackItem:
1484 popup_track_context_menu (1, event->button.time, item_type, false);
1488 case RangeMarkerBarItem:
1489 case TransportMarkerBarItem:
1490 case CdMarkerBarItem:
1494 case TimecodeRulerItem:
1495 case SamplesRulerItem:
1496 case MinsecRulerItem:
1498 popup_ruler_menu (where, item_type);
1502 marker_context_menu (&event->button, item);
1505 case TempoMarkerItem:
1506 tempo_or_meter_marker_context_menu (&event->button, item);
1509 case MeterMarkerItem:
1510 tempo_or_meter_marker_context_menu (&event->button, item);
1513 case CrossfadeViewItem:
1514 popup_track_context_menu (1, event->button.time, item_type, false);
1517 case ControlPointItem:
1518 popup_control_point_context_menu (item, event);
1529 /* delete events get handled here */
1531 Editing::MouseMode const eff = effective_mouse_mode ();
1533 if (!_drags->active () && Keyboard::is_delete_event (&event->button)) {
1535 switch (item_type) {
1536 case TempoMarkerItem:
1537 remove_tempo_marker (item);
1540 case MeterMarkerItem:
1541 remove_meter_marker (item);
1545 remove_marker (*item, event);
1549 if (eff == MouseObject) {
1550 remove_clicked_region ();
1554 case ControlPointItem:
1555 remove_control_point (item);
1559 remove_midi_note (item, event);
1568 switch (event->button.button) {
1571 switch (item_type) {
1572 /* see comments in button_press_handler */
1573 case PlayheadCursorItem:
1576 case AutomationLineItem:
1577 case StartSelectionTrimItem:
1578 case EndSelectionTrimItem:
1582 if (!_dragging_playhead) {
1583 snap_to_with_modifier (where, event, 0, true);
1584 mouse_add_new_marker (where);
1588 case CdMarkerBarItem:
1589 if (!_dragging_playhead) {
1590 // if we get here then a dragged range wasn't done
1591 snap_to_with_modifier (where, event, 0, true);
1592 mouse_add_new_marker (where, true);
1597 if (!_dragging_playhead) {
1598 snap_to_with_modifier (where, event);
1599 mouse_add_new_tempo_event (where);
1604 if (!_dragging_playhead) {
1605 mouse_add_new_meter_event (pixel_to_sample (event->button.x));
1610 case TimecodeRulerItem:
1611 case SamplesRulerItem:
1612 case MinsecRulerItem:
1623 switch (item_type) {
1624 case AutomationTrackItem:
1625 atv = dynamic_cast<AutomationTimeAxisView*>(clicked_axisview);
1627 bool with_guard_points = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
1628 atv->add_automation_event (event, where, event->button.y, with_guard_points);
1638 switch (item_type) {
1641 /* check that we didn't drag before releasing, since
1642 its really annoying to create new control
1643 points when doing this.
1645 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (clicked_regionview);
1646 if (!were_dragging && arv) {
1647 bool with_guard_points = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
1648 arv->add_gain_point_event (item, event, with_guard_points);
1654 case AutomationTrackItem: {
1655 bool with_guard_points = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
1656 dynamic_cast<AutomationTimeAxisView*>(clicked_axisview)->
1657 add_automation_event (event, where, event->button.y, with_guard_points);
1667 pop_canvas_cursor ();
1668 if (scrubbing_direction == 0) {
1669 /* no drag, just a click */
1670 switch (item_type) {
1672 play_selected_region ();
1678 /* make sure we stop */
1679 _session->request_transport_speed (0.0);
1688 /* do any (de)selection operations that should occur on button release */
1689 button_selection (item, event, item_type);
1698 switch (item_type) {
1700 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1702 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier|Keyboard::SecondaryModifier))) {
1705 // Button2 click is unused
1720 // x_style_paste (where, 1.0);
1741 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1748 /* by the time we reach here, entered_regionview and entered trackview
1749 * will have already been set as appropriate. Things are done this
1750 * way because this method isn't passed a pointer to a variable type of
1751 * thing that is entered (which may or may not be canvas item).
1752 * (e.g. the actual entered regionview)
1755 choose_canvas_cursor_on_entry (&event->crossing, item_type);
1757 switch (item_type) {
1758 case ControlPointItem:
1759 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1760 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1763 fraction = 1.0 - (cp->get_y() / cp->line().height());
1765 _verbose_cursor->set (cp->line().get_verbose_cursor_string (fraction));
1766 _verbose_cursor->show ();
1771 if (mouse_mode == MouseGain) {
1772 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1774 line->set_outline_color (ARDOUR_UI::config()->get_canvasvar_EnteredGainLine());
1779 case AutomationLineItem:
1780 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1781 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1783 line->set_outline_color (ARDOUR_UI::config()->get_canvasvar_EnteredAutomationLine());
1788 case AutomationTrackItem:
1789 AutomationTimeAxisView* atv;
1790 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1791 clear_entered_track = false;
1792 set_entered_track (atv);
1797 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1800 entered_marker = marker;
1801 marker->set_color_rgba (ARDOUR_UI::config()->get_canvasvar_EnteredMarker());
1803 case MeterMarkerItem:
1804 case TempoMarkerItem:
1807 case FadeInHandleItem:
1808 case FadeInTrimHandleItem:
1809 if (mouse_mode == MouseObject && !internal_editing()) {
1810 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
1812 RegionView* rv = static_cast<RegionView*>(item->get_data ("regionview"));
1813 rect->set_fill_color (rv->get_fill_color());
1818 case FadeOutHandleItem:
1819 case FadeOutTrimHandleItem:
1820 if (mouse_mode == MouseObject && !internal_editing()) {
1821 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
1823 RegionView* rv = static_cast<RegionView*>(item->get_data ("regionview"));
1824 rect->set_fill_color (rv->get_fill_color ());
1829 case FeatureLineItem:
1831 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1832 line->set_outline_color (0xFF0000FF);
1843 /* third pass to handle entered track status in a comprehensible way.
1846 switch (item_type) {
1848 case AutomationLineItem:
1849 case ControlPointItem:
1850 /* these do not affect the current entered track state */
1851 clear_entered_track = false;
1854 case AutomationTrackItem:
1855 /* handled above already */
1867 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent*, ItemType item_type)
1875 switch (item_type) {
1876 case ControlPointItem:
1877 _verbose_cursor->hide ();
1881 case AutomationLineItem:
1882 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1884 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1886 line->set_outline_color (al->get_line_color());
1892 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1896 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
1897 location_flags_changed (loc, this);
1900 case MeterMarkerItem:
1901 case TempoMarkerItem:
1904 case FadeInTrimHandleItem:
1905 case FadeOutTrimHandleItem:
1906 case FadeInHandleItem:
1907 case FadeOutHandleItem:
1909 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
1911 rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_InactiveFadeHandle());
1916 case AutomationTrackItem:
1919 case FeatureLineItem:
1921 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1922 line->set_outline_color (ARDOUR_UI::config()->get_canvasvar_ZeroLine());
1934 Editor::scrub (framepos_t frame, double current_x)
1938 if (scrubbing_direction == 0) {
1940 _session->request_locate (frame, false);
1941 _session->request_transport_speed (0.1);
1942 scrubbing_direction = 1;
1946 if (last_scrub_x > current_x) {
1948 /* pointer moved to the left */
1950 if (scrubbing_direction > 0) {
1952 /* we reversed direction to go backwards */
1955 scrub_reverse_distance += (int) (last_scrub_x - current_x);
1959 /* still moving to the left (backwards) */
1961 scrub_reversals = 0;
1962 scrub_reverse_distance = 0;
1964 delta = 0.01 * (last_scrub_x - current_x);
1965 _session->request_transport_speed_nonzero (_session->transport_speed() - delta);
1969 /* pointer moved to the right */
1971 if (scrubbing_direction < 0) {
1972 /* we reversed direction to go forward */
1975 scrub_reverse_distance += (int) (current_x - last_scrub_x);
1978 /* still moving to the right */
1980 scrub_reversals = 0;
1981 scrub_reverse_distance = 0;
1983 delta = 0.01 * (current_x - last_scrub_x);
1984 _session->request_transport_speed_nonzero (_session->transport_speed() + delta);
1988 /* if there have been more than 2 opposite motion moves detected, or one that moves
1989 back more than 10 pixels, reverse direction
1992 if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
1994 if (scrubbing_direction > 0) {
1995 /* was forwards, go backwards */
1996 _session->request_transport_speed (-0.1);
1997 scrubbing_direction = -1;
1999 /* was backwards, go forwards */
2000 _session->request_transport_speed (0.1);
2001 scrubbing_direction = 1;
2004 scrub_reverse_distance = 0;
2005 scrub_reversals = 0;
2009 last_scrub_x = current_x;
2013 Editor::motion_handler (ArdourCanvas::Item* /*item*/, GdkEvent* event, bool from_autoscroll)
2015 _last_motion_y = event->motion.y;
2017 if (event->motion.is_hint) {
2020 /* We call this so that MOTION_NOTIFY events continue to be
2021 delivered to the canvas. We need to do this because we set
2022 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
2023 the density of the events, at the expense of a round-trip
2024 to the server. Given that this will mostly occur on cases
2025 where DISPLAY = :0.0, and given the cost of what the motion
2026 event might do, its a good tradeoff.
2029 _track_canvas->get_pointer (x, y);
2032 if (current_stepping_trackview) {
2033 /* don't keep the persistent stepped trackview if the mouse moves */
2034 current_stepping_trackview = 0;
2035 step_timeout.disconnect ();
2038 if (_session && _session->actively_recording()) {
2039 /* Sorry. no dragging stuff around while we record */
2043 update_join_object_range_location (event->motion.y);
2045 if (_drags->active ()) {
2046 return _drags->motion_handler (event, from_autoscroll);
2053 Editor::can_remove_control_point (ArdourCanvas::Item* item)
2055 ControlPoint* control_point;
2057 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2058 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2062 AutomationLine& line = control_point->line ();
2063 if (dynamic_cast<AudioRegionGainLine*> (&line)) {
2064 /* we shouldn't remove the first or last gain point in region gain lines */
2065 if (line.is_last_point(*control_point) || line.is_first_point(*control_point)) {
2074 Editor::remove_control_point (ArdourCanvas::Item* item)
2076 if (!can_remove_control_point (item)) {
2080 ControlPoint* control_point;
2082 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2083 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2087 control_point->line().remove_point (*control_point);
2091 Editor::edit_control_point (ArdourCanvas::Item* item)
2093 ControlPoint* p = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"));
2096 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2100 ControlPointDialog d (p);
2103 if (d.run () != RESPONSE_ACCEPT) {
2107 p->line().modify_point_y (*p, d.get_y_fraction ());
2111 Editor::edit_notes (TimeAxisViewItem& tavi)
2113 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(&tavi);
2119 MidiRegionView::Selection const & s = mrv->selection();
2125 EditNoteDialog* d = new EditNoteDialog (&(*s.begin())->region_view(), s);
2129 d->signal_response().connect (sigc::bind (sigc::mem_fun (*this, &Editor::note_edit_done), d));
2133 Editor::note_edit_done (int r, EditNoteDialog* d)
2140 Editor::visible_order_range (int* low, int* high) const
2142 *low = TimeAxisView::max_order ();
2145 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
2147 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
2149 if (!rtv->hidden()) {
2151 if (*high < rtv->order()) {
2152 *high = rtv->order ();
2155 if (*low > rtv->order()) {
2156 *low = rtv->order ();
2163 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
2165 /* Either add to or set the set the region selection, unless
2166 this is an alignment click (control used)
2169 if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
2170 TimeAxisView* tv = &rv.get_time_axis_view();
2171 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
2173 if (rtv && rtv->is_track()) {
2174 speed = rtv->track()->speed();
2177 framepos_t where = get_preferred_edit_position();
2181 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
2183 align_region (rv.region(), SyncPoint, (framepos_t) (where * speed));
2185 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
2187 align_region (rv.region(), End, (framepos_t) (where * speed));
2191 align_region (rv.region(), Start, (framepos_t) (where * speed));
2198 Editor::collect_new_region_view (RegionView* rv)
2200 latest_regionviews.push_back (rv);
2204 Editor::collect_and_select_new_region_view (RegionView* rv)
2207 latest_regionviews.push_back (rv);
2211 Editor::cancel_selection ()
2213 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2214 (*i)->hide_selection ();
2217 selection->clear ();
2218 clicked_selection = 0;
2222 Editor::cancel_time_selection ()
2224 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2225 (*i)->hide_selection ();
2227 selection->time.clear ();
2228 clicked_selection = 0;
2232 Editor::point_trim (GdkEvent* event, framepos_t new_bound)
2234 RegionView* rv = clicked_regionview;
2236 /* Choose action dependant on which button was pressed */
2237 switch (event->button.button) {
2239 begin_reversible_command (_("start point trim"));
2241 if (selection->selected (rv)) {
2242 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
2243 i != selection->regions.by_layer().end(); ++i)
2245 if (!(*i)->region()->locked()) {
2246 (*i)->region()->clear_changes ();
2247 (*i)->region()->trim_front (new_bound);
2248 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2253 if (!rv->region()->locked()) {
2254 rv->region()->clear_changes ();
2255 rv->region()->trim_front (new_bound);
2256 _session->add_command(new StatefulDiffCommand (rv->region()));
2260 commit_reversible_command();
2264 begin_reversible_command (_("End point trim"));
2266 if (selection->selected (rv)) {
2268 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
2270 if (!(*i)->region()->locked()) {
2271 (*i)->region()->clear_changes();
2272 (*i)->region()->trim_end (new_bound);
2273 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2279 if (!rv->region()->locked()) {
2280 rv->region()->clear_changes ();
2281 rv->region()->trim_end (new_bound);
2282 _session->add_command (new StatefulDiffCommand (rv->region()));
2286 commit_reversible_command();
2295 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* /*event*/)
2300 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2301 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2305 Location* location = find_location_from_marker (marker, is_start);
2306 location->set_hidden (true, this);
2311 Editor::reposition_zoom_rect (framepos_t start, framepos_t end)
2313 double x1 = sample_to_pixel (start);
2314 double x2 = sample_to_pixel (end);
2315 double y2 = _full_canvas_height - 1.0;
2317 zoom_rect->set (ArdourCanvas::Rect (x1, 1.0, x2, y2));
2322 Editor::mouse_rename_region (ArdourCanvas::Item* /*item*/, GdkEvent* /*event*/)
2324 using namespace Gtkmm2ext;
2326 ArdourPrompter prompter (false);
2328 prompter.set_prompt (_("Name for region:"));
2329 prompter.set_initial_text (clicked_regionview->region()->name());
2330 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
2331 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
2332 prompter.show_all ();
2333 switch (prompter.run ()) {
2334 case Gtk::RESPONSE_ACCEPT:
2336 prompter.get_result(str);
2338 clicked_regionview->region()->set_name (str);
2347 Editor::mouse_brush_insert_region (RegionView* rv, framepos_t pos)
2349 /* no brushing without a useful snap setting */
2351 switch (_snap_mode) {
2353 return; /* can't work because it allows region to be placed anywhere */
2358 switch (_snap_type) {
2366 /* don't brush a copy over the original */
2368 if (pos == rv->region()->position()) {
2372 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
2374 if (rtv == 0 || !rtv->is_track()) {
2378 boost::shared_ptr<Playlist> playlist = rtv->playlist();
2379 double speed = rtv->track()->speed();
2381 playlist->clear_changes ();
2382 boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region(), true));
2383 playlist->add_region (new_region, (framepos_t) (pos * speed));
2384 _session->add_command (new StatefulDiffCommand (playlist));
2386 // playlist is frozen, so we have to update manually XXX this is disgusting
2388 playlist->RegionAdded (new_region); /* EMIT SIGNAL */
2392 Editor::track_height_step_timeout ()
2394 if (get_microseconds() - last_track_height_step_timestamp < 250000) {
2395 current_stepping_trackview = 0;
2402 Editor::add_region_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2404 assert (region_view);
2406 if (!region_view->region()->playlist()) {
2410 if (Config->get_edit_mode() == Splice) {
2411 _drags->add (new RegionSpliceDrag (this, item, region_view, selection->regions.by_layer()));
2413 _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), false, false));
2418 Editor::add_region_copy_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2420 assert (region_view);
2422 if (!region_view->region()->playlist()) {
2426 _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), false, true));
2430 Editor::add_region_brush_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2432 assert (region_view);
2434 if (!region_view->region()->playlist()) {
2438 if (Config->get_edit_mode() == Splice) {
2442 _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), true, false));
2444 begin_reversible_command (Operations::drag_region_brush);
2447 /** Start a grab where a time range is selected, track(s) are selected, and the
2448 * user clicks and drags a region with a modifier in order to create a new region containing
2449 * the section of the clicked region that lies within the time range.
2452 Editor::start_selection_grab (ArdourCanvas::Item* /*item*/, GdkEvent* event)
2454 if (clicked_regionview == 0) {
2458 /* lets try to create new Region for the selection */
2460 vector<boost::shared_ptr<Region> > new_regions;
2461 create_region_from_selection (new_regions);
2463 if (new_regions.empty()) {
2467 /* XXX fix me one day to use all new regions */
2469 boost::shared_ptr<Region> region (new_regions.front());
2471 /* add it to the current stream/playlist.
2473 tricky: the streamview for the track will add a new regionview. we will
2474 catch the signal it sends when it creates the regionview to
2475 set the regionview we want to then drag.
2478 latest_regionviews.clear();
2479 sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
2481 /* A selection grab currently creates two undo/redo operations, one for
2482 creating the new region and another for moving it.
2485 begin_reversible_command (Operations::selection_grab);
2487 boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
2489 playlist->clear_changes ();
2490 clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
2491 _session->add_command(new StatefulDiffCommand (playlist));
2493 commit_reversible_command ();
2497 if (latest_regionviews.empty()) {
2498 /* something went wrong */
2502 /* we need to deselect all other regionviews, and select this one
2503 i'm ignoring undo stuff, because the region creation will take care of it
2505 selection->set (latest_regionviews);
2507 _drags->set (new RegionMoveDrag (this, latest_regionviews.front()->get_canvas_group(), latest_regionviews.front(), latest_regionviews, false, false), event);
2513 if (_drags->active ()) {
2516 selection->clear ();
2521 Editor::set_internal_edit (bool yn)
2523 if (_internal_editing == yn) {
2527 _internal_editing = yn;
2530 pre_internal_mouse_mode = mouse_mode;
2531 pre_internal_snap_type = _snap_type;
2532 pre_internal_snap_mode = _snap_mode;
2534 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2535 (*i)->enter_internal_edit_mode ();
2538 set_snap_to (internal_snap_type);
2539 set_snap_mode (internal_snap_mode);
2543 internal_snap_mode = _snap_mode;
2544 internal_snap_type = _snap_type;
2546 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2547 (*i)->leave_internal_edit_mode ();
2550 if (mouse_mode == MouseDraw && pre_internal_mouse_mode != MouseDraw) {
2551 /* we were drawing .. flip back to something sensible */
2552 set_mouse_mode (pre_internal_mouse_mode);
2555 set_snap_to (pre_internal_snap_type);
2556 set_snap_mode (pre_internal_snap_mode);
2559 reset_canvas_cursor ();
2562 /** Update _join_object_range_state which indicate whether we are over the top
2563 * or bottom half of a route view, used by the `join object/range' tool
2564 * mode. Coordinates in canvas space.
2567 Editor::update_join_object_range_location (double y)
2569 if (_internal_editing || !get_smart_mode()) {
2570 _join_object_range_state = JOIN_OBJECT_RANGE_NONE;
2574 JoinObjectRangeState const old = _join_object_range_state;
2576 if (mouse_mode == MouseObject) {
2577 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2578 } else if (mouse_mode == MouseRange) {
2579 _join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
2583 if (entered_regionview) {
2585 ArdourCanvas::Duple const item_space = entered_regionview->get_canvas_group()->canvas_to_item (ArdourCanvas::Duple (0, y));
2586 double const c = item_space.y / entered_regionview->height();
2588 _join_object_range_state = c <= 0.5 ? JOIN_OBJECT_RANGE_RANGE : JOIN_OBJECT_RANGE_OBJECT;
2590 if (_join_object_range_state != old) {
2591 set_canvas_cursor (which_track_cursor ());
2594 } else if (entered_track) {
2596 RouteTimeAxisView* entered_route_view = dynamic_cast<RouteTimeAxisView*> (entered_track);
2598 if (entered_route_view) {
2599 /* track/bus ... but not in a region ... use range mode */
2600 _join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
2601 if (_join_object_range_state != old) {
2602 set_canvas_cursor (which_track_cursor ());
2605 /* Other kinds of tracks use object mode */
2606 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2607 if (_join_object_range_state != old) {
2608 set_canvas_cursor (which_track_cursor ());
2615 Editor::effective_mouse_mode () const
2617 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
2619 } else if (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE) {
2627 Editor::remove_midi_note (ArdourCanvas::Item* item, GdkEvent *)
2629 NoteBase* e = reinterpret_cast<NoteBase*> (item->get_data ("notebase"));
2632 e->region_view().delete_note (e->note ());
2635 /** Obtain the pointer position in canvas coordinates */
2637 Editor::get_pointer_position (double& x, double& y) const
2640 _track_canvas->get_pointer (px, py);
2641 _track_canvas->window_to_canvas (px, py, x, y);