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 case FadeInHandleItem:
569 case FadeInTrimHandleItem:
571 case FadeOutHandleItem:
572 case FadeOutTrimHandleItem:
574 case StartCrossFadeItem:
575 case EndCrossFadeItem:
576 if (get_smart_mode() || eff_mouse_mode != MouseRange) {
577 set_selected_regionview_from_click (press, op);
578 } else if (event->type == GDK_BUTTON_PRESS) {
579 set_selected_track_as_side_effect (op);
583 case ControlPointItem:
584 set_selected_track_as_side_effect (op);
585 if (eff_mouse_mode != MouseRange) {
586 set_selected_control_point_from_click (press, op);
591 /* for context click, select track */
592 if (event->button.button == 3) {
593 selection->clear_tracks ();
594 set_selected_track_as_side_effect (op);
598 case AutomationTrackItem:
599 set_selected_track_as_side_effect (op);
608 Editor::button_press_handler_1 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
610 /* single mouse clicks on any of these item types operate
611 independent of mouse mode, mostly because they are
612 not on the main track canvas or because we want
617 case PlayheadCursorItem:
618 _drags->set (new CursorDrag (this, *playhead_cursor, true), event);
622 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
623 hide_marker (item, event);
625 _drags->set (new MarkerDrag (this, item), event);
629 case TempoMarkerItem:
631 TempoMarker* m = reinterpret_cast<TempoMarker*> (item->get_data ("marker"));
634 new TempoMarkerDrag (
637 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
644 case MeterMarkerItem:
646 MeterMarker* m = reinterpret_cast<MeterMarker*> (item->get_data ("marker"));
649 new MeterMarkerDrag (
652 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
660 _drags->set (new VideoTimeLineDrag (this, item), event);
667 case TimecodeRulerItem:
668 case SamplesRulerItem:
669 case MinsecRulerItem:
671 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
672 _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
678 case RangeMarkerBarItem:
679 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
680 _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
682 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateRangeMarker), event);
687 case CdMarkerBarItem:
688 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
689 _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
691 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateCDMarker), event);
696 case TransportMarkerBarItem:
697 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
698 _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
700 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateTransportMarker), event);
709 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
710 /* special case: allow trim of range selections in joined object mode;
711 in theory eff should equal MouseRange in this case, but it doesn't
712 because entering the range selection canvas item results in entered_regionview
713 being set to 0, so update_join_object_range_location acts as if we aren't
716 if (item_type == StartSelectionTrimItem) {
717 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
718 } else if (item_type == EndSelectionTrimItem) {
719 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
723 Editing::MouseMode eff = effective_mouse_mode ();
725 /* special case: allow drag of region fade in/out in object mode with join object/range enabled */
726 if (get_smart_mode()) {
728 case FadeInHandleItem:
729 case FadeInTrimHandleItem:
730 case FadeOutHandleItem:
731 case FadeOutTrimHandleItem:
739 /* there is no Range mode when in internal edit mode */
740 if (eff == MouseRange && internal_editing()) {
747 case StartSelectionTrimItem:
748 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
751 case EndSelectionTrimItem:
752 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
756 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
757 start_selection_grab (item, event);
759 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
760 /* grab selection for moving */
761 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionMove), event);
763 double const y = event->button.y;
764 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
766 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
767 if ( get_smart_mode() && atv) {
768 /* smart "join" mode: drag automation */
769 _drags->set (new AutomationRangeDrag (this, atv, selection->time), event, _cursors->up_down);
771 /* this was debated, but decided the more common action was to
772 make a new selection */
773 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
780 if (internal_editing()) {
781 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
782 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
786 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::RangeSelectModifier)) {
787 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionExtend), event);
789 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
795 case RegionViewNameHighlight:
796 if (!clicked_regionview->region()->locked()) {
797 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
803 if (!internal_editing()) {
804 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::RangeSelectModifier)) {
805 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionExtend), event);
807 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
817 /* Existing note: allow trimming/motion */
818 if (internal_editing()) {
819 /* trim notes if we're in internal edit mode and near the ends of the note */
820 NoteBase* cn = reinterpret_cast<NoteBase*>(item->get_data ("notebase"));
822 if (cn->big_enough_to_trim() && cn->mouse_near_ends()) {
823 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
825 _drags->set (new NoteDrag (this, item), event);
831 if (internal_editing()) {
832 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
833 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
847 /* Existing note: allow trimming/motion */
848 if (internal_editing()) {
849 NoteBase* cn = reinterpret_cast<NoteBase*> (item->get_data ("notebase"));
851 if (cn->big_enough_to_trim() && cn->mouse_near_ends()) {
852 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
854 _drags->set (new NoteDrag (this, item), event);
864 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)) &&
865 event->type == GDK_BUTTON_PRESS) {
867 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
869 } else if (event->type == GDK_BUTTON_PRESS) {
872 case FadeInHandleItem:
874 _drags->set (new FadeInDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), selection->regions), event, _cursors->fade_in);
878 case FadeOutHandleItem:
880 _drags->set (new FadeOutDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), selection->regions), event, _cursors->fade_out);
884 case StartCrossFadeItem:
885 case EndCrossFadeItem:
886 /* we might allow user to grab inside the fade to trim a region with preserve_fade_anchor. for not this is not fully implemented */
887 // if (!clicked_regionview->region()->locked()) {
888 // _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer(), true), event);
893 case FeatureLineItem:
895 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier)) {
896 remove_transient(item);
900 _drags->set (new FeatureLineDrag (this, item), event);
906 if (dynamic_cast<AutomationRegionView*> (clicked_regionview)) {
907 /* click on an automation region view; do nothing here and let the ARV's signal handler
913 if (internal_editing ()) {
917 /* click on a normal region view */
918 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
919 add_region_copy_drag (item, event, clicked_regionview);
920 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
921 add_region_brush_drag (item, event, clicked_regionview);
923 add_region_drag (item, event, clicked_regionview);
927 // if (!internal_editing() && (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE && !selection->regions.empty())) {
928 // _drags->add (new SelectionDrag (this, clicked_axisview->get_selection_rect (clicked_selection)->rect, SelectionDrag::SelectionMove));
931 _drags->start_grab (event);
935 case RegionViewNameHighlight:
936 case LeftFrameHandle:
937 case RightFrameHandle:
938 if (!clicked_regionview->region()->locked()) {
939 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
944 case FadeInTrimHandleItem:
945 case FadeOutTrimHandleItem:
946 if (!clicked_regionview->region()->locked()) {
947 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer(), true), event);
954 /* rename happens on edit clicks */
955 if (clicked_regionview->get_name_highlight()) {
956 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer()), event);
962 case ControlPointItem:
963 _drags->set (new ControlPointDrag (this, item), event);
967 case AutomationLineItem:
968 _drags->set (new LineDrag (this, item), event);
973 if (internal_editing()) {
974 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
975 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
979 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
983 case AutomationTrackItem:
985 TimeAxisView* parent = clicked_axisview->get_parent ();
986 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (clicked_axisview);
988 if (parent && dynamic_cast<MidiTimeAxisView*> (parent) && atv->show_regions ()) {
990 RouteTimeAxisView* p = dynamic_cast<RouteTimeAxisView*> (parent);
992 boost::shared_ptr<Playlist> pl = p->track()->playlist ();
993 if (pl->n_regions() == 0) {
994 /* Parent has no regions; create one so that we have somewhere to put automation */
995 _drags->set (new RegionCreateDrag (this, item, parent), event);
997 /* See if there's a region before the click that we can extend, and extend it if so */
998 framepos_t const t = canvas_event_sample (event);
999 boost::shared_ptr<Region> prev = pl->find_next_region (t, End, -1);
1001 _drags->set (new RegionCreateDrag (this, item, parent), event);
1003 prev->set_length (t - prev->position ());
1007 /* rubberband drag to select automation points */
1008 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
1015 if ( get_smart_mode() ) {
1016 /* we're in "smart" joined mode, and we've clicked on a Selection */
1017 double const y = event->button.y;
1018 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
1020 /* if we're over an automation track, start a drag of its data */
1021 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
1023 _drags->set (new AutomationRangeDrag (this, atv, selection->time), event, _cursors->up_down);
1026 /* if we're over a track and a region, and in the `object' part of a region,
1027 put a selection around the region and drag both
1029 /* RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1030 if (rtv && _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
1031 boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track> (rtv->route ());
1033 boost::shared_ptr<Playlist> pl = t->playlist ();
1036 boost::shared_ptr<Region> r = pl->top_region_at (canvas_event_sample (event));
1038 RegionView* rv = rtv->view()->find_view (r);
1039 clicked_selection = select_range (rv->region()->position(),
1040 rv->region()->last_frame()+1);
1041 _drags->add (new SelectionDrag (this, item, SelectionDrag::SelectionMove));
1042 list<RegionView*> rvs;
1044 _drags->add (new RegionMoveDrag (this, item, rv, rvs, false, false));
1045 _drags->start_grab (event);
1069 switch (item_type) {
1071 _drags->set (new LineDrag (this, item), event);
1074 case ControlPointItem:
1075 _drags->set (new ControlPointDrag (this, item), event);
1081 AudioRegionView* arv = dynamic_cast<AudioRegionView *> (clicked_regionview);
1083 _drags->set (new AutomationRangeDrag (this, arv, selection->time), event, _cursors->up_down);
1084 _drags->start_grab (event);
1090 case AutomationLineItem:
1091 _drags->set (new LineDrag (this, item), event);
1101 if (event->type == GDK_BUTTON_PRESS) {
1102 _drags->set (new MouseZoomDrag (this, item), event);
1109 if (internal_editing() && item_type == NoteItem ) {
1110 /* drag notes if we're in internal edit mode */
1111 NoteBase* cn = reinterpret_cast<NoteBase*>(item->get_data ("notebase"));
1113 if (cn->big_enough_to_trim()) {
1114 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
1117 } else if (clicked_regionview) {
1119 _drags->set (new TimeFXDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1125 _drags->set (new ScrubDrag (this, item), event);
1126 scrub_reversals = 0;
1127 scrub_reverse_distance = 0;
1128 last_scrub_x = event->button.x;
1129 scrubbing_direction = 0;
1130 push_canvas_cursor (_cursors->transparent);
1142 Editor::button_press_handler_2 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1144 Editing::MouseMode const eff = effective_mouse_mode ();
1147 switch (item_type) {
1149 if (internal_editing ()) {
1150 /* no region drags in internal edit mode */
1154 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
1155 add_region_copy_drag (item, event, clicked_regionview);
1157 add_region_drag (item, event, clicked_regionview);
1159 _drags->start_grab (event);
1162 case ControlPointItem:
1163 _drags->set (new ControlPointDrag (this, item), event);
1171 switch (item_type) {
1172 case RegionViewNameHighlight:
1173 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1177 case LeftFrameHandle:
1178 case RightFrameHandle:
1179 if (!internal_editing ()) {
1180 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1185 case RegionViewName:
1186 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer()), event);
1200 /* relax till release */
1206 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1207 temporal_zoom_to_frame (false, canvas_event_sample (event));
1209 temporal_zoom_to_frame (true, canvas_event_sample(event));
1222 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1224 if (event->type == GDK_2BUTTON_PRESS) {
1225 _drags->mark_double_click ();
1226 gdk_pointer_ungrab (GDK_CURRENT_TIME);
1230 if (event->type != GDK_BUTTON_PRESS) {
1234 pre_press_cursor = current_canvas_cursor;
1236 _track_canvas->grab_focus();
1238 if (_session && _session->actively_recording()) {
1242 if (internal_editing()) {
1243 bool leave_internal_edit_mode = false;
1245 switch (item_type) {
1250 if (!dynamic_cast<MidiRegionView*> (clicked_regionview) && !dynamic_cast<AutomationRegionView*> (clicked_regionview)) {
1251 leave_internal_edit_mode = true;
1255 case PlayheadCursorItem:
1257 case TempoMarkerItem:
1258 case MeterMarkerItem:
1262 case RangeMarkerBarItem:
1263 case CdMarkerBarItem:
1264 case TransportMarkerBarItem:
1266 case TimecodeRulerItem:
1267 case SamplesRulerItem:
1268 case MinsecRulerItem:
1270 /* button press on these items never does anything to
1271 change the editing mode.
1279 if (leave_internal_edit_mode) {
1280 ActionManager::do_action ("MouseMode", "toggle-internal-edit");
1284 button_selection (item, event, item_type);
1286 if (!_drags->active () &&
1287 (Keyboard::is_delete_event (&event->button) ||
1288 Keyboard::is_context_menu_event (&event->button) ||
1289 Keyboard::is_edit_event (&event->button))) {
1291 /* handled by button release */
1295 //not rolling, range mode click + join_play_range : locate the PH here
1296 if ( !_drags->active () && !_session->transport_rolling() && ( effective_mouse_mode() == MouseRange ) && Config->get_always_play_range() ) {
1297 framepos_t where = canvas_event_sample (event);
1299 _session->request_locate (where, false);
1302 switch (event->button.button) {
1304 return button_press_handler_1 (item, event, item_type);
1308 return button_press_handler_2 (item, event, item_type);
1315 return button_press_dispatch (&event->button);
1324 Editor::button_press_dispatch (GdkEventButton* ev)
1326 /* this function is intended only for buttons 4 and above.
1329 Gtkmm2ext::MouseButton b (ev->state, ev->button);
1330 return button_bindings->activate (b, Gtkmm2ext::Bindings::Press);
1334 Editor::button_release_dispatch (GdkEventButton* ev)
1336 /* this function is intended only for buttons 4 and above.
1339 Gtkmm2ext::MouseButton b (ev->state, ev->button);
1340 return button_bindings->activate (b, Gtkmm2ext::Bindings::Release);
1344 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1346 framepos_t where = canvas_event_sample (event);
1347 AutomationTimeAxisView* atv = 0;
1349 if (pre_press_cursor) {
1350 set_canvas_cursor (pre_press_cursor);
1351 pre_press_cursor = 0;
1354 /* no action if we're recording */
1356 if (_session && _session->actively_recording()) {
1360 /* see if we're finishing a drag */
1362 bool were_dragging = false;
1363 if (_drags->active ()) {
1364 bool const r = _drags->end_grab (event);
1366 /* grab dragged, so do nothing else */
1370 were_dragging = true;
1373 update_region_layering_order_editor ();
1375 /* edit events get handled here */
1377 if (!_drags->active () && Keyboard::is_edit_event (&event->button)) {
1378 switch (item_type) {
1380 show_region_properties ();
1383 case TempoMarkerItem: {
1385 TempoMarker* tempo_marker;
1387 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
1388 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
1392 if ((tempo_marker = dynamic_cast<TempoMarker*> (marker)) == 0) {
1393 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
1397 edit_tempo_marker (*tempo_marker);
1401 case MeterMarkerItem: {
1403 MeterMarker* meter_marker;
1405 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
1406 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
1410 if ((meter_marker = dynamic_cast<MeterMarker*> (marker)) == 0) {
1411 fatal << _("programming error: marker for meter is not a meter marker!") << endmsg;
1414 edit_meter_marker (*meter_marker);
1418 case RegionViewName:
1419 if (clicked_regionview->name_active()) {
1420 return mouse_rename_region (item, event);
1424 case ControlPointItem:
1425 edit_control_point (item);
1434 /* context menu events get handled here */
1435 if (Keyboard::is_context_menu_event (&event->button)) {
1437 context_click_event = *event;
1439 if (!_drags->active ()) {
1441 /* no matter which button pops up the context menu, tell the menu
1442 widget to use button 1 to drive menu selection.
1445 switch (item_type) {
1447 case FadeInHandleItem:
1448 case FadeInTrimHandleItem:
1449 case StartCrossFadeItem:
1450 case LeftFrameHandle:
1451 popup_xfade_in_context_menu (1, event->button.time, item, item_type);
1455 case FadeOutHandleItem:
1456 case FadeOutTrimHandleItem:
1457 case EndCrossFadeItem:
1458 case RightFrameHandle:
1459 popup_xfade_out_context_menu (1, event->button.time, item, item_type);
1463 popup_track_context_menu (1, event->button.time, item_type, false);
1467 case RegionViewNameHighlight:
1468 case RegionViewName:
1469 popup_track_context_menu (1, event->button.time, item_type, false);
1473 popup_track_context_menu (1, event->button.time, item_type, true);
1476 case AutomationTrackItem:
1477 popup_track_context_menu (1, event->button.time, item_type, false);
1481 case RangeMarkerBarItem:
1482 case TransportMarkerBarItem:
1483 case CdMarkerBarItem:
1487 case TimecodeRulerItem:
1488 case SamplesRulerItem:
1489 case MinsecRulerItem:
1491 popup_ruler_menu (where, item_type);
1495 marker_context_menu (&event->button, item);
1498 case TempoMarkerItem:
1499 tempo_or_meter_marker_context_menu (&event->button, item);
1502 case MeterMarkerItem:
1503 tempo_or_meter_marker_context_menu (&event->button, item);
1506 case CrossfadeViewItem:
1507 popup_track_context_menu (1, event->button.time, item_type, false);
1510 case ControlPointItem:
1511 popup_control_point_context_menu (item, event);
1522 /* delete events get handled here */
1524 Editing::MouseMode const eff = effective_mouse_mode ();
1526 if (!_drags->active () && Keyboard::is_delete_event (&event->button)) {
1528 switch (item_type) {
1529 case TempoMarkerItem:
1530 remove_tempo_marker (item);
1533 case MeterMarkerItem:
1534 remove_meter_marker (item);
1538 remove_marker (*item, event);
1542 if (eff == MouseObject) {
1543 remove_clicked_region ();
1547 case ControlPointItem:
1548 remove_control_point (item);
1552 remove_midi_note (item, event);
1561 switch (event->button.button) {
1564 switch (item_type) {
1565 /* see comments in button_press_handler */
1566 case PlayheadCursorItem:
1569 case AutomationLineItem:
1570 case StartSelectionTrimItem:
1571 case EndSelectionTrimItem:
1575 if (!_dragging_playhead) {
1576 snap_to_with_modifier (where, event, 0, true);
1577 mouse_add_new_marker (where);
1581 case CdMarkerBarItem:
1582 if (!_dragging_playhead) {
1583 // if we get here then a dragged range wasn't done
1584 snap_to_with_modifier (where, event, 0, true);
1585 mouse_add_new_marker (where, true);
1590 if (!_dragging_playhead) {
1591 snap_to_with_modifier (where, event);
1592 mouse_add_new_tempo_event (where);
1597 if (!_dragging_playhead) {
1598 mouse_add_new_meter_event (pixel_to_sample (event->button.x));
1603 case TimecodeRulerItem:
1604 case SamplesRulerItem:
1605 case MinsecRulerItem:
1616 switch (item_type) {
1617 case AutomationTrackItem:
1618 atv = dynamic_cast<AutomationTimeAxisView*>(clicked_axisview);
1620 bool with_guard_points = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
1621 atv->add_automation_event (event, where, event->button.y, with_guard_points);
1631 switch (item_type) {
1634 /* check that we didn't drag before releasing, since
1635 its really annoying to create new control
1636 points when doing this.
1638 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (clicked_regionview);
1639 if (!were_dragging && arv) {
1640 bool with_guard_points = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
1641 arv->add_gain_point_event (item, event, with_guard_points);
1647 case AutomationTrackItem: {
1648 bool with_guard_points = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
1649 dynamic_cast<AutomationTimeAxisView*>(clicked_axisview)->
1650 add_automation_event (event, where, event->button.y, with_guard_points);
1660 pop_canvas_cursor ();
1661 if (scrubbing_direction == 0) {
1662 /* no drag, just a click */
1663 switch (item_type) {
1665 play_selected_region ();
1671 /* make sure we stop */
1672 _session->request_transport_speed (0.0);
1681 /* do any (de)selection operations that should occur on button release */
1682 button_selection (item, event, item_type);
1691 switch (item_type) {
1693 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1695 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier|Keyboard::SecondaryModifier))) {
1698 // Button2 click is unused
1713 // x_style_paste (where, 1.0);
1734 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1741 /* by the time we reach here, entered_regionview and entered trackview
1742 * will have already been set as appropriate. Things are done this
1743 * way because this method isn't passed a pointer to a variable type of
1744 * thing that is entered (which may or may not be canvas item).
1745 * (e.g. the actual entered regionview)
1748 choose_canvas_cursor_on_entry (&event->crossing, item_type);
1750 switch (item_type) {
1751 case ControlPointItem:
1752 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1753 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1756 fraction = 1.0 - (cp->get_y() / cp->line().height());
1758 _verbose_cursor->set (cp->line().get_verbose_cursor_string (fraction));
1759 _verbose_cursor->show ();
1764 if (mouse_mode == MouseGain) {
1765 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1767 line->set_outline_color (ARDOUR_UI::config()->get_canvasvar_EnteredGainLine());
1772 case AutomationLineItem:
1773 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1774 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1776 line->set_outline_color (ARDOUR_UI::config()->get_canvasvar_EnteredAutomationLine());
1781 case AutomationTrackItem:
1782 AutomationTimeAxisView* atv;
1783 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1784 clear_entered_track = false;
1785 set_entered_track (atv);
1790 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1793 entered_marker = marker;
1794 marker->set_color_rgba (ARDOUR_UI::config()->get_canvasvar_EnteredMarker());
1796 case MeterMarkerItem:
1797 case TempoMarkerItem:
1800 case FadeInHandleItem:
1801 case FadeInTrimHandleItem:
1802 if (mouse_mode == MouseObject && !internal_editing()) {
1803 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
1805 RegionView* rv = static_cast<RegionView*>(item->get_data ("regionview"));
1806 rect->set_fill_color (rv->get_fill_color());
1811 case FadeOutHandleItem:
1812 case FadeOutTrimHandleItem:
1813 if (mouse_mode == MouseObject && !internal_editing()) {
1814 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
1816 RegionView* rv = static_cast<RegionView*>(item->get_data ("regionview"));
1817 rect->set_fill_color (rv->get_fill_color ());
1822 case FeatureLineItem:
1824 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1825 line->set_outline_color (0xFF0000FF);
1836 /* third pass to handle entered track status in a comprehensible way.
1839 switch (item_type) {
1841 case AutomationLineItem:
1842 case ControlPointItem:
1843 /* these do not affect the current entered track state */
1844 clear_entered_track = false;
1847 case AutomationTrackItem:
1848 /* handled above already */
1860 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent*, ItemType item_type)
1868 switch (item_type) {
1869 case ControlPointItem:
1870 _verbose_cursor->hide ();
1874 case AutomationLineItem:
1875 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1877 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1879 line->set_outline_color (al->get_line_color());
1885 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1889 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
1890 location_flags_changed (loc, this);
1893 case MeterMarkerItem:
1894 case TempoMarkerItem:
1897 case FadeInTrimHandleItem:
1898 case FadeOutTrimHandleItem:
1899 case FadeInHandleItem:
1900 case FadeOutHandleItem:
1902 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
1904 rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_InactiveFadeHandle());
1909 case AutomationTrackItem:
1912 case FeatureLineItem:
1914 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1915 line->set_outline_color (ARDOUR_UI::config()->get_canvasvar_ZeroLine());
1927 Editor::scrub (framepos_t frame, double current_x)
1931 if (scrubbing_direction == 0) {
1933 _session->request_locate (frame, false);
1934 _session->request_transport_speed (0.1);
1935 scrubbing_direction = 1;
1939 if (last_scrub_x > current_x) {
1941 /* pointer moved to the left */
1943 if (scrubbing_direction > 0) {
1945 /* we reversed direction to go backwards */
1948 scrub_reverse_distance += (int) (last_scrub_x - current_x);
1952 /* still moving to the left (backwards) */
1954 scrub_reversals = 0;
1955 scrub_reverse_distance = 0;
1957 delta = 0.01 * (last_scrub_x - current_x);
1958 _session->request_transport_speed_nonzero (_session->transport_speed() - delta);
1962 /* pointer moved to the right */
1964 if (scrubbing_direction < 0) {
1965 /* we reversed direction to go forward */
1968 scrub_reverse_distance += (int) (current_x - last_scrub_x);
1971 /* still moving to the right */
1973 scrub_reversals = 0;
1974 scrub_reverse_distance = 0;
1976 delta = 0.01 * (current_x - last_scrub_x);
1977 _session->request_transport_speed_nonzero (_session->transport_speed() + delta);
1981 /* if there have been more than 2 opposite motion moves detected, or one that moves
1982 back more than 10 pixels, reverse direction
1985 if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
1987 if (scrubbing_direction > 0) {
1988 /* was forwards, go backwards */
1989 _session->request_transport_speed (-0.1);
1990 scrubbing_direction = -1;
1992 /* was backwards, go forwards */
1993 _session->request_transport_speed (0.1);
1994 scrubbing_direction = 1;
1997 scrub_reverse_distance = 0;
1998 scrub_reversals = 0;
2002 last_scrub_x = current_x;
2006 Editor::motion_handler (ArdourCanvas::Item* /*item*/, GdkEvent* event, bool from_autoscroll)
2008 _last_motion_y = event->motion.y;
2010 if (event->motion.is_hint) {
2013 /* We call this so that MOTION_NOTIFY events continue to be
2014 delivered to the canvas. We need to do this because we set
2015 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
2016 the density of the events, at the expense of a round-trip
2017 to the server. Given that this will mostly occur on cases
2018 where DISPLAY = :0.0, and given the cost of what the motion
2019 event might do, its a good tradeoff.
2022 _track_canvas->get_pointer (x, y);
2025 if (current_stepping_trackview) {
2026 /* don't keep the persistent stepped trackview if the mouse moves */
2027 current_stepping_trackview = 0;
2028 step_timeout.disconnect ();
2031 if (_session && _session->actively_recording()) {
2032 /* Sorry. no dragging stuff around while we record */
2036 update_join_object_range_location (event->motion.y);
2038 if (_drags->active ()) {
2039 return _drags->motion_handler (event, from_autoscroll);
2046 Editor::can_remove_control_point (ArdourCanvas::Item* item)
2048 ControlPoint* control_point;
2050 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2051 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2055 AutomationLine& line = control_point->line ();
2056 if (dynamic_cast<AudioRegionGainLine*> (&line)) {
2057 /* we shouldn't remove the first or last gain point in region gain lines */
2058 if (line.is_last_point(*control_point) || line.is_first_point(*control_point)) {
2067 Editor::remove_control_point (ArdourCanvas::Item* item)
2069 if (!can_remove_control_point (item)) {
2073 ControlPoint* control_point;
2075 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2076 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2080 control_point->line().remove_point (*control_point);
2084 Editor::edit_control_point (ArdourCanvas::Item* item)
2086 ControlPoint* p = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"));
2089 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2093 ControlPointDialog d (p);
2096 if (d.run () != RESPONSE_ACCEPT) {
2100 p->line().modify_point_y (*p, d.get_y_fraction ());
2104 Editor::edit_notes (TimeAxisViewItem& tavi)
2106 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(&tavi);
2112 MidiRegionView::Selection const & s = mrv->selection();
2118 EditNoteDialog* d = new EditNoteDialog (&(*s.begin())->region_view(), s);
2122 d->signal_response().connect (sigc::bind (sigc::mem_fun (*this, &Editor::note_edit_done), d));
2126 Editor::note_edit_done (int r, EditNoteDialog* d)
2133 Editor::visible_order_range (int* low, int* high) const
2135 *low = TimeAxisView::max_order ();
2138 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
2140 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
2142 if (!rtv->hidden()) {
2144 if (*high < rtv->order()) {
2145 *high = rtv->order ();
2148 if (*low > rtv->order()) {
2149 *low = rtv->order ();
2156 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
2158 /* Either add to or set the set the region selection, unless
2159 this is an alignment click (control used)
2162 if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
2163 TimeAxisView* tv = &rv.get_time_axis_view();
2164 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
2166 if (rtv && rtv->is_track()) {
2167 speed = rtv->track()->speed();
2170 framepos_t where = get_preferred_edit_position();
2174 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
2176 align_region (rv.region(), SyncPoint, (framepos_t) (where * speed));
2178 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
2180 align_region (rv.region(), End, (framepos_t) (where * speed));
2184 align_region (rv.region(), Start, (framepos_t) (where * speed));
2191 Editor::collect_new_region_view (RegionView* rv)
2193 latest_regionviews.push_back (rv);
2197 Editor::collect_and_select_new_region_view (RegionView* rv)
2200 latest_regionviews.push_back (rv);
2204 Editor::cancel_selection ()
2206 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2207 (*i)->hide_selection ();
2210 selection->clear ();
2211 clicked_selection = 0;
2215 Editor::cancel_time_selection ()
2217 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2218 (*i)->hide_selection ();
2220 selection->time.clear ();
2221 clicked_selection = 0;
2225 Editor::point_trim (GdkEvent* event, framepos_t new_bound)
2227 RegionView* rv = clicked_regionview;
2229 /* Choose action dependant on which button was pressed */
2230 switch (event->button.button) {
2232 begin_reversible_command (_("start point trim"));
2234 if (selection->selected (rv)) {
2235 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
2236 i != selection->regions.by_layer().end(); ++i)
2238 if (!(*i)->region()->locked()) {
2239 (*i)->region()->clear_changes ();
2240 (*i)->region()->trim_front (new_bound);
2241 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2246 if (!rv->region()->locked()) {
2247 rv->region()->clear_changes ();
2248 rv->region()->trim_front (new_bound);
2249 _session->add_command(new StatefulDiffCommand (rv->region()));
2253 commit_reversible_command();
2257 begin_reversible_command (_("End point trim"));
2259 if (selection->selected (rv)) {
2261 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
2263 if (!(*i)->region()->locked()) {
2264 (*i)->region()->clear_changes();
2265 (*i)->region()->trim_end (new_bound);
2266 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2272 if (!rv->region()->locked()) {
2273 rv->region()->clear_changes ();
2274 rv->region()->trim_end (new_bound);
2275 _session->add_command (new StatefulDiffCommand (rv->region()));
2279 commit_reversible_command();
2288 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* /*event*/)
2293 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2294 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2298 Location* location = find_location_from_marker (marker, is_start);
2299 location->set_hidden (true, this);
2304 Editor::reposition_zoom_rect (framepos_t start, framepos_t end)
2306 double x1 = sample_to_pixel (start);
2307 double x2 = sample_to_pixel (end);
2308 double y2 = _full_canvas_height - 1.0;
2310 zoom_rect->set (ArdourCanvas::Rect (x1, 1.0, x2, y2));
2315 Editor::mouse_rename_region (ArdourCanvas::Item* /*item*/, GdkEvent* /*event*/)
2317 using namespace Gtkmm2ext;
2319 ArdourPrompter prompter (false);
2321 prompter.set_prompt (_("Name for region:"));
2322 prompter.set_initial_text (clicked_regionview->region()->name());
2323 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
2324 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
2325 prompter.show_all ();
2326 switch (prompter.run ()) {
2327 case Gtk::RESPONSE_ACCEPT:
2329 prompter.get_result(str);
2331 clicked_regionview->region()->set_name (str);
2340 Editor::mouse_brush_insert_region (RegionView* rv, framepos_t pos)
2342 /* no brushing without a useful snap setting */
2344 switch (_snap_mode) {
2346 return; /* can't work because it allows region to be placed anywhere */
2351 switch (_snap_type) {
2359 /* don't brush a copy over the original */
2361 if (pos == rv->region()->position()) {
2365 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
2367 if (rtv == 0 || !rtv->is_track()) {
2371 boost::shared_ptr<Playlist> playlist = rtv->playlist();
2372 double speed = rtv->track()->speed();
2374 playlist->clear_changes ();
2375 boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region(), true));
2376 playlist->add_region (new_region, (framepos_t) (pos * speed));
2377 _session->add_command (new StatefulDiffCommand (playlist));
2379 // playlist is frozen, so we have to update manually XXX this is disgusting
2381 playlist->RegionAdded (new_region); /* EMIT SIGNAL */
2385 Editor::track_height_step_timeout ()
2387 if (get_microseconds() - last_track_height_step_timestamp < 250000) {
2388 current_stepping_trackview = 0;
2395 Editor::add_region_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2397 assert (region_view);
2399 if (!region_view->region()->playlist()) {
2403 if (Config->get_edit_mode() == Splice) {
2404 _drags->add (new RegionSpliceDrag (this, item, region_view, selection->regions.by_layer()));
2406 _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), false, false));
2411 Editor::add_region_copy_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2413 assert (region_view);
2415 if (!region_view->region()->playlist()) {
2419 _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), false, true));
2423 Editor::add_region_brush_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2425 assert (region_view);
2427 if (!region_view->region()->playlist()) {
2431 if (Config->get_edit_mode() == Splice) {
2435 _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), true, false));
2437 begin_reversible_command (Operations::drag_region_brush);
2440 /** Start a grab where a time range is selected, track(s) are selected, and the
2441 * user clicks and drags a region with a modifier in order to create a new region containing
2442 * the section of the clicked region that lies within the time range.
2445 Editor::start_selection_grab (ArdourCanvas::Item* /*item*/, GdkEvent* event)
2447 if (clicked_regionview == 0) {
2451 /* lets try to create new Region for the selection */
2453 vector<boost::shared_ptr<Region> > new_regions;
2454 create_region_from_selection (new_regions);
2456 if (new_regions.empty()) {
2460 /* XXX fix me one day to use all new regions */
2462 boost::shared_ptr<Region> region (new_regions.front());
2464 /* add it to the current stream/playlist.
2466 tricky: the streamview for the track will add a new regionview. we will
2467 catch the signal it sends when it creates the regionview to
2468 set the regionview we want to then drag.
2471 latest_regionviews.clear();
2472 sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
2474 /* A selection grab currently creates two undo/redo operations, one for
2475 creating the new region and another for moving it.
2478 begin_reversible_command (Operations::selection_grab);
2480 boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
2482 playlist->clear_changes ();
2483 clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
2484 _session->add_command(new StatefulDiffCommand (playlist));
2486 commit_reversible_command ();
2490 if (latest_regionviews.empty()) {
2491 /* something went wrong */
2495 /* we need to deselect all other regionviews, and select this one
2496 i'm ignoring undo stuff, because the region creation will take care of it
2498 selection->set (latest_regionviews);
2500 _drags->set (new RegionMoveDrag (this, latest_regionviews.front()->get_canvas_group(), latest_regionviews.front(), latest_regionviews, false, false), event);
2506 if (_drags->active ()) {
2509 selection->clear ();
2514 Editor::set_internal_edit (bool yn)
2516 if (_internal_editing == yn) {
2520 _internal_editing = yn;
2523 pre_internal_mouse_mode = mouse_mode;
2524 pre_internal_snap_type = _snap_type;
2525 pre_internal_snap_mode = _snap_mode;
2527 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2528 (*i)->enter_internal_edit_mode ();
2531 set_snap_to (internal_snap_type);
2532 set_snap_mode (internal_snap_mode);
2536 internal_snap_mode = _snap_mode;
2537 internal_snap_type = _snap_type;
2539 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2540 (*i)->leave_internal_edit_mode ();
2543 if (mouse_mode == MouseDraw && pre_internal_mouse_mode != MouseDraw) {
2544 /* we were drawing .. flip back to something sensible */
2545 set_mouse_mode (pre_internal_mouse_mode);
2548 set_snap_to (pre_internal_snap_type);
2549 set_snap_mode (pre_internal_snap_mode);
2552 reset_canvas_cursor ();
2555 /** Update _join_object_range_state which indicate whether we are over the top
2556 * or bottom half of a route view, used by the `join object/range' tool
2557 * mode. Coordinates in canvas space.
2560 Editor::update_join_object_range_location (double y)
2562 if (_internal_editing || !get_smart_mode()) {
2563 _join_object_range_state = JOIN_OBJECT_RANGE_NONE;
2567 JoinObjectRangeState const old = _join_object_range_state;
2569 if (mouse_mode == MouseObject) {
2570 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2571 } else if (mouse_mode == MouseRange) {
2572 _join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
2576 if (entered_regionview) {
2578 ArdourCanvas::Duple const item_space = entered_regionview->get_canvas_group()->canvas_to_item (ArdourCanvas::Duple (0, y));
2579 double const c = item_space.y / entered_regionview->height();
2581 _join_object_range_state = c <= 0.5 ? JOIN_OBJECT_RANGE_RANGE : JOIN_OBJECT_RANGE_OBJECT;
2583 if (_join_object_range_state != old) {
2584 set_canvas_cursor (which_track_cursor ());
2587 } else if (entered_track) {
2589 RouteTimeAxisView* entered_route_view = dynamic_cast<RouteTimeAxisView*> (entered_track);
2591 if (entered_route_view) {
2592 /* track/bus ... but not in a region ... use range mode */
2593 _join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
2594 if (_join_object_range_state != old) {
2595 set_canvas_cursor (which_track_cursor ());
2598 /* Other kinds of tracks use object mode */
2599 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2600 if (_join_object_range_state != old) {
2601 set_canvas_cursor (which_track_cursor ());
2608 Editor::effective_mouse_mode () const
2610 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
2612 } else if (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE) {
2620 Editor::remove_midi_note (ArdourCanvas::Item* item, GdkEvent *)
2622 NoteBase* e = reinterpret_cast<NoteBase*> (item->get_data ("notebase"));
2625 e->region_view().delete_note (e->note ());
2628 /** Obtain the pointer position in canvas coordinates */
2630 Editor::get_pointer_position (double& x, double& y) const
2633 _track_canvas->get_pointer (px, py);
2634 _track_canvas->window_to_canvas (px, py, x, y);