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.
28 #include "pbd/error.h"
29 #include "pbd/enumwriter.h"
30 #include "pbd/memento_command.h"
31 #include "pbd/basename.h"
32 #include "pbd/stateful_diff_command.h"
34 #include "gtkmm2ext/bindings.h"
35 #include "gtkmm2ext/utils.h"
36 #include "gtkmm2ext/tearoff.h"
38 #include "ardour_ui.h"
40 #include "canvas-note.h"
42 #include "time_axis_view.h"
43 #include "audio_time_axis.h"
44 #include "audio_region_view.h"
45 #include "midi_region_view.h"
47 #include "streamview.h"
48 #include "region_gain_line.h"
49 #include "automation_time_axis.h"
50 #include "control_point.h"
53 #include "selection.h"
56 #include "rgb_macros.h"
57 #include "control_point_dialog.h"
58 #include "editor_drag.h"
59 #include "automation_region_view.h"
60 #include "edit_note_dialog.h"
61 #include "mouse_cursors.h"
62 #include "editor_cursors.h"
63 #include "verbose_cursor.h"
65 #include "ardour/audioregion.h"
66 #include "ardour/operations.h"
67 #include "ardour/playlist.h"
68 #include "ardour/profile.h"
69 #include "ardour/region_factory.h"
70 #include "ardour/route.h"
71 #include "ardour/session.h"
72 #include "ardour/types.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) {
105 Gdk::ModifierType mask;
106 Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->track_canvas->get_window();
107 Glib::RefPtr<const Gdk::Window> pointer_window;
109 if (!canvas_window) {
113 pointer_window = canvas_window->get_pointer (x, y, mask);
115 if (pointer_window == track_canvas->get_bin_window()) {
118 in_track_canvas = true;
121 in_track_canvas = false;
126 event.type = GDK_BUTTON_RELEASE;
130 where = event_frame (&event, 0, 0);
135 Editor::event_frame (GdkEvent const * event, double* pcx, double* pcy) const
149 switch (event->type) {
150 case GDK_BUTTON_RELEASE:
151 case GDK_BUTTON_PRESS:
152 case GDK_2BUTTON_PRESS:
153 case GDK_3BUTTON_PRESS:
154 *pcx = event->button.x;
155 *pcy = event->button.y;
156 _trackview_group->w2i(*pcx, *pcy);
158 case GDK_MOTION_NOTIFY:
159 *pcx = event->motion.x;
160 *pcy = event->motion.y;
161 _trackview_group->w2i(*pcx, *pcy);
163 case GDK_ENTER_NOTIFY:
164 case GDK_LEAVE_NOTIFY:
165 track_canvas->w2c(event->crossing.x, event->crossing.y, *pcx, *pcy);
168 case GDK_KEY_RELEASE:
169 // track_canvas->w2c(event->key.x, event->key.y, *pcx, *pcy);
172 warning << string_compose (_("Editor::event_frame() used on unhandled event type %1"), event->type) << endmsg;
176 /* note that pixel_to_frame() never returns less than zero, so even if the pixel
177 position is negative (as can be the case with motion events in particular),
178 the frame location is always positive.
181 return pixel_to_frame (*pcx);
185 Editor::which_grabber_cursor ()
187 Gdk::Cursor* c = _cursors->grabber;
189 if (_internal_editing) {
190 switch (mouse_mode) {
192 c = _cursors->midi_pencil;
196 c = _cursors->grabber_note;
200 c = _cursors->midi_resize;
204 c = _cursors->grabber_note;
213 switch (_edit_point) {
215 c = _cursors->grabber_edit_point;
218 boost::shared_ptr<Movable> m = _movable.lock();
219 if (m && m->locked()) {
220 c = _cursors->speaker;
230 Editor::set_current_trimmable (boost::shared_ptr<Trimmable> t)
232 boost::shared_ptr<Trimmable> st = _trimmable.lock();
234 if (!st || st == t) {
236 set_canvas_cursor ();
241 Editor::set_current_movable (boost::shared_ptr<Movable> m)
243 boost::shared_ptr<Movable> sm = _movable.lock();
245 if (!sm || sm != m) {
247 set_canvas_cursor ();
252 Editor::set_canvas_cursor ()
254 switch (mouse_mode) {
256 current_canvas_cursor = _cursors->selector;
257 if (_internal_editing) {
258 current_canvas_cursor = which_grabber_cursor();
263 current_canvas_cursor = which_grabber_cursor();
267 current_canvas_cursor = _cursors->midi_pencil;
271 current_canvas_cursor = _cursors->cross_hair;
275 if (Keyboard::the_keyboard().key_is_down (GDK_Control_L)) {
276 current_canvas_cursor = _cursors->zoom_out;
278 current_canvas_cursor = _cursors->zoom_in;
283 current_canvas_cursor = _cursors->time_fx; // just use playhead
287 current_canvas_cursor = _cursors->speaker;
291 if (!_internal_editing) {
292 switch (_join_object_range_state) {
293 case JOIN_OBJECT_RANGE_NONE:
295 case JOIN_OBJECT_RANGE_OBJECT:
296 current_canvas_cursor = which_grabber_cursor ();
298 case JOIN_OBJECT_RANGE_RANGE:
299 current_canvas_cursor = _cursors->selector;
304 /* up-down cursor as a cue that automation can be dragged up and down when in join object/range mode */
305 if (!_internal_editing && get_smart_mode() ) {
307 get_pointer_position (x, y);
308 ArdourCanvas::Item* i = track_canvas->get_item_at (x, y);
309 if (i && i->property_parent() && (*i->property_parent()).get_data (X_("timeselection"))) {
310 pair<TimeAxisView*, int> tvp = trackview_by_y_position (_last_motion_y + vertical_adjustment.get_value() - canvas_timebars_vsize);
311 if (dynamic_cast<AutomationTimeAxisView*> (tvp.first)) {
312 current_canvas_cursor = _cursors->up_down;
317 set_canvas_cursor (current_canvas_cursor, true);
321 Editor::mouse_mode_object_range_toggled()
323 MouseMode m = mouse_mode;
325 Glib::RefPtr<Action> act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-range"));
327 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
329 if (tact->get_active())
330 m = MouseObject; //Smart mode turned to ON, force editing to Object mode
332 set_mouse_mode(m, true); //call this so the button styles can get updated
336 Editor::set_mouse_mode (MouseMode m, bool force)
338 if (_drags->active ()) {
342 if (!force && m == mouse_mode) {
346 Glib::RefPtr<Action> act;
350 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-range"));
354 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-object"));
358 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-draw"));
362 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-gain"));
366 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-zoom"));
370 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-timefx"));
374 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-audition"));
380 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
383 /* go there and back to ensure that the toggled handler is called to set up mouse_mode */
384 tact->set_active (false);
385 tact->set_active (true);
387 //NOTE: this will result in a call to mouse_mode_toggled which does the heavy lifting
391 Editor::mouse_mode_toggled (MouseMode m)
393 Glib::RefPtr<Action> act;
394 Glib::RefPtr<ToggleAction> tact;
398 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-range"));
402 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-object"));
406 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-draw"));
410 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-gain"));
414 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-zoom"));
418 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-timefx"));
422 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-audition"));
428 tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
431 if (!tact->get_active()) {
432 /* this was just the notification that the old mode has been
433 * left. we'll get called again with the new mode active in a
441 act = ActionManager::get_action (X_("MouseMode"), X_("toggle-internal-edit"));
442 tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
443 tact->set_active (true);
449 if (_session && mouse_mode == MouseAudition) {
450 /* stop transport and reset default speed to avoid oddness with
452 _session->request_transport_speed (0.0, true);
459 //TODO: set button styles for smart buttons
461 if ( smart_mode_action->get_active() ) {
462 if( mouse_mode == MouseObject ) { //smart active and object active
463 smart_mode_button.set_active(1);
464 smart_mode_button.set_name("smart mode button");
465 mouse_move_button.set_name("smart mode button");
466 } else { //smart active but object inactive
467 smart_mode_button.set_active(0);
468 smart_mode_button.set_name("smart mode button");
469 mouse_move_button.set_name("mouse mode button");
472 smart_mode_button.set_active(0);
473 smart_mode_button.set_name("mouse mode button");
474 mouse_move_button.set_name("mouse mode button");
478 set_canvas_cursor ();
479 set_gain_envelope_visibility ();
481 MouseModeChanged (); /* EMIT SIGNAL */
485 Editor::step_mouse_mode (bool next)
487 switch (current_mouse_mode()) {
490 if (Profile->get_sae()) {
491 set_mouse_mode (MouseZoom);
493 set_mouse_mode (MouseRange);
496 set_mouse_mode (MouseTimeFX);
501 if (next) set_mouse_mode (MouseDraw);
502 else set_mouse_mode (MouseObject);
506 if (next) set_mouse_mode (MouseZoom);
507 else set_mouse_mode (MouseRange);
512 if (Profile->get_sae()) {
513 set_mouse_mode (MouseTimeFX);
515 set_mouse_mode (MouseGain);
518 if (Profile->get_sae()) {
519 set_mouse_mode (MouseObject);
521 set_mouse_mode (MouseDraw);
527 if (next) set_mouse_mode (MouseTimeFX);
528 else set_mouse_mode (MouseZoom);
533 set_mouse_mode (MouseAudition);
535 if (Profile->get_sae()) {
536 set_mouse_mode (MouseZoom);
538 set_mouse_mode (MouseGain);
544 if (next) set_mouse_mode (MouseObject);
545 else set_mouse_mode (MouseTimeFX);
551 Editor::toggle_internal_editing_from_double_click (GdkEvent* event)
553 if (_drags->active()) {
554 _drags->end_grab (event);
557 ActionManager::do_action ("MouseMode", "toggle-internal-edit");
559 /* prevent reversion of edit cursor on button release */
561 pre_press_cursor = 0;
567 Editor::button_selection (ArdourCanvas::Item* /*item*/, GdkEvent* event, ItemType item_type)
569 /* in object/audition/timefx/gain-automation mode,
570 any button press sets the selection if the object
571 can be selected. this is a bit of hack, because
572 we want to avoid this if the mouse operation is a
575 note: not dbl-click or triple-click
577 Also note that there is no region selection in internal edit mode, otherwise
578 for operations operating on the selection (e.g. cut) it is not obvious whether
579 to cut notes or regions.
582 if (((mouse_mode != MouseObject) &&
583 (mouse_mode != MouseAudition || item_type != RegionItem) &&
584 (mouse_mode != MouseTimeFX || item_type != RegionItem) &&
585 (mouse_mode != MouseGain) &&
586 (mouse_mode != MouseDraw)) ||
587 ((event->type != GDK_BUTTON_PRESS && event->type != GDK_BUTTON_RELEASE) || event->button.button > 3) ||
588 (internal_editing() && mouse_mode != MouseTimeFX)) {
593 if (event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) {
595 if ((event->button.state & Keyboard::RelevantModifierKeyMask) && event->button.button != 1) {
597 /* almost no selection action on modified button-2 or button-3 events */
599 if (item_type != RegionItem && event->button.button != 2) {
605 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
606 bool press = (event->type == GDK_BUTTON_PRESS);
610 if (!get_smart_mode() || (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT)) {
612 if (mouse_mode != MouseRange) {
613 set_selected_regionview_from_click (press, op);
615 /* don't change the selection unless the
616 clicked track is not currently selected. if
617 so, "collapse" the selection to just this
620 if (!selection->selected (clicked_axisview)) {
621 set_selected_track_as_side_effect (Selection::Set);
625 if (mouse_mode != MouseRange) {
626 set_selected_regionview_from_click (press, op);
632 case RegionViewNameHighlight:
634 case LeftFrameHandle:
635 case RightFrameHandle:
636 if ( mouse_mode != MouseRange ) {
637 set_selected_regionview_from_click (press, op);
638 } else if (event->type == GDK_BUTTON_PRESS) {
639 set_selected_track_as_side_effect (op);
643 case FadeInHandleItem:
645 case FadeOutHandleItem:
647 case StartCrossFadeItem:
648 case EndCrossFadeItem:
649 if ( mouse_mode != MouseRange ) {
650 set_selected_regionview_from_click (press, op);
651 } else if (event->type == GDK_BUTTON_PRESS) {
652 set_selected_track_as_side_effect (op);
656 case ControlPointItem:
657 set_selected_track_as_side_effect (op);
658 if ( mouse_mode != MouseRange ) {
659 set_selected_control_point_from_click (press, op);
664 /* for context click, select track */
665 if (event->button.button == 3) {
666 selection->clear_tracks ();
667 set_selected_track_as_side_effect (op);
671 case AutomationTrackItem:
672 set_selected_track_as_side_effect (op);
681 Editor::button_press_handler_1 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
683 /* single mouse clicks on any of these item types operate
684 independent of mouse mode, mostly because they are
685 not on the main track canvas or because we want
690 case PlayheadCursorItem:
691 _drags->set (new CursorDrag (this, item, true), event);
695 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
696 hide_marker (item, event);
698 _drags->set (new MarkerDrag (this, item), event);
702 case TempoMarkerItem:
704 TempoMarker* m = reinterpret_cast<TempoMarker*> (item->get_data ("marker"));
706 if (m->tempo().movable ()) {
708 new TempoMarkerDrag (
711 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
721 case MeterMarkerItem:
723 MeterMarker* m = reinterpret_cast<MeterMarker*> (item->get_data ("marker"));
725 if (m->meter().movable ()) {
727 new MeterMarkerDrag (
730 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
743 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
744 _drags->set (new CursorDrag (this, &playhead_cursor->canvas_item, false), event);
750 case RangeMarkerBarItem:
751 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
752 _drags->set (new CursorDrag (this, &playhead_cursor->canvas_item, false), event);
754 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateRangeMarker), event);
759 case CdMarkerBarItem:
760 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
761 _drags->set (new CursorDrag (this, &playhead_cursor->canvas_item, false), event);
763 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateCDMarker), event);
768 case TransportMarkerBarItem:
769 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
770 _drags->set (new CursorDrag (this, &playhead_cursor->canvas_item, false), event);
772 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateTransportMarker), event);
781 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
782 /* special case: allow trim of range selections in joined object mode;
783 in theory eff should equal MouseRange in this case, but it doesn't
784 because entering the range selection canvas item results in entered_regionview
785 being set to 0, so update_join_object_range_location acts as if we aren't
788 if (item_type == StartSelectionTrimItem) {
789 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
790 } else if (item_type == EndSelectionTrimItem) {
791 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
795 Editing::MouseMode eff = effective_mouse_mode ();
797 /* special case: allow drag of region fade in/out in object mode with join object/range enabled */
798 if (item_type == FadeInHandleItem || item_type == FadeOutHandleItem) {
802 /* there is no Range mode when in internal edit mode */
803 if (eff == MouseRange && internal_editing()) {
810 case StartSelectionTrimItem:
811 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
814 case EndSelectionTrimItem:
815 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
819 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
820 start_selection_grab (item, event);
821 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
822 /* grab selection for moving */
823 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionMove), event);
825 double const y = event->button.y + vertical_adjustment.get_value() - canvas_timebars_vsize;
826 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
828 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
829 if ( get_smart_mode() && atv) {
830 /* smart "join" mode: drag automation */
831 _drags->set (new AutomationRangeDrag (this, atv, selection->time), event, _cursors->up_down);
833 /* this was debated, but decided the more common action was to
834 make a new selection */
835 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
842 if (internal_editing()) {
843 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
844 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
848 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::RangeSelectModifier)) {
849 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionExtend), event);
851 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
857 case RegionViewNameHighlight:
858 if (!clicked_regionview->region()->locked()) {
859 RegionSelection s = get_equivalent_regions (selection->regions, Properties::select.property_id);
860 _drags->set (new TrimDrag (this, item, clicked_regionview, s.by_layer()), event);
866 if (!internal_editing()) {
867 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::RangeSelectModifier)) {
868 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionExtend), event);
870 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
880 if (internal_editing()) {
881 /* trim notes if we're in internal edit mode and near the ends of the note */
882 ArdourCanvas::CanvasNote* cn = dynamic_cast<ArdourCanvas::CanvasNote*> (item);
883 if (cn && cn->big_enough_to_trim() && cn->mouse_near_ends()) {
884 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
886 _drags->set (new NoteDrag (this, item), event);
892 if (internal_editing()) {
893 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
894 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
908 if (internal_editing()) {
909 ArdourCanvas::CanvasNoteEvent* cn = dynamic_cast<ArdourCanvas::CanvasNoteEvent*> (item);
910 if (cn->mouse_near_ends()) {
911 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
913 _drags->set (new NoteDrag (this, item), event);
923 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)) &&
924 event->type == GDK_BUTTON_PRESS) {
926 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
928 } else if (event->type == GDK_BUTTON_PRESS) {
931 case FadeInHandleItem:
933 RegionSelection s = get_equivalent_regions (selection->regions, Properties::select.property_id);
934 _drags->set (new FadeInDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), s), event, _cursors->fade_in);
938 case FadeOutHandleItem:
940 RegionSelection s = get_equivalent_regions (selection->regions, Properties::select.property_id);
941 _drags->set (new FadeOutDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), s), event, _cursors->fade_out);
945 case StartCrossFadeItem:
946 case EndCrossFadeItem:
947 /* we might allow user to grab inside the fade to trim a region with preserve_fade_anchor. for not this is not fully implemented */
948 // if (!clicked_regionview->region()->locked()) {
949 // RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
950 // _drags->set (new TrimDrag (this, item, clicked_regionview, s.by_layer(), true), event);
955 case FeatureLineItem:
957 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier)) {
958 remove_transient(item);
962 _drags->set (new FeatureLineDrag (this, item), event);
968 if (dynamic_cast<AutomationRegionView*> (clicked_regionview)) {
969 /* click on an automation region view; do nothing here and let the ARV's signal handler
975 if (internal_editing ()) {
976 if (event->type == GDK_2BUTTON_PRESS && event->button.button == 1) {
977 Glib::RefPtr<Action> act = ActionManager::get_action (X_("MouseMode"), X_("toggle-internal-edit"));
983 /* click on a normal region view */
984 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
985 add_region_copy_drag (item, event, clicked_regionview);
986 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
987 add_region_brush_drag (item, event, clicked_regionview);
989 add_region_drag (item, event, clicked_regionview);
993 // if (!internal_editing() && (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE && !selection->regions.empty())) {
994 // _drags->add (new SelectionDrag (this, clicked_axisview->get_selection_rect (clicked_selection)->rect, SelectionDrag::SelectionMove));
997 _drags->start_grab (event);
1000 case RegionViewNameHighlight:
1001 case LeftFrameHandle:
1002 case RightFrameHandle:
1003 if (!clicked_regionview->region()->locked()) {
1004 RegionSelection s = get_equivalent_regions (selection->regions, Properties::select.property_id);
1005 _drags->set (new TrimDrag (this, item, clicked_regionview, s.by_layer()), event);
1010 case RegionViewName:
1012 /* rename happens on edit clicks */
1013 RegionSelection s = get_equivalent_regions (selection->regions, Properties::select.property_id);
1014 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, s.by_layer()), event);
1019 case ControlPointItem:
1020 _drags->set (new ControlPointDrag (this, item), event);
1024 case AutomationLineItem:
1025 _drags->set (new LineDrag (this, item), event);
1030 if (internal_editing()) {
1031 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
1032 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
1036 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
1040 case AutomationTrackItem:
1042 TimeAxisView* parent = clicked_axisview->get_parent ();
1043 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (clicked_axisview);
1045 if (parent && dynamic_cast<MidiTimeAxisView*> (parent) && atv->show_regions ()) {
1047 RouteTimeAxisView* p = dynamic_cast<RouteTimeAxisView*> (parent);
1049 boost::shared_ptr<Playlist> pl = p->track()->playlist ();
1050 if (pl->n_regions() == 0) {
1051 /* Parent has no regions; create one so that we have somewhere to put automation */
1052 _drags->set (new RegionCreateDrag (this, item, parent), event);
1054 /* See if there's a region before the click that we can extend, and extend it if so */
1055 framepos_t const t = event_frame (event);
1056 boost::shared_ptr<Region> prev = pl->find_next_region (t, End, -1);
1058 _drags->set (new RegionCreateDrag (this, item, parent), event);
1060 prev->set_length (t - prev->position ());
1064 /* rubberband drag to select automation points */
1065 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
1072 if ( get_smart_mode() ) {
1073 /* we're in "smart" joined mode, and we've clicked on a Selection */
1074 double const y = event->button.y + vertical_adjustment.get_value() - canvas_timebars_vsize;
1075 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
1077 /* if we're over an automation track, start a drag of its data */
1078 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
1080 _drags->set (new AutomationRangeDrag (this, atv, selection->time), event, _cursors->up_down);
1083 /* if we're over a track and a region, and in the `object' part of a region,
1084 put a selection around the region and drag both
1086 /* RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1087 if (rtv && _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
1088 boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track> (rtv->route ());
1090 boost::shared_ptr<Playlist> pl = t->playlist ();
1093 boost::shared_ptr<Region> r = pl->top_region_at (event_frame (event));
1095 RegionView* rv = rtv->view()->find_view (r);
1096 clicked_selection = select_range (rv->region()->position(),
1097 rv->region()->last_frame()+1);
1098 _drags->add (new SelectionDrag (this, item, SelectionDrag::SelectionMove));
1099 list<RegionView*> rvs;
1101 _drags->add (new RegionMoveDrag (this, item, rv, rvs, false, false));
1102 _drags->start_grab (event);
1114 case ImageFrameHandleStartItem:
1115 imageframe_start_handle_op(item, event) ;
1118 case ImageFrameHandleEndItem:
1119 imageframe_end_handle_op(item, event) ;
1122 case MarkerViewHandleStartItem:
1123 markerview_item_start_handle_op(item, event) ;
1126 case MarkerViewHandleEndItem:
1127 markerview_item_end_handle_op(item, event) ;
1130 case MarkerViewItem:
1131 start_markerview_grab(item, event) ;
1133 case ImageFrameItem:
1134 start_imageframe_grab(item, event) ;
1150 switch (item_type) {
1152 _drags->set (new LineDrag (this, item), event);
1155 case ControlPointItem:
1156 _drags->set (new ControlPointDrag (this, item), event);
1162 AudioRegionView* arv = dynamic_cast<AudioRegionView *> (clicked_regionview);
1164 _drags->set (new AutomationRangeDrag (this, arv, selection->time), event, _cursors->up_down);
1165 _drags->start_grab (event);
1171 case AutomationLineItem:
1172 _drags->set (new LineDrag (this, item), event);
1182 if (event->type == GDK_BUTTON_PRESS) {
1183 _drags->set (new MouseZoomDrag (this, item), event);
1190 if (internal_editing() && item_type == NoteItem) {
1191 /* drag notes if we're in internal edit mode */
1192 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
1194 } else if (clicked_regionview) {
1196 _drags->set (new TimeFXDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1202 _drags->set (new ScrubDrag (this, item), event);
1203 scrub_reversals = 0;
1204 scrub_reverse_distance = 0;
1205 last_scrub_x = event->button.x;
1206 scrubbing_direction = 0;
1207 set_canvas_cursor (_cursors->transparent);
1219 Editor::button_press_handler_2 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1221 Editing::MouseMode const eff = effective_mouse_mode ();
1224 switch (item_type) {
1226 if (internal_editing ()) {
1227 /* no region drags in internal edit mode */
1231 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
1232 add_region_copy_drag (item, event, clicked_regionview);
1234 add_region_drag (item, event, clicked_regionview);
1236 _drags->start_grab (event);
1239 case ControlPointItem:
1240 _drags->set (new ControlPointDrag (this, item), event);
1248 switch (item_type) {
1249 case RegionViewNameHighlight:
1250 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1254 case LeftFrameHandle:
1255 case RightFrameHandle:
1256 if (!internal_editing ()) {
1257 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1262 case RegionViewName:
1263 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer()), event);
1277 /* relax till release */
1283 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1284 temporal_zoom_to_frame (false, event_frame (event));
1286 temporal_zoom_to_frame (true, event_frame(event));
1299 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1301 if (event->type != GDK_BUTTON_PRESS) {
1305 Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->track_canvas->get_window();
1307 if (canvas_window) {
1308 Glib::RefPtr<const Gdk::Window> pointer_window;
1311 Gdk::ModifierType mask;
1313 pointer_window = canvas_window->get_pointer (x, y, mask);
1315 if (pointer_window == track_canvas->get_bin_window()) {
1316 track_canvas->window_to_world (x, y, wx, wy);
1320 pre_press_cursor = current_canvas_cursor;
1322 track_canvas->grab_focus();
1324 if (_session && _session->actively_recording()) {
1328 if (internal_editing()) {
1329 bool leave_internal_edit_mode = false;
1331 switch (item_type) {
1336 if (!dynamic_cast<MidiRegionView*> (clicked_regionview) && !dynamic_cast<AutomationRegionView*> (clicked_regionview)) {
1337 leave_internal_edit_mode = true;
1341 case PlayheadCursorItem:
1343 case TempoMarkerItem:
1344 case MeterMarkerItem:
1348 case RangeMarkerBarItem:
1349 case CdMarkerBarItem:
1350 case TransportMarkerBarItem:
1352 /* button press on these events never does anything to
1353 change the editing mode.
1361 if (leave_internal_edit_mode) {
1362 ActionManager::do_action ("MouseMode", "toggle-internal-edit");
1366 button_selection (item, event, item_type);
1368 if (!_drags->active () &&
1369 (Keyboard::is_delete_event (&event->button) ||
1370 Keyboard::is_context_menu_event (&event->button) ||
1371 Keyboard::is_edit_event (&event->button))) {
1373 /* handled by button release */
1377 //not rolling, range mode click + join_play_range : locate the PH here
1378 if ( !_drags->active () && !_session->transport_rolling() && ( effective_mouse_mode() == MouseRange ) && Config->get_always_play_range() ) {
1379 framepos_t where = event_frame (event, 0, 0);
1381 _session->request_locate (where, false);
1384 switch (event->button.button) {
1386 return button_press_handler_1 (item, event, item_type);
1390 return button_press_handler_2 (item, event, item_type);
1397 return button_press_dispatch (&event->button);
1406 Editor::button_press_dispatch (GdkEventButton* ev)
1408 /* this function is intended only for buttons 4 and above.
1411 Gtkmm2ext::MouseButton b (ev->state, ev->button);
1412 return button_bindings->activate (b, Gtkmm2ext::Bindings::Press);
1416 Editor::button_release_dispatch (GdkEventButton* ev)
1418 /* this function is intended only for buttons 4 and above.
1421 Gtkmm2ext::MouseButton b (ev->state, ev->button);
1422 return button_bindings->activate (b, Gtkmm2ext::Bindings::Release);
1426 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1428 framepos_t where = event_frame (event, 0, 0);
1429 AutomationTimeAxisView* atv = 0;
1431 if (pre_press_cursor) {
1432 set_canvas_cursor (pre_press_cursor);
1433 pre_press_cursor = 0;
1436 /* no action if we're recording */
1438 if (_session && _session->actively_recording()) {
1442 /* see if we're finishing a drag */
1444 bool were_dragging = false;
1445 if (_drags->active ()) {
1446 bool const r = _drags->end_grab (event);
1448 /* grab dragged, so do nothing else */
1452 were_dragging = true;
1455 update_region_layering_order_editor ();
1457 /* edit events get handled here */
1459 if (!_drags->active () && Keyboard::is_edit_event (&event->button)) {
1460 switch (item_type) {
1462 show_region_properties ();
1465 case TempoMarkerItem:
1466 edit_tempo_marker (item);
1469 case MeterMarkerItem:
1470 edit_meter_marker (item);
1473 case RegionViewName:
1474 if (clicked_regionview->name_active()) {
1475 return mouse_rename_region (item, event);
1479 case ControlPointItem:
1480 edit_control_point (item);
1485 ArdourCanvas::CanvasNoteEvent* e = dynamic_cast<ArdourCanvas::CanvasNoteEvent*> (item);
1487 edit_notes (e->region_view().selection ());
1497 /* context menu events get handled here */
1498 if (Keyboard::is_context_menu_event (&event->button)) {
1500 context_click_event = *event;
1502 if (!_drags->active ()) {
1504 /* no matter which button pops up the context menu, tell the menu
1505 widget to use button 1 to drive menu selection.
1508 switch (item_type) {
1510 case FadeInHandleItem:
1512 case FadeOutHandleItem:
1513 popup_fade_context_menu (1, event->button.time, item, item_type);
1516 case StartCrossFadeItem:
1517 popup_xfade_in_context_menu (1, event->button.time, item, item_type);
1520 case EndCrossFadeItem:
1521 popup_xfade_out_context_menu (1, event->button.time, item, item_type);
1525 popup_track_context_menu (1, event->button.time, item_type, false);
1529 case RegionViewNameHighlight:
1530 case LeftFrameHandle:
1531 case RightFrameHandle:
1532 case RegionViewName:
1533 popup_track_context_menu (1, event->button.time, item_type, false);
1537 popup_track_context_menu (1, event->button.time, item_type, true);
1540 case AutomationTrackItem:
1541 popup_track_context_menu (1, event->button.time, item_type, false);
1545 case RangeMarkerBarItem:
1546 case TransportMarkerBarItem:
1547 case CdMarkerBarItem:
1550 popup_ruler_menu (where, item_type);
1554 marker_context_menu (&event->button, item);
1557 case TempoMarkerItem:
1558 tempo_or_meter_marker_context_menu (&event->button, item);
1561 case MeterMarkerItem:
1562 tempo_or_meter_marker_context_menu (&event->button, item);
1565 case CrossfadeViewItem:
1566 popup_track_context_menu (1, event->button.time, item_type, false);
1569 case ControlPointItem:
1570 popup_control_point_context_menu (item, event);
1574 case ImageFrameItem:
1575 popup_imageframe_edit_menu(1, event->button.time, item, true) ;
1577 case ImageFrameTimeAxisItem:
1578 popup_imageframe_edit_menu(1, event->button.time, item, false) ;
1580 case MarkerViewItem:
1581 popup_marker_time_axis_edit_menu(1, event->button.time, item, true) ;
1583 case MarkerTimeAxisItem:
1584 popup_marker_time_axis_edit_menu(1, event->button.time, item, false) ;
1596 /* delete events get handled here */
1598 Editing::MouseMode const eff = effective_mouse_mode ();
1600 if (!_drags->active () && Keyboard::is_delete_event (&event->button)) {
1602 switch (item_type) {
1603 case TempoMarkerItem:
1604 remove_tempo_marker (item);
1607 case MeterMarkerItem:
1608 remove_meter_marker (item);
1612 remove_marker (*item, event);
1616 if (eff == MouseObject) {
1617 remove_clicked_region ();
1621 case ControlPointItem:
1622 remove_control_point (item);
1626 remove_midi_note (item, event);
1635 switch (event->button.button) {
1638 switch (item_type) {
1639 /* see comments in button_press_handler */
1640 case PlayheadCursorItem:
1643 case AutomationLineItem:
1644 case StartSelectionTrimItem:
1645 case EndSelectionTrimItem:
1649 if (!_dragging_playhead) {
1650 snap_to_with_modifier (where, event, 0, true);
1651 mouse_add_new_marker (where);
1655 case CdMarkerBarItem:
1656 if (!_dragging_playhead) {
1657 // if we get here then a dragged range wasn't done
1658 snap_to_with_modifier (where, event, 0, true);
1659 mouse_add_new_marker (where, true);
1664 if (!_dragging_playhead) {
1665 snap_to_with_modifier (where, event);
1666 mouse_add_new_tempo_event (where);
1671 if (!_dragging_playhead) {
1672 mouse_add_new_meter_event (pixel_to_frame (event->button.x));
1683 switch (item_type) {
1684 case AutomationTrackItem:
1685 atv = dynamic_cast<AutomationTimeAxisView*>(clicked_axisview);
1687 atv->add_automation_event (event, where, event->button.y);
1697 switch (item_type) {
1700 /* check that we didn't drag before releasing, since
1701 its really annoying to create new control
1702 points when doing this.
1704 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (clicked_regionview);
1705 if (!were_dragging && arv) {
1706 arv->add_gain_point_event (item, event);
1712 case AutomationTrackItem:
1713 dynamic_cast<AutomationTimeAxisView*>(clicked_axisview)->
1714 add_automation_event (event, where, event->button.y);
1723 set_canvas_cursor (current_canvas_cursor);
1724 if (scrubbing_direction == 0) {
1725 /* no drag, just a click */
1726 switch (item_type) {
1728 play_selected_region ();
1734 /* make sure we stop */
1735 _session->request_transport_speed (0.0);
1744 /* do any (de)selection operations that should occur on button release */
1745 button_selection (item, event, item_type);
1754 switch (item_type) {
1756 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1758 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier|Keyboard::SecondaryModifier))) {
1761 // Button2 click is unused
1776 // x_style_paste (where, 1.0);
1797 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1804 switch (item_type) {
1805 case ControlPointItem:
1806 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1807 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1808 cp->set_visible (true);
1812 at_y = cp->get_y ();
1813 cp->i2w (at_x, at_y);
1817 fraction = 1.0 - (cp->get_y() / cp->line().height());
1819 if (is_drawable() && !_drags->active ()) {
1820 set_canvas_cursor (_cursors->fader);
1823 _verbose_cursor->set (cp->line().get_verbose_cursor_string (fraction), at_x, at_y);
1824 _verbose_cursor->show ();
1829 if (mouse_mode == MouseGain) {
1830 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1832 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredGainLine.get();
1833 if (is_drawable()) {
1834 set_canvas_cursor (_cursors->fader);
1839 case AutomationLineItem:
1840 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1841 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1843 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredAutomationLine.get();
1845 if (is_drawable()) {
1846 set_canvas_cursor (_cursors->fader);
1851 case RegionViewNameHighlight:
1852 if (is_drawable() && effective_mouse_mode() == MouseObject && entered_regionview) {
1853 set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1854 _over_region_trim_target = true;
1858 case LeftFrameHandle:
1859 case RightFrameHandle:
1860 if (is_drawable() && effective_mouse_mode() == MouseObject && !internal_editing() && entered_regionview) {
1861 set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1865 case StartSelectionTrimItem:
1867 case ImageFrameHandleStartItem:
1868 case MarkerViewHandleStartItem:
1870 if (is_drawable()) {
1871 set_canvas_cursor (_cursors->left_side_trim);
1874 case EndSelectionTrimItem:
1876 case ImageFrameHandleEndItem:
1877 case MarkerViewHandleEndItem:
1879 if (is_drawable()) {
1880 set_canvas_cursor (_cursors->right_side_trim);
1884 case PlayheadCursorItem:
1885 if (is_drawable()) {
1886 switch (_edit_point) {
1888 set_canvas_cursor (_cursors->grabber_edit_point);
1891 set_canvas_cursor (_cursors->grabber);
1897 case RegionViewName:
1899 /* when the name is not an active item, the entire name highlight is for trimming */
1901 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1902 if (mouse_mode == MouseObject && is_drawable()) {
1903 set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1904 _over_region_trim_target = true;
1910 case AutomationTrackItem:
1911 if (is_drawable()) {
1912 Gdk::Cursor *cursor;
1913 switch (mouse_mode) {
1915 cursor = _cursors->selector;
1918 cursor = _cursors->zoom_in;
1921 cursor = _cursors->cross_hair;
1925 set_canvas_cursor (cursor);
1927 AutomationTimeAxisView* atv;
1928 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1929 clear_entered_track = false;
1930 set_entered_track (atv);
1936 case RangeMarkerBarItem:
1937 case TransportMarkerBarItem:
1938 case CdMarkerBarItem:
1941 if (is_drawable()) {
1942 set_canvas_cursor (_cursors->timebar);
1947 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1950 entered_marker = marker;
1951 marker->set_color_rgba (ARDOUR_UI::config()->canvasvar_EnteredMarker.get());
1953 case MeterMarkerItem:
1954 case TempoMarkerItem:
1955 if (is_drawable()) {
1956 set_canvas_cursor (_cursors->timebar);
1960 case FadeInHandleItem:
1961 if (mouse_mode == MouseObject && !internal_editing()) {
1962 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1964 rect->property_fill_color_rgba() = 0xBBBBBBAA;
1966 set_canvas_cursor (_cursors->fade_in);
1970 case FadeOutHandleItem:
1971 if (mouse_mode == MouseObject && !internal_editing()) {
1972 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1974 rect->property_fill_color_rgba() = 0xBBBBBBAA;
1976 set_canvas_cursor (_cursors->fade_out);
1979 case FeatureLineItem:
1981 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1982 line->property_fill_color_rgba() = 0xFF0000FF;
1986 if ( get_smart_mode() ) {
1987 set_canvas_cursor ();
1995 /* second pass to handle entered track status in a comprehensible way.
1998 switch (item_type) {
2000 case AutomationLineItem:
2001 case ControlPointItem:
2002 /* these do not affect the current entered track state */
2003 clear_entered_track = false;
2006 case AutomationTrackItem:
2007 /* handled above already */
2011 set_entered_track (0);
2019 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent*, ItemType item_type)
2029 switch (item_type) {
2030 case ControlPointItem:
2031 cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
2032 if (cp->line().the_list()->interpolation() != AutomationList::Discrete) {
2033 if (cp->line().npoints() > 1 && !cp->get_selected()) {
2034 cp->set_visible (false);
2038 if (is_drawable()) {
2039 set_canvas_cursor (current_canvas_cursor);
2042 _verbose_cursor->hide ();
2045 case RegionViewNameHighlight:
2046 case LeftFrameHandle:
2047 case RightFrameHandle:
2048 case StartSelectionTrimItem:
2049 case EndSelectionTrimItem:
2050 case PlayheadCursorItem:
2053 case ImageFrameHandleStartItem:
2054 case ImageFrameHandleEndItem:
2055 case MarkerViewHandleStartItem:
2056 case MarkerViewHandleEndItem:
2059 _over_region_trim_target = false;
2061 if (is_drawable()) {
2062 set_canvas_cursor (current_canvas_cursor);
2067 case AutomationLineItem:
2068 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
2070 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
2072 line->property_fill_color_rgba() = al->get_line_color();
2074 if (is_drawable()) {
2075 set_canvas_cursor (current_canvas_cursor);
2079 case RegionViewName:
2080 /* see enter_handler() for notes */
2081 _over_region_trim_target = false;
2083 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
2084 if (is_drawable() && mouse_mode == MouseObject) {
2085 set_canvas_cursor (current_canvas_cursor);
2090 case RangeMarkerBarItem:
2091 case TransportMarkerBarItem:
2092 case CdMarkerBarItem:
2096 if (is_drawable()) {
2097 set_canvas_cursor (current_canvas_cursor);
2102 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2106 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
2107 location_flags_changed (loc, this);
2110 case MeterMarkerItem:
2111 case TempoMarkerItem:
2113 if (is_drawable()) {
2114 set_canvas_cursor (current_canvas_cursor);
2119 case FadeInHandleItem:
2120 case FadeOutHandleItem:
2121 rv = static_cast<RegionView*>(item->get_data ("regionview"));
2123 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
2125 rect->property_fill_color_rgba() = rv->get_fill_color();
2128 set_canvas_cursor (current_canvas_cursor);
2131 case AutomationTrackItem:
2132 if (is_drawable()) {
2133 set_canvas_cursor (current_canvas_cursor);
2134 clear_entered_track = true;
2135 Glib::signal_idle().connect (sigc::mem_fun(*this, &Editor::left_automation_track));
2138 case FeatureLineItem:
2140 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
2141 line->property_fill_color_rgba() = (guint) ARDOUR_UI::config()->canvasvar_ZeroLine.get();;
2153 Editor::left_automation_track ()
2155 if (clear_entered_track) {
2156 set_entered_track (0);
2157 clear_entered_track = false;
2163 Editor::scrub (framepos_t frame, double current_x)
2167 if (scrubbing_direction == 0) {
2169 _session->request_locate (frame, false);
2170 _session->request_transport_speed (0.1);
2171 scrubbing_direction = 1;
2175 if (last_scrub_x > current_x) {
2177 /* pointer moved to the left */
2179 if (scrubbing_direction > 0) {
2181 /* we reversed direction to go backwards */
2184 scrub_reverse_distance += (int) (last_scrub_x - current_x);
2188 /* still moving to the left (backwards) */
2190 scrub_reversals = 0;
2191 scrub_reverse_distance = 0;
2193 delta = 0.01 * (last_scrub_x - current_x);
2194 _session->request_transport_speed_nonzero (_session->transport_speed() - delta);
2198 /* pointer moved to the right */
2200 if (scrubbing_direction < 0) {
2201 /* we reversed direction to go forward */
2204 scrub_reverse_distance += (int) (current_x - last_scrub_x);
2207 /* still moving to the right */
2209 scrub_reversals = 0;
2210 scrub_reverse_distance = 0;
2212 delta = 0.01 * (current_x - last_scrub_x);
2213 _session->request_transport_speed_nonzero (_session->transport_speed() + delta);
2217 /* if there have been more than 2 opposite motion moves detected, or one that moves
2218 back more than 10 pixels, reverse direction
2221 if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
2223 if (scrubbing_direction > 0) {
2224 /* was forwards, go backwards */
2225 _session->request_transport_speed (-0.1);
2226 scrubbing_direction = -1;
2228 /* was backwards, go forwards */
2229 _session->request_transport_speed (0.1);
2230 scrubbing_direction = 1;
2233 scrub_reverse_distance = 0;
2234 scrub_reversals = 0;
2238 last_scrub_x = current_x;
2242 Editor::motion_handler (ArdourCanvas::Item* /*item*/, GdkEvent* event, bool from_autoscroll)
2244 _last_motion_y = event->motion.y;
2246 if (event->motion.is_hint) {
2249 /* We call this so that MOTION_NOTIFY events continue to be
2250 delivered to the canvas. We need to do this because we set
2251 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
2252 the density of the events, at the expense of a round-trip
2253 to the server. Given that this will mostly occur on cases
2254 where DISPLAY = :0.0, and given the cost of what the motion
2255 event might do, its a good tradeoff.
2258 track_canvas->get_pointer (x, y);
2261 if (current_stepping_trackview) {
2262 /* don't keep the persistent stepped trackview if the mouse moves */
2263 current_stepping_trackview = 0;
2264 step_timeout.disconnect ();
2267 if (_session && _session->actively_recording()) {
2268 /* Sorry. no dragging stuff around while we record */
2272 JoinObjectRangeState const old = _join_object_range_state;
2273 update_join_object_range_location (event->motion.x, event->motion.y);
2275 if (!_internal_editing && _join_object_range_state != old) {
2276 set_canvas_cursor ();
2279 if (!_internal_editing && _over_region_trim_target) {
2280 set_canvas_cursor_for_region_view (event->motion.x, entered_regionview);
2283 bool handled = false;
2284 if (_drags->active ()) {
2285 handled = _drags->motion_handler (event, from_autoscroll);
2292 track_canvas_motion (event);
2297 Editor::can_remove_control_point (ArdourCanvas::Item* item)
2299 ControlPoint* control_point;
2301 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2302 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2306 AutomationLine& line = control_point->line ();
2307 if (dynamic_cast<AudioRegionGainLine*> (&line)) {
2308 /* we shouldn't remove the first or last gain point in region gain lines */
2309 if (line.is_last_point(*control_point) || line.is_first_point(*control_point)) {
2318 Editor::remove_control_point (ArdourCanvas::Item* item)
2320 if (!can_remove_control_point (item)) {
2324 ControlPoint* control_point;
2326 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2327 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2331 control_point->line().remove_point (*control_point);
2335 Editor::edit_control_point (ArdourCanvas::Item* item)
2337 ControlPoint* p = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"));
2340 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2344 ControlPointDialog d (p);
2345 d.set_position (Gtk::WIN_POS_MOUSE);
2348 if (d.run () != RESPONSE_ACCEPT) {
2352 p->line().modify_point_y (*p, d.get_y_fraction ());
2356 Editor::edit_notes (MidiRegionView::Selection const & s)
2362 EditNoteDialog d (&(*s.begin())->region_view(), s);
2363 d.set_position (Gtk::WIN_POS_MOUSE);
2371 Editor::visible_order_range (int* low, int* high) const
2373 *low = TimeAxisView::max_order ();
2376 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
2378 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
2380 if (!rtv->hidden()) {
2382 if (*high < rtv->order()) {
2383 *high = rtv->order ();
2386 if (*low > rtv->order()) {
2387 *low = rtv->order ();
2394 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
2396 /* Either add to or set the set the region selection, unless
2397 this is an alignment click (control used)
2400 if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
2401 TimeAxisView* tv = &rv.get_time_axis_view();
2402 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
2404 if (rtv && rtv->is_track()) {
2405 speed = rtv->track()->speed();
2408 framepos_t where = get_preferred_edit_position();
2412 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
2414 align_region (rv.region(), SyncPoint, (framepos_t) (where * speed));
2416 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
2418 align_region (rv.region(), End, (framepos_t) (where * speed));
2422 align_region (rv.region(), Start, (framepos_t) (where * speed));
2429 Editor::collect_new_region_view (RegionView* rv)
2431 latest_regionviews.push_back (rv);
2435 Editor::collect_and_select_new_region_view (RegionView* rv)
2438 latest_regionviews.push_back (rv);
2442 Editor::cancel_selection ()
2444 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2445 (*i)->hide_selection ();
2448 selection->clear ();
2449 clicked_selection = 0;
2453 Editor::cancel_time_selection ()
2455 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2456 (*i)->hide_selection ();
2458 selection->time.clear ();
2459 clicked_selection = 0;
2463 Editor::point_trim (GdkEvent* event, framepos_t new_bound)
2465 RegionView* rv = clicked_regionview;
2467 /* Choose action dependant on which button was pressed */
2468 switch (event->button.button) {
2470 begin_reversible_command (_("start point trim"));
2472 if (selection->selected (rv)) {
2473 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
2474 i != selection->regions.by_layer().end(); ++i)
2476 if (!(*i)->region()->locked()) {
2477 (*i)->region()->clear_changes ();
2478 (*i)->region()->trim_front (new_bound);
2479 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2484 if (!rv->region()->locked()) {
2485 rv->region()->clear_changes ();
2486 rv->region()->trim_front (new_bound);
2487 _session->add_command(new StatefulDiffCommand (rv->region()));
2491 commit_reversible_command();
2495 begin_reversible_command (_("End point trim"));
2497 if (selection->selected (rv)) {
2499 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
2501 if (!(*i)->region()->locked()) {
2502 (*i)->region()->clear_changes();
2503 (*i)->region()->trim_end (new_bound);
2504 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2510 if (!rv->region()->locked()) {
2511 rv->region()->clear_changes ();
2512 rv->region()->trim_end (new_bound);
2513 _session->add_command (new StatefulDiffCommand (rv->region()));
2517 commit_reversible_command();
2526 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* /*event*/)
2531 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2532 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2536 Location* location = find_location_from_marker (marker, is_start);
2537 location->set_hidden (true, this);
2542 Editor::reposition_zoom_rect (framepos_t start, framepos_t end)
2544 double x1 = frame_to_pixel (start);
2545 double x2 = frame_to_pixel (end);
2546 double y2 = full_canvas_height - 1.0;
2548 zoom_rect->property_x1() = x1;
2549 zoom_rect->property_y1() = 1.0;
2550 zoom_rect->property_x2() = x2;
2551 zoom_rect->property_y2() = y2;
2556 Editor::mouse_rename_region (ArdourCanvas::Item* /*item*/, GdkEvent* /*event*/)
2558 using namespace Gtkmm2ext;
2560 ArdourPrompter prompter (false);
2562 prompter.set_prompt (_("Name for region:"));
2563 prompter.set_initial_text (clicked_regionview->region()->name());
2564 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
2565 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
2566 prompter.show_all ();
2567 switch (prompter.run ()) {
2568 case Gtk::RESPONSE_ACCEPT:
2570 prompter.get_result(str);
2572 clicked_regionview->region()->set_name (str);
2581 Editor::mouse_brush_insert_region (RegionView* rv, framepos_t pos)
2583 /* no brushing without a useful snap setting */
2585 switch (_snap_mode) {
2587 return; /* can't work because it allows region to be placed anywhere */
2592 switch (_snap_type) {
2600 /* don't brush a copy over the original */
2602 if (pos == rv->region()->position()) {
2606 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
2608 if (rtv == 0 || !rtv->is_track()) {
2612 boost::shared_ptr<Playlist> playlist = rtv->playlist();
2613 double speed = rtv->track()->speed();
2615 playlist->clear_changes ();
2616 boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region(), true));
2617 playlist->add_region (new_region, (framepos_t) (pos * speed));
2618 _session->add_command (new StatefulDiffCommand (playlist));
2620 // playlist is frozen, so we have to update manually XXX this is disgusting
2622 playlist->RegionAdded (new_region); /* EMIT SIGNAL */
2626 Editor::track_height_step_timeout ()
2628 if (get_microseconds() - last_track_height_step_timestamp < 250000) {
2629 current_stepping_trackview = 0;
2636 Editor::add_region_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2638 assert (region_view);
2640 if (!region_view->region()->playlist()) {
2644 _region_motion_group->raise_to_top ();
2646 if (Config->get_edit_mode() == Splice) {
2647 _drags->add (new RegionSpliceDrag (this, item, region_view, selection->regions.by_layer()));
2649 RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::select.property_id);
2650 _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), false, false));
2653 /* sync the canvas to what we think is its current state */
2654 update_canvas_now();
2658 Editor::add_region_copy_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2660 assert (region_view);
2662 if (!region_view->region()->playlist()) {
2666 _region_motion_group->raise_to_top ();
2668 RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::select.property_id);
2669 _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), false, true));
2673 Editor::add_region_brush_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2675 assert (region_view);
2677 if (!region_view->region()->playlist()) {
2681 if (Config->get_edit_mode() == Splice) {
2685 RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::select.property_id);
2686 _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), true, false));
2688 begin_reversible_command (Operations::drag_region_brush);
2691 /** Start a grab where a time range is selected, track(s) are selected, and the
2692 * user clicks and drags a region with a modifier in order to create a new region containing
2693 * the section of the clicked region that lies within the time range.
2696 Editor::start_selection_grab (ArdourCanvas::Item* /*item*/, GdkEvent* event)
2698 if (clicked_regionview == 0) {
2702 /* lets try to create new Region for the selection */
2704 vector<boost::shared_ptr<Region> > new_regions;
2705 create_region_from_selection (new_regions);
2707 if (new_regions.empty()) {
2711 /* XXX fix me one day to use all new regions */
2713 boost::shared_ptr<Region> region (new_regions.front());
2715 /* add it to the current stream/playlist.
2717 tricky: the streamview for the track will add a new regionview. we will
2718 catch the signal it sends when it creates the regionview to
2719 set the regionview we want to then drag.
2722 latest_regionviews.clear();
2723 sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
2725 /* A selection grab currently creates two undo/redo operations, one for
2726 creating the new region and another for moving it.
2729 begin_reversible_command (Operations::selection_grab);
2731 boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
2733 playlist->clear_changes ();
2734 clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
2735 _session->add_command(new StatefulDiffCommand (playlist));
2737 commit_reversible_command ();
2741 if (latest_regionviews.empty()) {
2742 /* something went wrong */
2746 /* we need to deselect all other regionviews, and select this one
2747 i'm ignoring undo stuff, because the region creation will take care of it
2749 selection->set (latest_regionviews);
2751 _drags->set (new RegionMoveDrag (this, latest_regionviews.front()->get_canvas_group(), latest_regionviews.front(), latest_regionviews, false, false), event);
2757 if (_drags->active ()) {
2760 selection->clear ();
2765 Editor::set_internal_edit (bool yn)
2767 if (_internal_editing == yn) {
2771 _internal_editing = yn;
2774 pre_internal_mouse_mode = mouse_mode;
2775 pre_internal_snap_type = _snap_type;
2776 pre_internal_snap_mode = _snap_mode;
2778 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2779 (*i)->enter_internal_edit_mode ();
2782 set_snap_to (internal_snap_type);
2783 set_snap_mode (internal_snap_mode);
2787 internal_snap_mode = _snap_mode;
2788 internal_snap_type = _snap_type;
2790 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2791 (*i)->leave_internal_edit_mode ();
2794 if (mouse_mode == MouseDraw && pre_internal_mouse_mode != MouseDraw) {
2795 /* we were drawing .. flip back to something sensible */
2796 set_mouse_mode (pre_internal_mouse_mode);
2799 set_snap_to (pre_internal_snap_type);
2800 set_snap_mode (pre_internal_snap_mode);
2803 set_canvas_cursor ();
2806 /** Update _join_object_range_state which indicate whether we are over the top or bottom half of a region view,
2807 * used by the `join object/range' tool mode.
2810 Editor::update_join_object_range_location (double /*x*/, double y)
2812 /* XXX: actually, this decides based on whether the mouse is in the top
2813 or bottom half of a the waveform part RouteTimeAxisView;
2815 Note that entered_{track,regionview} is not always setup (e.g. if
2816 the mouse is over a TimeSelection), and to get a Region
2817 that we're over requires searching the playlist.
2820 if ( !get_smart_mode() ) {
2821 _join_object_range_state = JOIN_OBJECT_RANGE_NONE;
2825 if (mouse_mode == MouseObject) {
2826 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2827 } else if (mouse_mode == MouseRange) {
2828 _join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
2831 /* XXX: maybe we should make entered_track work in all cases, rather than resorting to this */
2832 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y + vertical_adjustment.get_value() - canvas_timebars_vsize);
2836 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
2841 rtv->canvas_display()->w2i (cx, cy);
2843 double const c = cy / (rtv->view()->child_height() - TimeAxisViewItem::NAME_HIGHLIGHT_SIZE);
2845 _join_object_range_state = c <= 0.5 ? JOIN_OBJECT_RANGE_RANGE : JOIN_OBJECT_RANGE_OBJECT;
2851 Editor::effective_mouse_mode () const
2853 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
2855 } else if (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE) {
2863 Editor::remove_midi_note (ArdourCanvas::Item* item, GdkEvent *)
2865 ArdourCanvas::CanvasNoteEvent* e = dynamic_cast<ArdourCanvas::CanvasNoteEvent*> (item);
2868 e->region_view().delete_note (e->note ());
2872 Editor::set_canvas_cursor_for_region_view (double x, RegionView* rv)
2876 ArdourCanvas::Group* g = rv->get_canvas_group ();
2877 ArdourCanvas::Group* p = g->get_parent_group ();
2879 /* Compute x in region view parent coordinates */
2883 double x1, x2, y1, y2;
2884 g->get_bounds (x1, y1, x2, y2);
2886 /* Halfway across the region */
2887 double const h = (x1 + x2) / 2;
2889 Trimmable::CanTrim ct = rv->region()->can_trim ();
2891 if (ct & Trimmable::FrontTrimEarlier) {
2892 set_canvas_cursor (_cursors->left_side_trim);
2894 set_canvas_cursor (_cursors->left_side_trim_right_only);
2897 if (ct & Trimmable::EndTrimLater) {
2898 set_canvas_cursor (_cursors->right_side_trim);
2900 set_canvas_cursor (_cursors->right_side_trim_left_only);
2905 /** Obtain the pointer position in world coordinates */
2907 Editor::get_pointer_position (double& x, double& y) const
2910 track_canvas->get_pointer (px, py);
2911 track_canvas->window_to_world (px, py, x, y);