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)
740 #ifdef WITH_VIDEOTIMELINE
742 _drags->set (new VideoTimeLineDrag (this, item), event);
750 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
751 _drags->set (new CursorDrag (this, &playhead_cursor->canvas_item, false), event);
757 case RangeMarkerBarItem:
758 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
759 _drags->set (new CursorDrag (this, &playhead_cursor->canvas_item, false), event);
761 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateRangeMarker), event);
766 case CdMarkerBarItem:
767 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
768 _drags->set (new CursorDrag (this, &playhead_cursor->canvas_item, false), event);
770 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateCDMarker), event);
775 case TransportMarkerBarItem:
776 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
777 _drags->set (new CursorDrag (this, &playhead_cursor->canvas_item, false), event);
779 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateTransportMarker), event);
788 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
789 /* special case: allow trim of range selections in joined object mode;
790 in theory eff should equal MouseRange in this case, but it doesn't
791 because entering the range selection canvas item results in entered_regionview
792 being set to 0, so update_join_object_range_location acts as if we aren't
795 if (item_type == StartSelectionTrimItem) {
796 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
797 } else if (item_type == EndSelectionTrimItem) {
798 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
802 Editing::MouseMode eff = effective_mouse_mode ();
804 /* special case: allow drag of region fade in/out in object mode with join object/range enabled */
805 if (item_type == FadeInHandleItem || item_type == FadeOutHandleItem) {
809 /* there is no Range mode when in internal edit mode */
810 if (eff == MouseRange && internal_editing()) {
817 case StartSelectionTrimItem:
818 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
821 case EndSelectionTrimItem:
822 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
826 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
827 start_selection_grab (item, event);
828 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
829 /* grab selection for moving */
830 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionMove), event);
832 double const y = event->button.y + vertical_adjustment.get_value() - canvas_timebars_vsize;
833 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
835 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
836 if ( get_smart_mode() && atv) {
837 /* smart "join" mode: drag automation */
838 _drags->set (new AutomationRangeDrag (this, atv, selection->time), event, _cursors->up_down);
840 /* this was debated, but decided the more common action was to
841 make a new selection */
842 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
849 if (internal_editing()) {
850 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
851 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
855 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::RangeSelectModifier)) {
856 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionExtend), event);
858 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
864 case RegionViewNameHighlight:
865 if (!clicked_regionview->region()->locked()) {
866 RegionSelection s = get_equivalent_regions (selection->regions, Properties::select.property_id);
867 _drags->set (new TrimDrag (this, item, clicked_regionview, s.by_layer()), event);
873 if (!internal_editing()) {
874 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::RangeSelectModifier)) {
875 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionExtend), event);
877 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
887 if (internal_editing()) {
888 /* trim notes if we're in internal edit mode and near the ends of the note */
889 ArdourCanvas::CanvasNote* cn = dynamic_cast<ArdourCanvas::CanvasNote*> (item);
890 if (cn && cn->big_enough_to_trim() && cn->mouse_near_ends()) {
891 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
893 _drags->set (new NoteDrag (this, item), event);
899 if (internal_editing()) {
900 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
901 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
915 if (internal_editing()) {
916 ArdourCanvas::CanvasNoteEvent* cn = dynamic_cast<ArdourCanvas::CanvasNoteEvent*> (item);
917 if (cn->mouse_near_ends()) {
918 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
920 _drags->set (new NoteDrag (this, item), event);
930 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)) &&
931 event->type == GDK_BUTTON_PRESS) {
933 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
935 } else if (event->type == GDK_BUTTON_PRESS) {
938 case FadeInHandleItem:
940 RegionSelection s = get_equivalent_regions (selection->regions, Properties::select.property_id);
941 _drags->set (new FadeInDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), s), event, _cursors->fade_in);
945 case FadeOutHandleItem:
947 RegionSelection s = get_equivalent_regions (selection->regions, Properties::select.property_id);
948 _drags->set (new FadeOutDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), s), event, _cursors->fade_out);
952 case StartCrossFadeItem:
953 case EndCrossFadeItem:
954 /* we might allow user to grab inside the fade to trim a region with preserve_fade_anchor. for not this is not fully implemented */
955 // if (!clicked_regionview->region()->locked()) {
956 // RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
957 // _drags->set (new TrimDrag (this, item, clicked_regionview, s.by_layer(), true), event);
962 case FeatureLineItem:
964 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier)) {
965 remove_transient(item);
969 _drags->set (new FeatureLineDrag (this, item), event);
975 if (dynamic_cast<AutomationRegionView*> (clicked_regionview)) {
976 /* click on an automation region view; do nothing here and let the ARV's signal handler
982 if (internal_editing ()) {
983 if (event->type == GDK_2BUTTON_PRESS && event->button.button == 1) {
984 Glib::RefPtr<Action> act = ActionManager::get_action (X_("MouseMode"), X_("toggle-internal-edit"));
990 /* click on a normal region view */
991 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
992 add_region_copy_drag (item, event, clicked_regionview);
993 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
994 add_region_brush_drag (item, event, clicked_regionview);
996 add_region_drag (item, event, clicked_regionview);
1000 // if (!internal_editing() && (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE && !selection->regions.empty())) {
1001 // _drags->add (new SelectionDrag (this, clicked_axisview->get_selection_rect (clicked_selection)->rect, SelectionDrag::SelectionMove));
1004 _drags->start_grab (event);
1007 case RegionViewNameHighlight:
1008 case LeftFrameHandle:
1009 case RightFrameHandle:
1010 if (!clicked_regionview->region()->locked()) {
1011 RegionSelection s = get_equivalent_regions (selection->regions, Properties::select.property_id);
1012 _drags->set (new TrimDrag (this, item, clicked_regionview, s.by_layer()), event);
1017 case RegionViewName:
1019 /* rename happens on edit clicks */
1020 RegionSelection s = get_equivalent_regions (selection->regions, Properties::select.property_id);
1021 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, s.by_layer()), event);
1026 case ControlPointItem:
1027 _drags->set (new ControlPointDrag (this, item), event);
1031 case AutomationLineItem:
1032 _drags->set (new LineDrag (this, item), event);
1037 if (internal_editing()) {
1038 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
1039 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
1043 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
1047 case AutomationTrackItem:
1049 TimeAxisView* parent = clicked_axisview->get_parent ();
1050 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (clicked_axisview);
1052 if (parent && dynamic_cast<MidiTimeAxisView*> (parent) && atv->show_regions ()) {
1054 RouteTimeAxisView* p = dynamic_cast<RouteTimeAxisView*> (parent);
1056 boost::shared_ptr<Playlist> pl = p->track()->playlist ();
1057 if (pl->n_regions() == 0) {
1058 /* Parent has no regions; create one so that we have somewhere to put automation */
1059 _drags->set (new RegionCreateDrag (this, item, parent), event);
1061 /* See if there's a region before the click that we can extend, and extend it if so */
1062 framepos_t const t = event_frame (event);
1063 boost::shared_ptr<Region> prev = pl->find_next_region (t, End, -1);
1065 _drags->set (new RegionCreateDrag (this, item, parent), event);
1067 prev->set_length (t - prev->position ());
1071 /* rubberband drag to select automation points */
1072 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
1079 if ( get_smart_mode() ) {
1080 /* we're in "smart" joined mode, and we've clicked on a Selection */
1081 double const y = event->button.y + vertical_adjustment.get_value() - canvas_timebars_vsize;
1082 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
1084 /* if we're over an automation track, start a drag of its data */
1085 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
1087 _drags->set (new AutomationRangeDrag (this, atv, selection->time), event, _cursors->up_down);
1090 /* if we're over a track and a region, and in the `object' part of a region,
1091 put a selection around the region and drag both
1093 /* RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1094 if (rtv && _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
1095 boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track> (rtv->route ());
1097 boost::shared_ptr<Playlist> pl = t->playlist ();
1100 boost::shared_ptr<Region> r = pl->top_region_at (event_frame (event));
1102 RegionView* rv = rtv->view()->find_view (r);
1103 clicked_selection = select_range (rv->region()->position(),
1104 rv->region()->last_frame()+1);
1105 _drags->add (new SelectionDrag (this, item, SelectionDrag::SelectionMove));
1106 list<RegionView*> rvs;
1108 _drags->add (new RegionMoveDrag (this, item, rv, rvs, false, false));
1109 _drags->start_grab (event);
1121 case ImageFrameHandleStartItem:
1122 imageframe_start_handle_op(item, event) ;
1125 case ImageFrameHandleEndItem:
1126 imageframe_end_handle_op(item, event) ;
1129 case MarkerViewHandleStartItem:
1130 markerview_item_start_handle_op(item, event) ;
1133 case MarkerViewHandleEndItem:
1134 markerview_item_end_handle_op(item, event) ;
1137 case MarkerViewItem:
1138 start_markerview_grab(item, event) ;
1140 case ImageFrameItem:
1141 start_imageframe_grab(item, event) ;
1157 switch (item_type) {
1159 _drags->set (new LineDrag (this, item), event);
1162 case ControlPointItem:
1163 _drags->set (new ControlPointDrag (this, item), event);
1169 AudioRegionView* arv = dynamic_cast<AudioRegionView *> (clicked_regionview);
1171 _drags->set (new AutomationRangeDrag (this, arv, selection->time), event, _cursors->up_down);
1172 _drags->start_grab (event);
1178 case AutomationLineItem:
1179 _drags->set (new LineDrag (this, item), event);
1189 if (event->type == GDK_BUTTON_PRESS) {
1190 _drags->set (new MouseZoomDrag (this, item), event);
1197 if (internal_editing() && item_type == NoteItem) {
1198 /* drag notes if we're in internal edit mode */
1199 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
1201 } else if (clicked_regionview) {
1203 _drags->set (new TimeFXDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1209 _drags->set (new ScrubDrag (this, item), event);
1210 scrub_reversals = 0;
1211 scrub_reverse_distance = 0;
1212 last_scrub_x = event->button.x;
1213 scrubbing_direction = 0;
1214 set_canvas_cursor (_cursors->transparent);
1226 Editor::button_press_handler_2 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1228 Editing::MouseMode const eff = effective_mouse_mode ();
1231 switch (item_type) {
1233 if (internal_editing ()) {
1234 /* no region drags in internal edit mode */
1238 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
1239 add_region_copy_drag (item, event, clicked_regionview);
1241 add_region_drag (item, event, clicked_regionview);
1243 _drags->start_grab (event);
1246 case ControlPointItem:
1247 _drags->set (new ControlPointDrag (this, item), event);
1255 switch (item_type) {
1256 case RegionViewNameHighlight:
1257 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1261 case LeftFrameHandle:
1262 case RightFrameHandle:
1263 if (!internal_editing ()) {
1264 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1269 case RegionViewName:
1270 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer()), event);
1284 /* relax till release */
1290 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1291 temporal_zoom_to_frame (false, event_frame (event));
1293 temporal_zoom_to_frame (true, event_frame(event));
1306 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1308 if (event->type != GDK_BUTTON_PRESS) {
1312 Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->track_canvas->get_window();
1314 if (canvas_window) {
1315 Glib::RefPtr<const Gdk::Window> pointer_window;
1318 Gdk::ModifierType mask;
1320 pointer_window = canvas_window->get_pointer (x, y, mask);
1322 if (pointer_window == track_canvas->get_bin_window()) {
1323 track_canvas->window_to_world (x, y, wx, wy);
1327 pre_press_cursor = current_canvas_cursor;
1329 track_canvas->grab_focus();
1331 if (_session && _session->actively_recording()) {
1335 if (internal_editing()) {
1336 bool leave_internal_edit_mode = false;
1338 switch (item_type) {
1343 if (!dynamic_cast<MidiRegionView*> (clicked_regionview) && !dynamic_cast<AutomationRegionView*> (clicked_regionview)) {
1344 leave_internal_edit_mode = true;
1348 case PlayheadCursorItem:
1350 case TempoMarkerItem:
1351 case MeterMarkerItem:
1355 case RangeMarkerBarItem:
1356 case CdMarkerBarItem:
1357 case TransportMarkerBarItem:
1359 /* button press on these events never does anything to
1360 change the editing mode.
1368 if (leave_internal_edit_mode) {
1369 ActionManager::do_action ("MouseMode", "toggle-internal-edit");
1373 button_selection (item, event, item_type);
1375 if (!_drags->active () &&
1376 (Keyboard::is_delete_event (&event->button) ||
1377 Keyboard::is_context_menu_event (&event->button) ||
1378 Keyboard::is_edit_event (&event->button))) {
1380 /* handled by button release */
1384 //not rolling, range mode click + join_play_range : locate the PH here
1385 if ( !_drags->active () && !_session->transport_rolling() && ( effective_mouse_mode() == MouseRange ) && Config->get_always_play_range() ) {
1386 framepos_t where = event_frame (event, 0, 0);
1388 _session->request_locate (where, false);
1391 switch (event->button.button) {
1393 return button_press_handler_1 (item, event, item_type);
1397 return button_press_handler_2 (item, event, item_type);
1404 return button_press_dispatch (&event->button);
1413 Editor::button_press_dispatch (GdkEventButton* ev)
1415 /* this function is intended only for buttons 4 and above.
1418 Gtkmm2ext::MouseButton b (ev->state, ev->button);
1419 return button_bindings->activate (b, Gtkmm2ext::Bindings::Press);
1423 Editor::button_release_dispatch (GdkEventButton* ev)
1425 /* this function is intended only for buttons 4 and above.
1428 Gtkmm2ext::MouseButton b (ev->state, ev->button);
1429 return button_bindings->activate (b, Gtkmm2ext::Bindings::Release);
1433 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1435 framepos_t where = event_frame (event, 0, 0);
1436 AutomationTimeAxisView* atv = 0;
1438 if (pre_press_cursor) {
1439 set_canvas_cursor (pre_press_cursor);
1440 pre_press_cursor = 0;
1443 /* no action if we're recording */
1445 if (_session && _session->actively_recording()) {
1449 /* see if we're finishing a drag */
1451 bool were_dragging = false;
1452 if (_drags->active ()) {
1453 bool const r = _drags->end_grab (event);
1455 /* grab dragged, so do nothing else */
1459 were_dragging = true;
1462 update_region_layering_order_editor ();
1464 /* edit events get handled here */
1466 if (!_drags->active () && Keyboard::is_edit_event (&event->button)) {
1467 switch (item_type) {
1469 show_region_properties ();
1472 case TempoMarkerItem:
1473 edit_tempo_marker (item);
1476 case MeterMarkerItem:
1477 edit_meter_marker (item);
1480 case RegionViewName:
1481 if (clicked_regionview->name_active()) {
1482 return mouse_rename_region (item, event);
1486 case ControlPointItem:
1487 edit_control_point (item);
1492 ArdourCanvas::CanvasNoteEvent* e = dynamic_cast<ArdourCanvas::CanvasNoteEvent*> (item);
1494 edit_notes (e->region_view().selection ());
1504 /* context menu events get handled here */
1505 if (Keyboard::is_context_menu_event (&event->button)) {
1507 context_click_event = *event;
1509 if (!_drags->active ()) {
1511 /* no matter which button pops up the context menu, tell the menu
1512 widget to use button 1 to drive menu selection.
1515 switch (item_type) {
1517 case FadeInHandleItem:
1519 case FadeOutHandleItem:
1520 popup_fade_context_menu (1, event->button.time, item, item_type);
1523 case StartCrossFadeItem:
1524 popup_xfade_in_context_menu (1, event->button.time, item, item_type);
1527 case EndCrossFadeItem:
1528 popup_xfade_out_context_menu (1, event->button.time, item, item_type);
1532 popup_track_context_menu (1, event->button.time, item_type, false);
1536 case RegionViewNameHighlight:
1537 case LeftFrameHandle:
1538 case RightFrameHandle:
1539 case RegionViewName:
1540 popup_track_context_menu (1, event->button.time, item_type, false);
1544 popup_track_context_menu (1, event->button.time, item_type, true);
1547 case AutomationTrackItem:
1548 popup_track_context_menu (1, event->button.time, item_type, false);
1552 case RangeMarkerBarItem:
1553 case TransportMarkerBarItem:
1554 case CdMarkerBarItem:
1557 #ifdef WITH_VIDEOTIMELINE
1560 popup_ruler_menu (where, item_type);
1564 marker_context_menu (&event->button, item);
1567 case TempoMarkerItem:
1568 tempo_or_meter_marker_context_menu (&event->button, item);
1571 case MeterMarkerItem:
1572 tempo_or_meter_marker_context_menu (&event->button, item);
1575 case CrossfadeViewItem:
1576 popup_track_context_menu (1, event->button.time, item_type, false);
1579 case ControlPointItem:
1580 popup_control_point_context_menu (item, event);
1584 case ImageFrameItem:
1585 popup_imageframe_edit_menu(1, event->button.time, item, true) ;
1587 case ImageFrameTimeAxisItem:
1588 popup_imageframe_edit_menu(1, event->button.time, item, false) ;
1590 case MarkerViewItem:
1591 popup_marker_time_axis_edit_menu(1, event->button.time, item, true) ;
1593 case MarkerTimeAxisItem:
1594 popup_marker_time_axis_edit_menu(1, event->button.time, item, false) ;
1606 /* delete events get handled here */
1608 Editing::MouseMode const eff = effective_mouse_mode ();
1610 if (!_drags->active () && Keyboard::is_delete_event (&event->button)) {
1612 switch (item_type) {
1613 case TempoMarkerItem:
1614 remove_tempo_marker (item);
1617 case MeterMarkerItem:
1618 remove_meter_marker (item);
1622 remove_marker (*item, event);
1626 if (eff == MouseObject) {
1627 remove_clicked_region ();
1631 case ControlPointItem:
1632 remove_control_point (item);
1636 remove_midi_note (item, event);
1645 switch (event->button.button) {
1648 switch (item_type) {
1649 /* see comments in button_press_handler */
1650 case PlayheadCursorItem:
1653 case AutomationLineItem:
1654 case StartSelectionTrimItem:
1655 case EndSelectionTrimItem:
1659 if (!_dragging_playhead) {
1660 snap_to_with_modifier (where, event, 0, true);
1661 mouse_add_new_marker (where);
1665 case CdMarkerBarItem:
1666 if (!_dragging_playhead) {
1667 // if we get here then a dragged range wasn't done
1668 snap_to_with_modifier (where, event, 0, true);
1669 mouse_add_new_marker (where, true);
1674 if (!_dragging_playhead) {
1675 snap_to_with_modifier (where, event);
1676 mouse_add_new_tempo_event (where);
1681 if (!_dragging_playhead) {
1682 mouse_add_new_meter_event (pixel_to_frame (event->button.x));
1693 switch (item_type) {
1694 case AutomationTrackItem:
1695 atv = dynamic_cast<AutomationTimeAxisView*>(clicked_axisview);
1697 atv->add_automation_event (event, where, event->button.y);
1707 switch (item_type) {
1710 /* check that we didn't drag before releasing, since
1711 its really annoying to create new control
1712 points when doing this.
1714 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (clicked_regionview);
1715 if (!were_dragging && arv) {
1716 arv->add_gain_point_event (item, event);
1722 case AutomationTrackItem:
1723 dynamic_cast<AutomationTimeAxisView*>(clicked_axisview)->
1724 add_automation_event (event, where, event->button.y);
1733 set_canvas_cursor (current_canvas_cursor);
1734 if (scrubbing_direction == 0) {
1735 /* no drag, just a click */
1736 switch (item_type) {
1738 play_selected_region ();
1744 /* make sure we stop */
1745 _session->request_transport_speed (0.0);
1754 /* do any (de)selection operations that should occur on button release */
1755 button_selection (item, event, item_type);
1764 switch (item_type) {
1766 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1768 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier|Keyboard::SecondaryModifier))) {
1771 // Button2 click is unused
1786 // x_style_paste (where, 1.0);
1807 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1814 switch (item_type) {
1815 case ControlPointItem:
1816 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1817 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1818 cp->set_visible (true);
1822 at_y = cp->get_y ();
1823 cp->i2w (at_x, at_y);
1827 fraction = 1.0 - (cp->get_y() / cp->line().height());
1829 if (is_drawable() && !_drags->active ()) {
1830 set_canvas_cursor (_cursors->fader);
1833 _verbose_cursor->set (cp->line().get_verbose_cursor_string (fraction), at_x, at_y);
1834 _verbose_cursor->show ();
1839 if (mouse_mode == MouseGain) {
1840 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1842 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredGainLine.get();
1843 if (is_drawable()) {
1844 set_canvas_cursor (_cursors->fader);
1849 case AutomationLineItem:
1850 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1851 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1853 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredAutomationLine.get();
1855 if (is_drawable()) {
1856 set_canvas_cursor (_cursors->fader);
1861 case RegionViewNameHighlight:
1862 if (is_drawable() && effective_mouse_mode() == MouseObject && entered_regionview) {
1863 set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1864 _over_region_trim_target = true;
1868 case LeftFrameHandle:
1869 case RightFrameHandle:
1870 if (is_drawable() && effective_mouse_mode() == MouseObject && !internal_editing() && entered_regionview) {
1871 set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1875 case StartSelectionTrimItem:
1877 case ImageFrameHandleStartItem:
1878 case MarkerViewHandleStartItem:
1880 if (is_drawable()) {
1881 set_canvas_cursor (_cursors->left_side_trim);
1884 case EndSelectionTrimItem:
1886 case ImageFrameHandleEndItem:
1887 case MarkerViewHandleEndItem:
1889 if (is_drawable()) {
1890 set_canvas_cursor (_cursors->right_side_trim);
1894 case PlayheadCursorItem:
1895 if (is_drawable()) {
1896 switch (_edit_point) {
1898 set_canvas_cursor (_cursors->grabber_edit_point);
1901 set_canvas_cursor (_cursors->grabber);
1907 case RegionViewName:
1909 /* when the name is not an active item, the entire name highlight is for trimming */
1911 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1912 if (mouse_mode == MouseObject && is_drawable()) {
1913 set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1914 _over_region_trim_target = true;
1920 case AutomationTrackItem:
1921 if (is_drawable()) {
1922 Gdk::Cursor *cursor;
1923 switch (mouse_mode) {
1925 cursor = _cursors->selector;
1928 cursor = _cursors->zoom_in;
1931 cursor = _cursors->cross_hair;
1935 set_canvas_cursor (cursor);
1937 AutomationTimeAxisView* atv;
1938 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1939 clear_entered_track = false;
1940 set_entered_track (atv);
1946 case RangeMarkerBarItem:
1947 case TransportMarkerBarItem:
1948 case CdMarkerBarItem:
1951 if (is_drawable()) {
1952 set_canvas_cursor (_cursors->timebar);
1957 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1960 entered_marker = marker;
1961 marker->set_color_rgba (ARDOUR_UI::config()->canvasvar_EnteredMarker.get());
1963 case MeterMarkerItem:
1964 case TempoMarkerItem:
1965 if (is_drawable()) {
1966 set_canvas_cursor (_cursors->timebar);
1970 case FadeInHandleItem:
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_in);
1980 case FadeOutHandleItem:
1981 if (mouse_mode == MouseObject && !internal_editing()) {
1982 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1984 rect->property_fill_color_rgba() = 0xBBBBBBAA;
1986 set_canvas_cursor (_cursors->fade_out);
1989 case FeatureLineItem:
1991 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1992 line->property_fill_color_rgba() = 0xFF0000FF;
1996 if ( get_smart_mode() ) {
1997 set_canvas_cursor ();
2005 /* second pass to handle entered track status in a comprehensible way.
2008 switch (item_type) {
2010 case AutomationLineItem:
2011 case ControlPointItem:
2012 /* these do not affect the current entered track state */
2013 clear_entered_track = false;
2016 case AutomationTrackItem:
2017 /* handled above already */
2021 set_entered_track (0);
2029 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent*, ItemType item_type)
2039 switch (item_type) {
2040 case ControlPointItem:
2041 cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
2042 if (cp->line().the_list()->interpolation() != AutomationList::Discrete) {
2043 if (cp->line().npoints() > 1 && !cp->get_selected()) {
2044 cp->set_visible (false);
2048 if (is_drawable()) {
2049 set_canvas_cursor (current_canvas_cursor);
2052 _verbose_cursor->hide ();
2055 case RegionViewNameHighlight:
2056 case LeftFrameHandle:
2057 case RightFrameHandle:
2058 case StartSelectionTrimItem:
2059 case EndSelectionTrimItem:
2060 case PlayheadCursorItem:
2063 case ImageFrameHandleStartItem:
2064 case ImageFrameHandleEndItem:
2065 case MarkerViewHandleStartItem:
2066 case MarkerViewHandleEndItem:
2069 _over_region_trim_target = false;
2071 if (is_drawable()) {
2072 set_canvas_cursor (current_canvas_cursor);
2077 case AutomationLineItem:
2078 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
2080 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
2082 line->property_fill_color_rgba() = al->get_line_color();
2084 if (is_drawable()) {
2085 set_canvas_cursor (current_canvas_cursor);
2089 case RegionViewName:
2090 /* see enter_handler() for notes */
2091 _over_region_trim_target = false;
2093 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
2094 if (is_drawable() && mouse_mode == MouseObject) {
2095 set_canvas_cursor (current_canvas_cursor);
2100 case RangeMarkerBarItem:
2101 case TransportMarkerBarItem:
2102 case CdMarkerBarItem:
2106 if (is_drawable()) {
2107 set_canvas_cursor (current_canvas_cursor);
2112 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2116 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
2117 location_flags_changed (loc, this);
2120 case MeterMarkerItem:
2121 case TempoMarkerItem:
2123 if (is_drawable()) {
2124 set_canvas_cursor (current_canvas_cursor);
2129 case FadeInHandleItem:
2130 case FadeOutHandleItem:
2131 rv = static_cast<RegionView*>(item->get_data ("regionview"));
2133 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
2135 rect->property_fill_color_rgba() = rv->get_fill_color();
2138 set_canvas_cursor (current_canvas_cursor);
2141 case AutomationTrackItem:
2142 if (is_drawable()) {
2143 set_canvas_cursor (current_canvas_cursor);
2144 clear_entered_track = true;
2145 Glib::signal_idle().connect (sigc::mem_fun(*this, &Editor::left_automation_track));
2148 case FeatureLineItem:
2150 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
2151 line->property_fill_color_rgba() = (guint) ARDOUR_UI::config()->canvasvar_ZeroLine.get();;
2163 Editor::left_automation_track ()
2165 if (clear_entered_track) {
2166 set_entered_track (0);
2167 clear_entered_track = false;
2173 Editor::scrub (framepos_t frame, double current_x)
2177 if (scrubbing_direction == 0) {
2179 _session->request_locate (frame, false);
2180 _session->request_transport_speed (0.1);
2181 scrubbing_direction = 1;
2185 if (last_scrub_x > current_x) {
2187 /* pointer moved to the left */
2189 if (scrubbing_direction > 0) {
2191 /* we reversed direction to go backwards */
2194 scrub_reverse_distance += (int) (last_scrub_x - current_x);
2198 /* still moving to the left (backwards) */
2200 scrub_reversals = 0;
2201 scrub_reverse_distance = 0;
2203 delta = 0.01 * (last_scrub_x - current_x);
2204 _session->request_transport_speed_nonzero (_session->transport_speed() - delta);
2208 /* pointer moved to the right */
2210 if (scrubbing_direction < 0) {
2211 /* we reversed direction to go forward */
2214 scrub_reverse_distance += (int) (current_x - last_scrub_x);
2217 /* still moving to the right */
2219 scrub_reversals = 0;
2220 scrub_reverse_distance = 0;
2222 delta = 0.01 * (current_x - last_scrub_x);
2223 _session->request_transport_speed_nonzero (_session->transport_speed() + delta);
2227 /* if there have been more than 2 opposite motion moves detected, or one that moves
2228 back more than 10 pixels, reverse direction
2231 if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
2233 if (scrubbing_direction > 0) {
2234 /* was forwards, go backwards */
2235 _session->request_transport_speed (-0.1);
2236 scrubbing_direction = -1;
2238 /* was backwards, go forwards */
2239 _session->request_transport_speed (0.1);
2240 scrubbing_direction = 1;
2243 scrub_reverse_distance = 0;
2244 scrub_reversals = 0;
2248 last_scrub_x = current_x;
2252 Editor::motion_handler (ArdourCanvas::Item* /*item*/, GdkEvent* event, bool from_autoscroll)
2254 _last_motion_y = event->motion.y;
2256 if (event->motion.is_hint) {
2259 /* We call this so that MOTION_NOTIFY events continue to be
2260 delivered to the canvas. We need to do this because we set
2261 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
2262 the density of the events, at the expense of a round-trip
2263 to the server. Given that this will mostly occur on cases
2264 where DISPLAY = :0.0, and given the cost of what the motion
2265 event might do, its a good tradeoff.
2268 track_canvas->get_pointer (x, y);
2271 if (current_stepping_trackview) {
2272 /* don't keep the persistent stepped trackview if the mouse moves */
2273 current_stepping_trackview = 0;
2274 step_timeout.disconnect ();
2277 if (_session && _session->actively_recording()) {
2278 /* Sorry. no dragging stuff around while we record */
2282 JoinObjectRangeState const old = _join_object_range_state;
2283 update_join_object_range_location (event->motion.x, event->motion.y);
2285 if (!_internal_editing && _join_object_range_state != old) {
2286 set_canvas_cursor ();
2289 if (!_internal_editing && _over_region_trim_target) {
2290 set_canvas_cursor_for_region_view (event->motion.x, entered_regionview);
2293 bool handled = false;
2294 if (_drags->active ()) {
2295 handled = _drags->motion_handler (event, from_autoscroll);
2302 track_canvas_motion (event);
2307 Editor::can_remove_control_point (ArdourCanvas::Item* item)
2309 ControlPoint* control_point;
2311 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2312 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2316 AutomationLine& line = control_point->line ();
2317 if (dynamic_cast<AudioRegionGainLine*> (&line)) {
2318 /* we shouldn't remove the first or last gain point in region gain lines */
2319 if (line.is_last_point(*control_point) || line.is_first_point(*control_point)) {
2328 Editor::remove_control_point (ArdourCanvas::Item* item)
2330 if (!can_remove_control_point (item)) {
2334 ControlPoint* control_point;
2336 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2337 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2341 control_point->line().remove_point (*control_point);
2345 Editor::edit_control_point (ArdourCanvas::Item* item)
2347 ControlPoint* p = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"));
2350 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2354 ControlPointDialog d (p);
2355 d.set_position (Gtk::WIN_POS_MOUSE);
2358 if (d.run () != RESPONSE_ACCEPT) {
2362 p->line().modify_point_y (*p, d.get_y_fraction ());
2366 Editor::edit_notes (MidiRegionView::Selection const & s)
2372 EditNoteDialog d (&(*s.begin())->region_view(), s);
2373 d.set_position (Gtk::WIN_POS_MOUSE);
2381 Editor::visible_order_range (int* low, int* high) const
2383 *low = TimeAxisView::max_order ();
2386 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
2388 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
2390 if (!rtv->hidden()) {
2392 if (*high < rtv->order()) {
2393 *high = rtv->order ();
2396 if (*low > rtv->order()) {
2397 *low = rtv->order ();
2404 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
2406 /* Either add to or set the set the region selection, unless
2407 this is an alignment click (control used)
2410 if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
2411 TimeAxisView* tv = &rv.get_time_axis_view();
2412 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
2414 if (rtv && rtv->is_track()) {
2415 speed = rtv->track()->speed();
2418 framepos_t where = get_preferred_edit_position();
2422 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
2424 align_region (rv.region(), SyncPoint, (framepos_t) (where * speed));
2426 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
2428 align_region (rv.region(), End, (framepos_t) (where * speed));
2432 align_region (rv.region(), Start, (framepos_t) (where * speed));
2439 Editor::collect_new_region_view (RegionView* rv)
2441 latest_regionviews.push_back (rv);
2445 Editor::collect_and_select_new_region_view (RegionView* rv)
2448 latest_regionviews.push_back (rv);
2452 Editor::cancel_selection ()
2454 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2455 (*i)->hide_selection ();
2458 selection->clear ();
2459 clicked_selection = 0;
2463 Editor::cancel_time_selection ()
2465 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2466 (*i)->hide_selection ();
2468 selection->time.clear ();
2469 clicked_selection = 0;
2473 Editor::point_trim (GdkEvent* event, framepos_t new_bound)
2475 RegionView* rv = clicked_regionview;
2477 /* Choose action dependant on which button was pressed */
2478 switch (event->button.button) {
2480 begin_reversible_command (_("start point trim"));
2482 if (selection->selected (rv)) {
2483 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
2484 i != selection->regions.by_layer().end(); ++i)
2486 if (!(*i)->region()->locked()) {
2487 (*i)->region()->clear_changes ();
2488 (*i)->region()->trim_front (new_bound);
2489 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2494 if (!rv->region()->locked()) {
2495 rv->region()->clear_changes ();
2496 rv->region()->trim_front (new_bound);
2497 _session->add_command(new StatefulDiffCommand (rv->region()));
2501 commit_reversible_command();
2505 begin_reversible_command (_("End point trim"));
2507 if (selection->selected (rv)) {
2509 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
2511 if (!(*i)->region()->locked()) {
2512 (*i)->region()->clear_changes();
2513 (*i)->region()->trim_end (new_bound);
2514 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2520 if (!rv->region()->locked()) {
2521 rv->region()->clear_changes ();
2522 rv->region()->trim_end (new_bound);
2523 _session->add_command (new StatefulDiffCommand (rv->region()));
2527 commit_reversible_command();
2536 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* /*event*/)
2541 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2542 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2546 Location* location = find_location_from_marker (marker, is_start);
2547 location->set_hidden (true, this);
2552 Editor::reposition_zoom_rect (framepos_t start, framepos_t end)
2554 double x1 = frame_to_pixel (start);
2555 double x2 = frame_to_pixel (end);
2556 double y2 = full_canvas_height - 1.0;
2558 zoom_rect->property_x1() = x1;
2559 zoom_rect->property_y1() = 1.0;
2560 zoom_rect->property_x2() = x2;
2561 zoom_rect->property_y2() = y2;
2566 Editor::mouse_rename_region (ArdourCanvas::Item* /*item*/, GdkEvent* /*event*/)
2568 using namespace Gtkmm2ext;
2570 ArdourPrompter prompter (false);
2572 prompter.set_prompt (_("Name for region:"));
2573 prompter.set_initial_text (clicked_regionview->region()->name());
2574 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
2575 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
2576 prompter.show_all ();
2577 switch (prompter.run ()) {
2578 case Gtk::RESPONSE_ACCEPT:
2580 prompter.get_result(str);
2582 clicked_regionview->region()->set_name (str);
2591 Editor::mouse_brush_insert_region (RegionView* rv, framepos_t pos)
2593 /* no brushing without a useful snap setting */
2595 switch (_snap_mode) {
2597 return; /* can't work because it allows region to be placed anywhere */
2602 switch (_snap_type) {
2610 /* don't brush a copy over the original */
2612 if (pos == rv->region()->position()) {
2616 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
2618 if (rtv == 0 || !rtv->is_track()) {
2622 boost::shared_ptr<Playlist> playlist = rtv->playlist();
2623 double speed = rtv->track()->speed();
2625 playlist->clear_changes ();
2626 boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region(), true));
2627 playlist->add_region (new_region, (framepos_t) (pos * speed));
2628 _session->add_command (new StatefulDiffCommand (playlist));
2630 // playlist is frozen, so we have to update manually XXX this is disgusting
2632 playlist->RegionAdded (new_region); /* EMIT SIGNAL */
2636 Editor::track_height_step_timeout ()
2638 if (get_microseconds() - last_track_height_step_timestamp < 250000) {
2639 current_stepping_trackview = 0;
2646 Editor::add_region_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2648 assert (region_view);
2650 if (!region_view->region()->playlist()) {
2654 _region_motion_group->raise_to_top ();
2656 if (Config->get_edit_mode() == Splice) {
2657 _drags->add (new RegionSpliceDrag (this, item, region_view, selection->regions.by_layer()));
2659 RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::select.property_id);
2660 _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), false, false));
2663 /* sync the canvas to what we think is its current state */
2664 update_canvas_now();
2668 Editor::add_region_copy_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2670 assert (region_view);
2672 if (!region_view->region()->playlist()) {
2676 _region_motion_group->raise_to_top ();
2678 RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::select.property_id);
2679 _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), false, true));
2683 Editor::add_region_brush_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2685 assert (region_view);
2687 if (!region_view->region()->playlist()) {
2691 if (Config->get_edit_mode() == Splice) {
2695 RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::select.property_id);
2696 _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), true, false));
2698 begin_reversible_command (Operations::drag_region_brush);
2701 /** Start a grab where a time range is selected, track(s) are selected, and the
2702 * user clicks and drags a region with a modifier in order to create a new region containing
2703 * the section of the clicked region that lies within the time range.
2706 Editor::start_selection_grab (ArdourCanvas::Item* /*item*/, GdkEvent* event)
2708 if (clicked_regionview == 0) {
2712 /* lets try to create new Region for the selection */
2714 vector<boost::shared_ptr<Region> > new_regions;
2715 create_region_from_selection (new_regions);
2717 if (new_regions.empty()) {
2721 /* XXX fix me one day to use all new regions */
2723 boost::shared_ptr<Region> region (new_regions.front());
2725 /* add it to the current stream/playlist.
2727 tricky: the streamview for the track will add a new regionview. we will
2728 catch the signal it sends when it creates the regionview to
2729 set the regionview we want to then drag.
2732 latest_regionviews.clear();
2733 sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
2735 /* A selection grab currently creates two undo/redo operations, one for
2736 creating the new region and another for moving it.
2739 begin_reversible_command (Operations::selection_grab);
2741 boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
2743 playlist->clear_changes ();
2744 clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
2745 _session->add_command(new StatefulDiffCommand (playlist));
2747 commit_reversible_command ();
2751 if (latest_regionviews.empty()) {
2752 /* something went wrong */
2756 /* we need to deselect all other regionviews, and select this one
2757 i'm ignoring undo stuff, because the region creation will take care of it
2759 selection->set (latest_regionviews);
2761 _drags->set (new RegionMoveDrag (this, latest_regionviews.front()->get_canvas_group(), latest_regionviews.front(), latest_regionviews, false, false), event);
2767 if (_drags->active ()) {
2770 selection->clear ();
2775 Editor::set_internal_edit (bool yn)
2777 if (_internal_editing == yn) {
2781 _internal_editing = yn;
2784 pre_internal_mouse_mode = mouse_mode;
2785 pre_internal_snap_type = _snap_type;
2786 pre_internal_snap_mode = _snap_mode;
2788 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2789 (*i)->enter_internal_edit_mode ();
2792 set_snap_to (internal_snap_type);
2793 set_snap_mode (internal_snap_mode);
2797 internal_snap_mode = _snap_mode;
2798 internal_snap_type = _snap_type;
2800 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2801 (*i)->leave_internal_edit_mode ();
2804 if (mouse_mode == MouseDraw && pre_internal_mouse_mode != MouseDraw) {
2805 /* we were drawing .. flip back to something sensible */
2806 set_mouse_mode (pre_internal_mouse_mode);
2809 set_snap_to (pre_internal_snap_type);
2810 set_snap_mode (pre_internal_snap_mode);
2813 set_canvas_cursor ();
2816 /** Update _join_object_range_state which indicate whether we are over the top or bottom half of a region view,
2817 * used by the `join object/range' tool mode.
2820 Editor::update_join_object_range_location (double /*x*/, double y)
2822 /* XXX: actually, this decides based on whether the mouse is in the top
2823 or bottom half of a the waveform part RouteTimeAxisView;
2825 Note that entered_{track,regionview} is not always setup (e.g. if
2826 the mouse is over a TimeSelection), and to get a Region
2827 that we're over requires searching the playlist.
2830 if ( !get_smart_mode() ) {
2831 _join_object_range_state = JOIN_OBJECT_RANGE_NONE;
2835 if (mouse_mode == MouseObject) {
2836 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2837 } else if (mouse_mode == MouseRange) {
2838 _join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
2841 /* XXX: maybe we should make entered_track work in all cases, rather than resorting to this */
2842 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y + vertical_adjustment.get_value() - canvas_timebars_vsize);
2846 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
2851 rtv->canvas_display()->w2i (cx, cy);
2853 double const c = cy / (rtv->view()->child_height() - TimeAxisViewItem::NAME_HIGHLIGHT_SIZE);
2855 _join_object_range_state = c <= 0.5 ? JOIN_OBJECT_RANGE_RANGE : JOIN_OBJECT_RANGE_OBJECT;
2861 Editor::effective_mouse_mode () const
2863 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
2865 } else if (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE) {
2873 Editor::remove_midi_note (ArdourCanvas::Item* item, GdkEvent *)
2875 ArdourCanvas::CanvasNoteEvent* e = dynamic_cast<ArdourCanvas::CanvasNoteEvent*> (item);
2878 e->region_view().delete_note (e->note ());
2882 Editor::set_canvas_cursor_for_region_view (double x, RegionView* rv)
2886 ArdourCanvas::Group* g = rv->get_canvas_group ();
2887 ArdourCanvas::Group* p = g->get_parent_group ();
2889 /* Compute x in region view parent coordinates */
2893 double x1, x2, y1, y2;
2894 g->get_bounds (x1, y1, x2, y2);
2896 /* Halfway across the region */
2897 double const h = (x1 + x2) / 2;
2899 Trimmable::CanTrim ct = rv->region()->can_trim ();
2901 if (ct & Trimmable::FrontTrimEarlier) {
2902 set_canvas_cursor (_cursors->left_side_trim);
2904 set_canvas_cursor (_cursors->left_side_trim_right_only);
2907 if (ct & Trimmable::EndTrimLater) {
2908 set_canvas_cursor (_cursors->right_side_trim);
2910 set_canvas_cursor (_cursors->right_side_trim_left_only);
2915 /** Obtain the pointer position in world coordinates */
2917 Editor::get_pointer_position (double& x, double& y) const
2920 track_canvas->get_pointer (px, py);
2921 track_canvas->window_to_world (px, py, x, y);