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)
741 _drags->set (new VideoTimeLineDrag (this, item), event);
748 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
749 _drags->set (new CursorDrag (this, &playhead_cursor->canvas_item, false), event);
755 case RangeMarkerBarItem:
756 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
757 _drags->set (new CursorDrag (this, &playhead_cursor->canvas_item, false), event);
759 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateRangeMarker), event);
764 case CdMarkerBarItem:
765 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
766 _drags->set (new CursorDrag (this, &playhead_cursor->canvas_item, false), event);
768 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateCDMarker), event);
773 case TransportMarkerBarItem:
774 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
775 _drags->set (new CursorDrag (this, &playhead_cursor->canvas_item, false), event);
777 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateTransportMarker), event);
786 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
787 /* special case: allow trim of range selections in joined object mode;
788 in theory eff should equal MouseRange in this case, but it doesn't
789 because entering the range selection canvas item results in entered_regionview
790 being set to 0, so update_join_object_range_location acts as if we aren't
793 if (item_type == StartSelectionTrimItem) {
794 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
795 } else if (item_type == EndSelectionTrimItem) {
796 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
800 Editing::MouseMode eff = effective_mouse_mode ();
802 /* special case: allow drag of region fade in/out in object mode with join object/range enabled */
803 if (item_type == FadeInHandleItem || item_type == FadeOutHandleItem) {
807 /* there is no Range mode when in internal edit mode */
808 if (eff == MouseRange && internal_editing()) {
815 case StartSelectionTrimItem:
816 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
819 case EndSelectionTrimItem:
820 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
824 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
825 start_selection_grab (item, event);
826 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
827 /* grab selection for moving */
828 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionMove), event);
830 double const y = event->button.y + vertical_adjustment.get_value() - canvas_timebars_vsize;
831 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
833 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
834 if ( get_smart_mode() && atv) {
835 /* smart "join" mode: drag automation */
836 _drags->set (new AutomationRangeDrag (this, atv, selection->time), event, _cursors->up_down);
838 /* this was debated, but decided the more common action was to
839 make a new selection */
840 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
847 if (internal_editing()) {
848 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
849 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
853 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::RangeSelectModifier)) {
854 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionExtend), event);
856 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
862 case RegionViewNameHighlight:
863 if (!clicked_regionview->region()->locked()) {
864 RegionSelection s = get_equivalent_regions (selection->regions, Properties::select.property_id);
865 _drags->set (new TrimDrag (this, item, clicked_regionview, s.by_layer()), event);
871 if (!internal_editing()) {
872 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::RangeSelectModifier)) {
873 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionExtend), event);
875 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
885 if (internal_editing()) {
886 /* trim notes if we're in internal edit mode and near the ends of the note */
887 ArdourCanvas::CanvasNote* cn = dynamic_cast<ArdourCanvas::CanvasNote*> (item);
888 if (cn && cn->big_enough_to_trim() && cn->mouse_near_ends()) {
889 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
891 _drags->set (new NoteDrag (this, item), event);
897 if (internal_editing()) {
898 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
899 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
913 if (internal_editing()) {
914 ArdourCanvas::CanvasNoteEvent* cn = dynamic_cast<ArdourCanvas::CanvasNoteEvent*> (item);
915 if (cn->mouse_near_ends()) {
916 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
918 _drags->set (new NoteDrag (this, item), event);
928 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)) &&
929 event->type == GDK_BUTTON_PRESS) {
931 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
933 } else if (event->type == GDK_BUTTON_PRESS) {
936 case FadeInHandleItem:
938 RegionSelection s = get_equivalent_regions (selection->regions, Properties::select.property_id);
939 _drags->set (new FadeInDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), s), event, _cursors->fade_in);
943 case FadeOutHandleItem:
945 RegionSelection s = get_equivalent_regions (selection->regions, Properties::select.property_id);
946 _drags->set (new FadeOutDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), s), event, _cursors->fade_out);
950 case StartCrossFadeItem:
951 case EndCrossFadeItem:
952 /* we might allow user to grab inside the fade to trim a region with preserve_fade_anchor. for not this is not fully implemented */
953 // if (!clicked_regionview->region()->locked()) {
954 // RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
955 // _drags->set (new TrimDrag (this, item, clicked_regionview, s.by_layer(), true), event);
960 case FeatureLineItem:
962 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier)) {
963 remove_transient(item);
967 _drags->set (new FeatureLineDrag (this, item), event);
973 if (dynamic_cast<AutomationRegionView*> (clicked_regionview)) {
974 /* click on an automation region view; do nothing here and let the ARV's signal handler
980 if (internal_editing ()) {
981 if (event->type == GDK_2BUTTON_PRESS && event->button.button == 1) {
982 Glib::RefPtr<Action> act = ActionManager::get_action (X_("MouseMode"), X_("toggle-internal-edit"));
988 /* click on a normal region view */
989 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
990 add_region_copy_drag (item, event, clicked_regionview);
991 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
992 add_region_brush_drag (item, event, clicked_regionview);
994 add_region_drag (item, event, clicked_regionview);
998 // if (!internal_editing() && (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE && !selection->regions.empty())) {
999 // _drags->add (new SelectionDrag (this, clicked_axisview->get_selection_rect (clicked_selection)->rect, SelectionDrag::SelectionMove));
1002 _drags->start_grab (event);
1005 case RegionViewNameHighlight:
1006 case LeftFrameHandle:
1007 case RightFrameHandle:
1008 if (!clicked_regionview->region()->locked()) {
1009 RegionSelection s = get_equivalent_regions (selection->regions, Properties::select.property_id);
1010 _drags->set (new TrimDrag (this, item, clicked_regionview, s.by_layer()), event);
1015 case RegionViewName:
1017 /* rename happens on edit clicks */
1018 RegionSelection s = get_equivalent_regions (selection->regions, Properties::select.property_id);
1019 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, s.by_layer()), event);
1024 case ControlPointItem:
1025 _drags->set (new ControlPointDrag (this, item), event);
1029 case AutomationLineItem:
1030 _drags->set (new LineDrag (this, item), event);
1035 if (internal_editing()) {
1036 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
1037 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
1041 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
1045 case AutomationTrackItem:
1047 TimeAxisView* parent = clicked_axisview->get_parent ();
1048 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (clicked_axisview);
1050 if (parent && dynamic_cast<MidiTimeAxisView*> (parent) && atv->show_regions ()) {
1052 RouteTimeAxisView* p = dynamic_cast<RouteTimeAxisView*> (parent);
1054 boost::shared_ptr<Playlist> pl = p->track()->playlist ();
1055 if (pl->n_regions() == 0) {
1056 /* Parent has no regions; create one so that we have somewhere to put automation */
1057 _drags->set (new RegionCreateDrag (this, item, parent), event);
1059 /* See if there's a region before the click that we can extend, and extend it if so */
1060 framepos_t const t = event_frame (event);
1061 boost::shared_ptr<Region> prev = pl->find_next_region (t, End, -1);
1063 _drags->set (new RegionCreateDrag (this, item, parent), event);
1065 prev->set_length (t - prev->position ());
1069 /* rubberband drag to select automation points */
1070 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
1077 if ( get_smart_mode() ) {
1078 /* we're in "smart" joined mode, and we've clicked on a Selection */
1079 double const y = event->button.y + vertical_adjustment.get_value() - canvas_timebars_vsize;
1080 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
1082 /* if we're over an automation track, start a drag of its data */
1083 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
1085 _drags->set (new AutomationRangeDrag (this, atv, selection->time), event, _cursors->up_down);
1088 /* if we're over a track and a region, and in the `object' part of a region,
1089 put a selection around the region and drag both
1091 /* RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1092 if (rtv && _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
1093 boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track> (rtv->route ());
1095 boost::shared_ptr<Playlist> pl = t->playlist ();
1098 boost::shared_ptr<Region> r = pl->top_region_at (event_frame (event));
1100 RegionView* rv = rtv->view()->find_view (r);
1101 clicked_selection = select_range (rv->region()->position(),
1102 rv->region()->last_frame()+1);
1103 _drags->add (new SelectionDrag (this, item, SelectionDrag::SelectionMove));
1104 list<RegionView*> rvs;
1106 _drags->add (new RegionMoveDrag (this, item, rv, rvs, false, false));
1107 _drags->start_grab (event);
1119 case ImageFrameHandleStartItem:
1120 imageframe_start_handle_op(item, event) ;
1123 case ImageFrameHandleEndItem:
1124 imageframe_end_handle_op(item, event) ;
1127 case MarkerViewHandleStartItem:
1128 markerview_item_start_handle_op(item, event) ;
1131 case MarkerViewHandleEndItem:
1132 markerview_item_end_handle_op(item, event) ;
1135 case MarkerViewItem:
1136 start_markerview_grab(item, event) ;
1138 case ImageFrameItem:
1139 start_imageframe_grab(item, event) ;
1155 switch (item_type) {
1157 _drags->set (new LineDrag (this, item), event);
1160 case ControlPointItem:
1161 _drags->set (new ControlPointDrag (this, item), event);
1167 AudioRegionView* arv = dynamic_cast<AudioRegionView *> (clicked_regionview);
1169 _drags->set (new AutomationRangeDrag (this, arv, selection->time), event, _cursors->up_down);
1170 _drags->start_grab (event);
1176 case AutomationLineItem:
1177 _drags->set (new LineDrag (this, item), event);
1187 if (event->type == GDK_BUTTON_PRESS) {
1188 _drags->set (new MouseZoomDrag (this, item), event);
1195 if (internal_editing() && item_type == NoteItem) {
1196 /* drag notes if we're in internal edit mode */
1197 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
1199 } else if (clicked_regionview) {
1201 _drags->set (new TimeFXDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1207 _drags->set (new ScrubDrag (this, item), event);
1208 scrub_reversals = 0;
1209 scrub_reverse_distance = 0;
1210 last_scrub_x = event->button.x;
1211 scrubbing_direction = 0;
1212 set_canvas_cursor (_cursors->transparent);
1224 Editor::button_press_handler_2 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1226 Editing::MouseMode const eff = effective_mouse_mode ();
1229 switch (item_type) {
1231 if (internal_editing ()) {
1232 /* no region drags in internal edit mode */
1236 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
1237 add_region_copy_drag (item, event, clicked_regionview);
1239 add_region_drag (item, event, clicked_regionview);
1241 _drags->start_grab (event);
1244 case ControlPointItem:
1245 _drags->set (new ControlPointDrag (this, item), event);
1253 switch (item_type) {
1254 case RegionViewNameHighlight:
1255 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1259 case LeftFrameHandle:
1260 case RightFrameHandle:
1261 if (!internal_editing ()) {
1262 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1267 case RegionViewName:
1268 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer()), event);
1282 /* relax till release */
1288 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1289 temporal_zoom_to_frame (false, event_frame (event));
1291 temporal_zoom_to_frame (true, event_frame(event));
1304 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1306 if (event->type != GDK_BUTTON_PRESS) {
1310 Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->track_canvas->get_window();
1312 if (canvas_window) {
1313 Glib::RefPtr<const Gdk::Window> pointer_window;
1316 Gdk::ModifierType mask;
1318 pointer_window = canvas_window->get_pointer (x, y, mask);
1320 if (pointer_window == track_canvas->get_bin_window()) {
1321 track_canvas->window_to_world (x, y, wx, wy);
1325 pre_press_cursor = current_canvas_cursor;
1327 track_canvas->grab_focus();
1329 if (_session && _session->actively_recording()) {
1333 if (internal_editing()) {
1334 bool leave_internal_edit_mode = false;
1336 switch (item_type) {
1341 if (!dynamic_cast<MidiRegionView*> (clicked_regionview) && !dynamic_cast<AutomationRegionView*> (clicked_regionview)) {
1342 leave_internal_edit_mode = true;
1346 case PlayheadCursorItem:
1348 case TempoMarkerItem:
1349 case MeterMarkerItem:
1353 case RangeMarkerBarItem:
1354 case CdMarkerBarItem:
1355 case TransportMarkerBarItem:
1357 /* button press on these events never does anything to
1358 change the editing mode.
1366 if (leave_internal_edit_mode) {
1367 ActionManager::do_action ("MouseMode", "toggle-internal-edit");
1371 button_selection (item, event, item_type);
1373 if (!_drags->active () &&
1374 (Keyboard::is_delete_event (&event->button) ||
1375 Keyboard::is_context_menu_event (&event->button) ||
1376 Keyboard::is_edit_event (&event->button))) {
1378 /* handled by button release */
1382 //not rolling, range mode click + join_play_range : locate the PH here
1383 if ( !_drags->active () && !_session->transport_rolling() && ( effective_mouse_mode() == MouseRange ) && Config->get_always_play_range() ) {
1384 framepos_t where = event_frame (event, 0, 0);
1386 _session->request_locate (where, false);
1389 switch (event->button.button) {
1391 return button_press_handler_1 (item, event, item_type);
1395 return button_press_handler_2 (item, event, item_type);
1402 return button_press_dispatch (&event->button);
1411 Editor::button_press_dispatch (GdkEventButton* ev)
1413 /* this function is intended only for buttons 4 and above.
1416 Gtkmm2ext::MouseButton b (ev->state, ev->button);
1417 return button_bindings->activate (b, Gtkmm2ext::Bindings::Press);
1421 Editor::button_release_dispatch (GdkEventButton* ev)
1423 /* this function is intended only for buttons 4 and above.
1426 Gtkmm2ext::MouseButton b (ev->state, ev->button);
1427 return button_bindings->activate (b, Gtkmm2ext::Bindings::Release);
1431 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1433 framepos_t where = event_frame (event, 0, 0);
1434 AutomationTimeAxisView* atv = 0;
1436 if (pre_press_cursor) {
1437 set_canvas_cursor (pre_press_cursor);
1438 pre_press_cursor = 0;
1441 /* no action if we're recording */
1443 if (_session && _session->actively_recording()) {
1447 /* see if we're finishing a drag */
1449 bool were_dragging = false;
1450 if (_drags->active ()) {
1451 bool const r = _drags->end_grab (event);
1453 /* grab dragged, so do nothing else */
1457 were_dragging = true;
1460 update_region_layering_order_editor ();
1462 /* edit events get handled here */
1464 if (!_drags->active () && Keyboard::is_edit_event (&event->button)) {
1465 switch (item_type) {
1467 show_region_properties ();
1470 case TempoMarkerItem:
1471 edit_tempo_marker (item);
1474 case MeterMarkerItem:
1475 edit_meter_marker (item);
1478 case RegionViewName:
1479 if (clicked_regionview->name_active()) {
1480 return mouse_rename_region (item, event);
1484 case ControlPointItem:
1485 edit_control_point (item);
1490 ArdourCanvas::CanvasNoteEvent* e = dynamic_cast<ArdourCanvas::CanvasNoteEvent*> (item);
1492 edit_notes (e->region_view().selection ());
1502 /* context menu events get handled here */
1503 if (Keyboard::is_context_menu_event (&event->button)) {
1505 context_click_event = *event;
1507 if (!_drags->active ()) {
1509 /* no matter which button pops up the context menu, tell the menu
1510 widget to use button 1 to drive menu selection.
1513 switch (item_type) {
1515 case FadeInHandleItem:
1517 case FadeOutHandleItem:
1518 popup_fade_context_menu (1, event->button.time, item, item_type);
1521 case StartCrossFadeItem:
1522 popup_xfade_in_context_menu (1, event->button.time, item, item_type);
1525 case EndCrossFadeItem:
1526 popup_xfade_out_context_menu (1, event->button.time, item, item_type);
1530 popup_track_context_menu (1, event->button.time, item_type, false);
1534 case RegionViewNameHighlight:
1535 case LeftFrameHandle:
1536 case RightFrameHandle:
1537 case RegionViewName:
1538 popup_track_context_menu (1, event->button.time, item_type, false);
1542 popup_track_context_menu (1, event->button.time, item_type, true);
1545 case AutomationTrackItem:
1546 popup_track_context_menu (1, event->button.time, item_type, false);
1550 case RangeMarkerBarItem:
1551 case TransportMarkerBarItem:
1552 case CdMarkerBarItem:
1556 popup_ruler_menu (where, item_type);
1560 marker_context_menu (&event->button, item);
1563 case TempoMarkerItem:
1564 tempo_or_meter_marker_context_menu (&event->button, item);
1567 case MeterMarkerItem:
1568 tempo_or_meter_marker_context_menu (&event->button, item);
1571 case CrossfadeViewItem:
1572 popup_track_context_menu (1, event->button.time, item_type, false);
1575 case ControlPointItem:
1576 popup_control_point_context_menu (item, event);
1580 case ImageFrameItem:
1581 popup_imageframe_edit_menu(1, event->button.time, item, true) ;
1583 case ImageFrameTimeAxisItem:
1584 popup_imageframe_edit_menu(1, event->button.time, item, false) ;
1586 case MarkerViewItem:
1587 popup_marker_time_axis_edit_menu(1, event->button.time, item, true) ;
1589 case MarkerTimeAxisItem:
1590 popup_marker_time_axis_edit_menu(1, event->button.time, item, false) ;
1602 /* delete events get handled here */
1604 Editing::MouseMode const eff = effective_mouse_mode ();
1606 if (!_drags->active () && Keyboard::is_delete_event (&event->button)) {
1608 switch (item_type) {
1609 case TempoMarkerItem:
1610 remove_tempo_marker (item);
1613 case MeterMarkerItem:
1614 remove_meter_marker (item);
1618 remove_marker (*item, event);
1622 if (eff == MouseObject) {
1623 remove_clicked_region ();
1627 case ControlPointItem:
1628 remove_control_point (item);
1632 remove_midi_note (item, event);
1641 switch (event->button.button) {
1644 switch (item_type) {
1645 /* see comments in button_press_handler */
1646 case PlayheadCursorItem:
1649 case AutomationLineItem:
1650 case StartSelectionTrimItem:
1651 case EndSelectionTrimItem:
1655 if (!_dragging_playhead) {
1656 snap_to_with_modifier (where, event, 0, true);
1657 mouse_add_new_marker (where);
1661 case CdMarkerBarItem:
1662 if (!_dragging_playhead) {
1663 // if we get here then a dragged range wasn't done
1664 snap_to_with_modifier (where, event, 0, true);
1665 mouse_add_new_marker (where, true);
1670 if (!_dragging_playhead) {
1671 snap_to_with_modifier (where, event);
1672 mouse_add_new_tempo_event (where);
1677 if (!_dragging_playhead) {
1678 mouse_add_new_meter_event (pixel_to_frame (event->button.x));
1689 switch (item_type) {
1690 case AutomationTrackItem:
1691 atv = dynamic_cast<AutomationTimeAxisView*>(clicked_axisview);
1693 atv->add_automation_event (event, where, event->button.y);
1703 switch (item_type) {
1706 /* check that we didn't drag before releasing, since
1707 its really annoying to create new control
1708 points when doing this.
1710 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (clicked_regionview);
1711 if (!were_dragging && arv) {
1712 arv->add_gain_point_event (item, event);
1718 case AutomationTrackItem:
1719 dynamic_cast<AutomationTimeAxisView*>(clicked_axisview)->
1720 add_automation_event (event, where, event->button.y);
1729 set_canvas_cursor (current_canvas_cursor);
1730 if (scrubbing_direction == 0) {
1731 /* no drag, just a click */
1732 switch (item_type) {
1734 play_selected_region ();
1740 /* make sure we stop */
1741 _session->request_transport_speed (0.0);
1750 /* do any (de)selection operations that should occur on button release */
1751 button_selection (item, event, item_type);
1760 switch (item_type) {
1762 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1764 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier|Keyboard::SecondaryModifier))) {
1767 // Button2 click is unused
1782 // x_style_paste (where, 1.0);
1803 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1810 switch (item_type) {
1811 case ControlPointItem:
1812 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1813 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1814 cp->set_visible (true);
1818 at_y = cp->get_y ();
1819 cp->i2w (at_x, at_y);
1823 fraction = 1.0 - (cp->get_y() / cp->line().height());
1825 if (is_drawable() && !_drags->active ()) {
1826 set_canvas_cursor (_cursors->fader);
1829 _verbose_cursor->set (cp->line().get_verbose_cursor_string (fraction), at_x, at_y);
1830 _verbose_cursor->show ();
1835 if (mouse_mode == MouseGain) {
1836 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1838 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredGainLine.get();
1839 if (is_drawable()) {
1840 set_canvas_cursor (_cursors->fader);
1845 case AutomationLineItem:
1846 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1847 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1849 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredAutomationLine.get();
1851 if (is_drawable()) {
1852 set_canvas_cursor (_cursors->fader);
1857 case RegionViewNameHighlight:
1858 if (is_drawable() && effective_mouse_mode() == MouseObject && entered_regionview) {
1859 set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1860 _over_region_trim_target = true;
1864 case LeftFrameHandle:
1865 case RightFrameHandle:
1866 if (is_drawable() && effective_mouse_mode() == MouseObject && !internal_editing() && entered_regionview) {
1867 set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1871 case StartSelectionTrimItem:
1873 case ImageFrameHandleStartItem:
1874 case MarkerViewHandleStartItem:
1876 if (is_drawable()) {
1877 set_canvas_cursor (_cursors->left_side_trim);
1880 case EndSelectionTrimItem:
1882 case ImageFrameHandleEndItem:
1883 case MarkerViewHandleEndItem:
1885 if (is_drawable()) {
1886 set_canvas_cursor (_cursors->right_side_trim);
1890 case PlayheadCursorItem:
1891 if (is_drawable()) {
1892 switch (_edit_point) {
1894 set_canvas_cursor (_cursors->grabber_edit_point);
1897 set_canvas_cursor (_cursors->grabber);
1903 case RegionViewName:
1905 /* when the name is not an active item, the entire name highlight is for trimming */
1907 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1908 if (mouse_mode == MouseObject && is_drawable()) {
1909 set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1910 _over_region_trim_target = true;
1916 case AutomationTrackItem:
1917 if (is_drawable()) {
1918 Gdk::Cursor *cursor;
1919 switch (mouse_mode) {
1921 cursor = _cursors->selector;
1924 cursor = _cursors->zoom_in;
1927 cursor = _cursors->cross_hair;
1931 set_canvas_cursor (cursor);
1933 AutomationTimeAxisView* atv;
1934 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1935 clear_entered_track = false;
1936 set_entered_track (atv);
1942 case RangeMarkerBarItem:
1943 case TransportMarkerBarItem:
1944 case CdMarkerBarItem:
1947 if (is_drawable()) {
1948 set_canvas_cursor (_cursors->timebar);
1953 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1956 entered_marker = marker;
1957 marker->set_color_rgba (ARDOUR_UI::config()->canvasvar_EnteredMarker.get());
1959 case MeterMarkerItem:
1960 case TempoMarkerItem:
1961 if (is_drawable()) {
1962 set_canvas_cursor (_cursors->timebar);
1966 case FadeInHandleItem:
1967 if (mouse_mode == MouseObject && !internal_editing()) {
1968 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1970 rect->property_fill_color_rgba() = 0xBBBBBBAA;
1972 set_canvas_cursor (_cursors->fade_in);
1976 case FadeOutHandleItem:
1977 if (mouse_mode == MouseObject && !internal_editing()) {
1978 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1980 rect->property_fill_color_rgba() = 0xBBBBBBAA;
1982 set_canvas_cursor (_cursors->fade_out);
1985 case FeatureLineItem:
1987 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1988 line->property_fill_color_rgba() = 0xFF0000FF;
1992 if ( get_smart_mode() ) {
1993 set_canvas_cursor ();
2001 /* second pass to handle entered track status in a comprehensible way.
2004 switch (item_type) {
2006 case AutomationLineItem:
2007 case ControlPointItem:
2008 /* these do not affect the current entered track state */
2009 clear_entered_track = false;
2012 case AutomationTrackItem:
2013 /* handled above already */
2017 set_entered_track (0);
2025 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent*, ItemType item_type)
2035 switch (item_type) {
2036 case ControlPointItem:
2037 cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
2038 if (cp->line().the_list()->interpolation() != AutomationList::Discrete) {
2039 if (cp->line().npoints() > 1 && !cp->get_selected()) {
2040 cp->set_visible (false);
2044 if (is_drawable()) {
2045 set_canvas_cursor (current_canvas_cursor);
2048 _verbose_cursor->hide ();
2051 case RegionViewNameHighlight:
2052 case LeftFrameHandle:
2053 case RightFrameHandle:
2054 case StartSelectionTrimItem:
2055 case EndSelectionTrimItem:
2056 case PlayheadCursorItem:
2059 case ImageFrameHandleStartItem:
2060 case ImageFrameHandleEndItem:
2061 case MarkerViewHandleStartItem:
2062 case MarkerViewHandleEndItem:
2065 _over_region_trim_target = false;
2067 if (is_drawable()) {
2068 set_canvas_cursor (current_canvas_cursor);
2073 case AutomationLineItem:
2074 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
2076 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
2078 line->property_fill_color_rgba() = al->get_line_color();
2080 if (is_drawable()) {
2081 set_canvas_cursor (current_canvas_cursor);
2085 case RegionViewName:
2086 /* see enter_handler() for notes */
2087 _over_region_trim_target = false;
2089 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
2090 if (is_drawable() && mouse_mode == MouseObject) {
2091 set_canvas_cursor (current_canvas_cursor);
2096 case RangeMarkerBarItem:
2097 case TransportMarkerBarItem:
2098 case CdMarkerBarItem:
2102 if (is_drawable()) {
2103 set_canvas_cursor (current_canvas_cursor);
2108 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2112 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
2113 location_flags_changed (loc, this);
2116 case MeterMarkerItem:
2117 case TempoMarkerItem:
2119 if (is_drawable()) {
2120 set_canvas_cursor (current_canvas_cursor);
2125 case FadeInHandleItem:
2126 case FadeOutHandleItem:
2127 rv = static_cast<RegionView*>(item->get_data ("regionview"));
2129 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
2131 rect->property_fill_color_rgba() = rv->get_fill_color();
2134 set_canvas_cursor (current_canvas_cursor);
2137 case AutomationTrackItem:
2138 if (is_drawable()) {
2139 set_canvas_cursor (current_canvas_cursor);
2140 clear_entered_track = true;
2141 Glib::signal_idle().connect (sigc::mem_fun(*this, &Editor::left_automation_track));
2144 case FeatureLineItem:
2146 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
2147 line->property_fill_color_rgba() = (guint) ARDOUR_UI::config()->canvasvar_ZeroLine.get();;
2159 Editor::left_automation_track ()
2161 if (clear_entered_track) {
2162 set_entered_track (0);
2163 clear_entered_track = false;
2169 Editor::scrub (framepos_t frame, double current_x)
2173 if (scrubbing_direction == 0) {
2175 _session->request_locate (frame, false);
2176 _session->request_transport_speed (0.1);
2177 scrubbing_direction = 1;
2181 if (last_scrub_x > current_x) {
2183 /* pointer moved to the left */
2185 if (scrubbing_direction > 0) {
2187 /* we reversed direction to go backwards */
2190 scrub_reverse_distance += (int) (last_scrub_x - current_x);
2194 /* still moving to the left (backwards) */
2196 scrub_reversals = 0;
2197 scrub_reverse_distance = 0;
2199 delta = 0.01 * (last_scrub_x - current_x);
2200 _session->request_transport_speed_nonzero (_session->transport_speed() - delta);
2204 /* pointer moved to the right */
2206 if (scrubbing_direction < 0) {
2207 /* we reversed direction to go forward */
2210 scrub_reverse_distance += (int) (current_x - last_scrub_x);
2213 /* still moving to the right */
2215 scrub_reversals = 0;
2216 scrub_reverse_distance = 0;
2218 delta = 0.01 * (current_x - last_scrub_x);
2219 _session->request_transport_speed_nonzero (_session->transport_speed() + delta);
2223 /* if there have been more than 2 opposite motion moves detected, or one that moves
2224 back more than 10 pixels, reverse direction
2227 if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
2229 if (scrubbing_direction > 0) {
2230 /* was forwards, go backwards */
2231 _session->request_transport_speed (-0.1);
2232 scrubbing_direction = -1;
2234 /* was backwards, go forwards */
2235 _session->request_transport_speed (0.1);
2236 scrubbing_direction = 1;
2239 scrub_reverse_distance = 0;
2240 scrub_reversals = 0;
2244 last_scrub_x = current_x;
2248 Editor::motion_handler (ArdourCanvas::Item* /*item*/, GdkEvent* event, bool from_autoscroll)
2250 _last_motion_y = event->motion.y;
2252 if (event->motion.is_hint) {
2255 /* We call this so that MOTION_NOTIFY events continue to be
2256 delivered to the canvas. We need to do this because we set
2257 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
2258 the density of the events, at the expense of a round-trip
2259 to the server. Given that this will mostly occur on cases
2260 where DISPLAY = :0.0, and given the cost of what the motion
2261 event might do, its a good tradeoff.
2264 track_canvas->get_pointer (x, y);
2267 if (current_stepping_trackview) {
2268 /* don't keep the persistent stepped trackview if the mouse moves */
2269 current_stepping_trackview = 0;
2270 step_timeout.disconnect ();
2273 if (_session && _session->actively_recording()) {
2274 /* Sorry. no dragging stuff around while we record */
2278 JoinObjectRangeState const old = _join_object_range_state;
2279 update_join_object_range_location (event->motion.x, event->motion.y);
2281 if (!_internal_editing && _join_object_range_state != old) {
2282 set_canvas_cursor ();
2285 if (!_internal_editing && _over_region_trim_target) {
2286 set_canvas_cursor_for_region_view (event->motion.x, entered_regionview);
2289 bool handled = false;
2290 if (_drags->active ()) {
2291 handled = _drags->motion_handler (event, from_autoscroll);
2298 track_canvas_motion (event);
2303 Editor::can_remove_control_point (ArdourCanvas::Item* item)
2305 ControlPoint* control_point;
2307 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2308 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2312 AutomationLine& line = control_point->line ();
2313 if (dynamic_cast<AudioRegionGainLine*> (&line)) {
2314 /* we shouldn't remove the first or last gain point in region gain lines */
2315 if (line.is_last_point(*control_point) || line.is_first_point(*control_point)) {
2324 Editor::remove_control_point (ArdourCanvas::Item* item)
2326 if (!can_remove_control_point (item)) {
2330 ControlPoint* control_point;
2332 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2333 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2337 control_point->line().remove_point (*control_point);
2341 Editor::edit_control_point (ArdourCanvas::Item* item)
2343 ControlPoint* p = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"));
2346 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2350 ControlPointDialog d (p);
2351 d.set_position (Gtk::WIN_POS_MOUSE);
2354 if (d.run () != RESPONSE_ACCEPT) {
2358 p->line().modify_point_y (*p, d.get_y_fraction ());
2362 Editor::edit_notes (MidiRegionView::Selection const & s)
2368 EditNoteDialog d (&(*s.begin())->region_view(), s);
2369 d.set_position (Gtk::WIN_POS_MOUSE);
2377 Editor::visible_order_range (int* low, int* high) const
2379 *low = TimeAxisView::max_order ();
2382 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
2384 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
2386 if (!rtv->hidden()) {
2388 if (*high < rtv->order()) {
2389 *high = rtv->order ();
2392 if (*low > rtv->order()) {
2393 *low = rtv->order ();
2400 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
2402 /* Either add to or set the set the region selection, unless
2403 this is an alignment click (control used)
2406 if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
2407 TimeAxisView* tv = &rv.get_time_axis_view();
2408 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
2410 if (rtv && rtv->is_track()) {
2411 speed = rtv->track()->speed();
2414 framepos_t where = get_preferred_edit_position();
2418 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
2420 align_region (rv.region(), SyncPoint, (framepos_t) (where * speed));
2422 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
2424 align_region (rv.region(), End, (framepos_t) (where * speed));
2428 align_region (rv.region(), Start, (framepos_t) (where * speed));
2435 Editor::collect_new_region_view (RegionView* rv)
2437 latest_regionviews.push_back (rv);
2441 Editor::collect_and_select_new_region_view (RegionView* rv)
2444 latest_regionviews.push_back (rv);
2448 Editor::cancel_selection ()
2450 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2451 (*i)->hide_selection ();
2454 selection->clear ();
2455 clicked_selection = 0;
2459 Editor::cancel_time_selection ()
2461 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2462 (*i)->hide_selection ();
2464 selection->time.clear ();
2465 clicked_selection = 0;
2469 Editor::point_trim (GdkEvent* event, framepos_t new_bound)
2471 RegionView* rv = clicked_regionview;
2473 /* Choose action dependant on which button was pressed */
2474 switch (event->button.button) {
2476 begin_reversible_command (_("start point trim"));
2478 if (selection->selected (rv)) {
2479 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
2480 i != selection->regions.by_layer().end(); ++i)
2482 if (!(*i)->region()->locked()) {
2483 (*i)->region()->clear_changes ();
2484 (*i)->region()->trim_front (new_bound);
2485 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2490 if (!rv->region()->locked()) {
2491 rv->region()->clear_changes ();
2492 rv->region()->trim_front (new_bound);
2493 _session->add_command(new StatefulDiffCommand (rv->region()));
2497 commit_reversible_command();
2501 begin_reversible_command (_("End point trim"));
2503 if (selection->selected (rv)) {
2505 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
2507 if (!(*i)->region()->locked()) {
2508 (*i)->region()->clear_changes();
2509 (*i)->region()->trim_end (new_bound);
2510 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2516 if (!rv->region()->locked()) {
2517 rv->region()->clear_changes ();
2518 rv->region()->trim_end (new_bound);
2519 _session->add_command (new StatefulDiffCommand (rv->region()));
2523 commit_reversible_command();
2532 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* /*event*/)
2537 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2538 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2542 Location* location = find_location_from_marker (marker, is_start);
2543 location->set_hidden (true, this);
2548 Editor::reposition_zoom_rect (framepos_t start, framepos_t end)
2550 double x1 = frame_to_pixel (start);
2551 double x2 = frame_to_pixel (end);
2552 double y2 = full_canvas_height - 1.0;
2554 zoom_rect->property_x1() = x1;
2555 zoom_rect->property_y1() = 1.0;
2556 zoom_rect->property_x2() = x2;
2557 zoom_rect->property_y2() = y2;
2562 Editor::mouse_rename_region (ArdourCanvas::Item* /*item*/, GdkEvent* /*event*/)
2564 using namespace Gtkmm2ext;
2566 ArdourPrompter prompter (false);
2568 prompter.set_prompt (_("Name for region:"));
2569 prompter.set_initial_text (clicked_regionview->region()->name());
2570 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
2571 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
2572 prompter.show_all ();
2573 switch (prompter.run ()) {
2574 case Gtk::RESPONSE_ACCEPT:
2576 prompter.get_result(str);
2578 clicked_regionview->region()->set_name (str);
2587 Editor::mouse_brush_insert_region (RegionView* rv, framepos_t pos)
2589 /* no brushing without a useful snap setting */
2591 switch (_snap_mode) {
2593 return; /* can't work because it allows region to be placed anywhere */
2598 switch (_snap_type) {
2606 /* don't brush a copy over the original */
2608 if (pos == rv->region()->position()) {
2612 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
2614 if (rtv == 0 || !rtv->is_track()) {
2618 boost::shared_ptr<Playlist> playlist = rtv->playlist();
2619 double speed = rtv->track()->speed();
2621 playlist->clear_changes ();
2622 boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region(), true));
2623 playlist->add_region (new_region, (framepos_t) (pos * speed));
2624 _session->add_command (new StatefulDiffCommand (playlist));
2626 // playlist is frozen, so we have to update manually XXX this is disgusting
2628 playlist->RegionAdded (new_region); /* EMIT SIGNAL */
2632 Editor::track_height_step_timeout ()
2634 if (get_microseconds() - last_track_height_step_timestamp < 250000) {
2635 current_stepping_trackview = 0;
2642 Editor::add_region_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2644 assert (region_view);
2646 if (!region_view->region()->playlist()) {
2650 _region_motion_group->raise_to_top ();
2652 if (Config->get_edit_mode() == Splice) {
2653 _drags->add (new RegionSpliceDrag (this, item, region_view, selection->regions.by_layer()));
2655 RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::select.property_id);
2656 _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), false, false));
2659 /* sync the canvas to what we think is its current state */
2660 update_canvas_now();
2664 Editor::add_region_copy_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2666 assert (region_view);
2668 if (!region_view->region()->playlist()) {
2672 _region_motion_group->raise_to_top ();
2674 RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::select.property_id);
2675 _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), false, true));
2679 Editor::add_region_brush_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2681 assert (region_view);
2683 if (!region_view->region()->playlist()) {
2687 if (Config->get_edit_mode() == Splice) {
2691 RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::select.property_id);
2692 _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), true, false));
2694 begin_reversible_command (Operations::drag_region_brush);
2697 /** Start a grab where a time range is selected, track(s) are selected, and the
2698 * user clicks and drags a region with a modifier in order to create a new region containing
2699 * the section of the clicked region that lies within the time range.
2702 Editor::start_selection_grab (ArdourCanvas::Item* /*item*/, GdkEvent* event)
2704 if (clicked_regionview == 0) {
2708 /* lets try to create new Region for the selection */
2710 vector<boost::shared_ptr<Region> > new_regions;
2711 create_region_from_selection (new_regions);
2713 if (new_regions.empty()) {
2717 /* XXX fix me one day to use all new regions */
2719 boost::shared_ptr<Region> region (new_regions.front());
2721 /* add it to the current stream/playlist.
2723 tricky: the streamview for the track will add a new regionview. we will
2724 catch the signal it sends when it creates the regionview to
2725 set the regionview we want to then drag.
2728 latest_regionviews.clear();
2729 sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
2731 /* A selection grab currently creates two undo/redo operations, one for
2732 creating the new region and another for moving it.
2735 begin_reversible_command (Operations::selection_grab);
2737 boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
2739 playlist->clear_changes ();
2740 clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
2741 _session->add_command(new StatefulDiffCommand (playlist));
2743 commit_reversible_command ();
2747 if (latest_regionviews.empty()) {
2748 /* something went wrong */
2752 /* we need to deselect all other regionviews, and select this one
2753 i'm ignoring undo stuff, because the region creation will take care of it
2755 selection->set (latest_regionviews);
2757 _drags->set (new RegionMoveDrag (this, latest_regionviews.front()->get_canvas_group(), latest_regionviews.front(), latest_regionviews, false, false), event);
2763 if (_drags->active ()) {
2766 selection->clear ();
2771 Editor::set_internal_edit (bool yn)
2773 if (_internal_editing == yn) {
2777 _internal_editing = yn;
2780 pre_internal_mouse_mode = mouse_mode;
2781 pre_internal_snap_type = _snap_type;
2782 pre_internal_snap_mode = _snap_mode;
2784 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2785 (*i)->enter_internal_edit_mode ();
2788 set_snap_to (internal_snap_type);
2789 set_snap_mode (internal_snap_mode);
2793 internal_snap_mode = _snap_mode;
2794 internal_snap_type = _snap_type;
2796 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2797 (*i)->leave_internal_edit_mode ();
2800 if (mouse_mode == MouseDraw && pre_internal_mouse_mode != MouseDraw) {
2801 /* we were drawing .. flip back to something sensible */
2802 set_mouse_mode (pre_internal_mouse_mode);
2805 set_snap_to (pre_internal_snap_type);
2806 set_snap_mode (pre_internal_snap_mode);
2809 set_canvas_cursor ();
2812 /** Update _join_object_range_state which indicate whether we are over the top or bottom half of a region view,
2813 * used by the `join object/range' tool mode.
2816 Editor::update_join_object_range_location (double /*x*/, double y)
2818 /* XXX: actually, this decides based on whether the mouse is in the top
2819 or bottom half of a the waveform part RouteTimeAxisView;
2821 Note that entered_{track,regionview} is not always setup (e.g. if
2822 the mouse is over a TimeSelection), and to get a Region
2823 that we're over requires searching the playlist.
2826 if ( !get_smart_mode() ) {
2827 _join_object_range_state = JOIN_OBJECT_RANGE_NONE;
2831 if (mouse_mode == MouseObject) {
2832 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2833 } else if (mouse_mode == MouseRange) {
2834 _join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
2837 /* XXX: maybe we should make entered_track work in all cases, rather than resorting to this */
2838 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y + vertical_adjustment.get_value() - canvas_timebars_vsize);
2842 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
2847 rtv->canvas_display()->w2i (cx, cy);
2849 double const c = cy / (rtv->view()->child_height() - TimeAxisViewItem::NAME_HIGHLIGHT_SIZE);
2851 _join_object_range_state = c <= 0.5 ? JOIN_OBJECT_RANGE_RANGE : JOIN_OBJECT_RANGE_OBJECT;
2857 Editor::effective_mouse_mode () const
2859 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
2861 } else if (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE) {
2869 Editor::remove_midi_note (ArdourCanvas::Item* item, GdkEvent *)
2871 ArdourCanvas::CanvasNoteEvent* e = dynamic_cast<ArdourCanvas::CanvasNoteEvent*> (item);
2874 e->region_view().delete_note (e->note ());
2878 Editor::set_canvas_cursor_for_region_view (double x, RegionView* rv)
2882 ArdourCanvas::Group* g = rv->get_canvas_group ();
2883 ArdourCanvas::Group* p = g->get_parent_group ();
2885 /* Compute x in region view parent coordinates */
2889 double x1, x2, y1, y2;
2890 g->get_bounds (x1, y1, x2, y2);
2892 /* Halfway across the region */
2893 double const h = (x1 + x2) / 2;
2895 Trimmable::CanTrim ct = rv->region()->can_trim ();
2897 if (ct & Trimmable::FrontTrimEarlier) {
2898 set_canvas_cursor (_cursors->left_side_trim);
2900 set_canvas_cursor (_cursors->left_side_trim_right_only);
2903 if (ct & Trimmable::EndTrimLater) {
2904 set_canvas_cursor (_cursors->right_side_trim);
2906 set_canvas_cursor (_cursors->right_side_trim_left_only);
2911 /** Obtain the pointer position in world coordinates */
2913 Editor::get_pointer_position (double& x, double& y) const
2916 track_canvas->get_pointer (px, py);
2917 track_canvas->window_to_world (px, py, x, y);