2 Copyright (C) 2000-2001 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
29 #include "pbd/error.h"
30 #include "pbd/enumwriter.h"
31 #include "pbd/memento_command.h"
32 #include "pbd/basename.h"
33 #include "pbd/stateful_diff_command.h"
35 #include "gtkmm2ext/bindings.h"
36 #include "gtkmm2ext/utils.h"
37 #include "gtkmm2ext/tearoff.h"
39 #include "canvas/canvas.h"
41 #include "ardour/audioregion.h"
42 #include "ardour/operations.h"
43 #include "ardour/playlist.h"
44 #include "ardour/profile.h"
45 #include "ardour/region_factory.h"
46 #include "ardour/route.h"
47 #include "ardour/session.h"
48 #include "ardour/types.h"
50 #include "ardour_ui.h"
53 #include "time_axis_view.h"
54 #include "audio_time_axis.h"
55 #include "audio_region_view.h"
56 #include "midi_region_view.h"
58 #include "streamview.h"
59 #include "region_gain_line.h"
60 #include "automation_time_axis.h"
61 #include "control_point.h"
64 #include "selection.h"
67 #include "rgb_macros.h"
68 #include "control_point_dialog.h"
69 #include "editor_drag.h"
70 #include "automation_region_view.h"
71 #include "edit_note_dialog.h"
72 #include "mouse_cursors.h"
73 #include "editor_cursors.h"
74 #include "verbose_cursor.h"
80 using namespace ARDOUR;
83 using namespace Editing;
84 using Gtkmm2ext::Keyboard;
87 Editor::mouse_frame (framepos_t& where, bool& in_track_canvas) const
89 /* gdk_window_get_pointer() has X11's XQueryPointer semantics in that it only
90 pays attentions to subwindows. this means that menu windows are ignored, and
91 if the pointer is in a menu, the return window from the call will be the
92 the regular subwindow *under* the menu.
94 this matters quite a lot if the pointer is moving around in a menu that overlaps
95 the track canvas because we will believe that we are within the track canvas
96 when we are not. therefore, we track enter/leave events for the track canvas
97 and allow that to override the result of gdk_window_get_pointer().
100 if (!within_track_canvas) {
105 Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->_track_canvas->get_window();
107 if (!canvas_window) {
111 Glib::RefPtr<const Gdk::Window> pointer_window = Gdk::Display::get_default()->get_window_at_pointer (x, y);
113 if (!pointer_window) {
117 if (pointer_window != canvas_window) {
118 in_track_canvas = false;
122 in_track_canvas = true;
125 event.type = GDK_BUTTON_RELEASE;
129 where = window_event_sample (&event, 0, 0);
135 Editor::window_event_sample (GdkEvent const * event, double* pcx, double* pcy) const
137 ArdourCanvas::Duple d;
139 if (!gdk_event_get_coords (event, &d.x, &d.y)) {
143 /* event coordinates are in window units, so convert to canvas
146 d = _track_canvas->window_to_canvas (d);
156 return pixel_to_sample (d.x);
160 Editor::canvas_event_sample (GdkEvent const * event, double* pcx, double* pcy) const
165 /* event coordinates are already in canvas units */
167 if (!gdk_event_get_coords (event, &x, &y)) {
168 cerr << "!NO c COORDS for event type " << event->type << endl;
180 /* note that pixel_to_sample_from_event() never returns less than zero, so even if the pixel
181 position is negative (as can be the case with motion events in particular),
182 the frame location is always positive.
185 return pixel_to_sample_from_event (x);
189 Editor::which_grabber_cursor ()
191 Gdk::Cursor* c = _cursors->grabber;
193 if (_internal_editing) {
194 switch (mouse_mode) {
196 c = _cursors->midi_pencil;
200 c = _cursors->grabber_note;
204 c = _cursors->midi_resize;
208 c = _cursors->grabber_note;
217 switch (_edit_point) {
219 c = _cursors->grabber_edit_point;
222 boost::shared_ptr<Movable> m = _movable.lock();
223 if (m && m->locked()) {
224 c = _cursors->speaker;
234 Editor::set_current_trimmable (boost::shared_ptr<Trimmable> t)
236 boost::shared_ptr<Trimmable> st = _trimmable.lock();
238 if (!st || st == t) {
240 set_canvas_cursor ();
245 Editor::set_current_movable (boost::shared_ptr<Movable> m)
247 boost::shared_ptr<Movable> sm = _movable.lock();
249 if (!sm || sm != m) {
251 set_canvas_cursor ();
256 Editor::set_canvas_cursor ()
258 switch (mouse_mode) {
260 current_canvas_cursor = _cursors->selector;
261 if (_internal_editing) {
262 current_canvas_cursor = which_grabber_cursor();
267 current_canvas_cursor = which_grabber_cursor();
271 current_canvas_cursor = _cursors->midi_pencil;
275 current_canvas_cursor = _cursors->cross_hair;
279 if (Keyboard::the_keyboard().key_is_down (GDK_Control_L)) {
280 current_canvas_cursor = _cursors->zoom_out;
282 current_canvas_cursor = _cursors->zoom_in;
287 current_canvas_cursor = _cursors->time_fx; // just use playhead
291 current_canvas_cursor = _cursors->speaker;
295 if (!_internal_editing) {
296 switch (_join_object_range_state) {
297 case JOIN_OBJECT_RANGE_NONE:
299 case JOIN_OBJECT_RANGE_OBJECT:
300 current_canvas_cursor = which_grabber_cursor ();
302 case JOIN_OBJECT_RANGE_RANGE:
303 current_canvas_cursor = _cursors->selector;
308 /* up-down cursor as a cue that automation can be dragged up and down when in join object/range mode */
309 if (!_internal_editing && get_smart_mode() ) {
312 get_pointer_position (x, y);
314 if (x >= 0 && y >= 0) {
316 vector<ArdourCanvas::Item const *> items;
318 /* Note how we choose a specific scroll group to get
319 * items from. This could be problematic.
322 hv_scroll_group->add_items_at_point (ArdourCanvas::Duple (x,y), items);
324 // first item will be the upper most
326 if (!items.empty()) {
327 const ArdourCanvas::Item* i = items.front();
329 if (i && i->parent() && i->parent()->get_data (X_("timeselection"))) {
330 pair<TimeAxisView*, int> tvp = trackview_by_y_position (_last_motion_y);
331 if (dynamic_cast<AutomationTimeAxisView*> (tvp.first)) {
332 current_canvas_cursor = _cursors->up_down;
339 set_canvas_cursor (current_canvas_cursor, true);
343 Editor::mouse_mode_object_range_toggled()
345 MouseMode m = mouse_mode;
347 Glib::RefPtr<Action> act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-range"));
349 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
352 if (tact->get_active()) {
353 m = MouseObject; //Smart mode turned to ON, force editing to Object mode
356 set_mouse_mode(m, true); //call this so the button styles can get updated
360 Editor::set_mouse_mode (MouseMode m, bool force)
362 if (_drags->active ()) {
366 if (!force && m == mouse_mode) {
370 Glib::RefPtr<Action> act;
374 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-range"));
378 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-object"));
382 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-draw"));
386 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-gain"));
390 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-zoom"));
394 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-timefx"));
398 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-audition"));
404 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
407 /* go there and back to ensure that the toggled handler is called to set up mouse_mode */
408 tact->set_active (false);
409 tact->set_active (true);
411 //NOTE: this will result in a call to mouse_mode_toggled which does the heavy lifting
415 Editor::mouse_mode_toggled (MouseMode m)
417 Glib::RefPtr<Action> act;
418 Glib::RefPtr<ToggleAction> tact;
422 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-range"));
426 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-object"));
430 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-draw"));
434 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-gain"));
438 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-zoom"));
442 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-timefx"));
446 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-audition"));
452 tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
455 if (!tact->get_active()) {
456 /* this was just the notification that the old mode has been
457 * left. we'll get called again with the new mode active in a
465 act = ActionManager::get_action (X_("MouseMode"), X_("toggle-internal-edit"));
466 tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
467 tact->set_active (true);
473 if (_session && mouse_mode == MouseAudition) {
474 /* stop transport and reset default speed to avoid oddness with
476 _session->request_transport_speed (0.0, true);
483 //TODO: set button styles for smart buttons
485 if ( smart_mode_action->get_active() ) {
486 if( mouse_mode == MouseObject ) { //smart active and object active
487 smart_mode_button.set_active(1);
488 smart_mode_button.set_name("smart mode button");
489 mouse_move_button.set_name("smart mode button");
490 } else { //smart active but object inactive
491 smart_mode_button.set_active(0);
492 smart_mode_button.set_name("smart mode button");
493 mouse_move_button.set_name("mouse mode button");
496 smart_mode_button.set_active(0);
497 smart_mode_button.set_name("mouse mode button");
498 mouse_move_button.set_name("mouse mode button");
502 set_canvas_cursor ();
503 set_gain_envelope_visibility ();
505 update_time_selection_display ();
507 MouseModeChanged (); /* EMIT SIGNAL */
511 Editor::update_time_selection_display ()
513 if (smart_mode_action->get_active()) {
514 /* not sure what to do here */
515 if (mouse_mode == MouseObject) {
519 switch (mouse_mode) {
521 selection->clear_objects ();
524 selection->clear_time ();
531 Editor::step_mouse_mode (bool next)
533 switch (current_mouse_mode()) {
536 if (Profile->get_sae()) {
537 set_mouse_mode (MouseZoom);
539 set_mouse_mode (MouseRange);
542 set_mouse_mode (MouseTimeFX);
547 if (next) set_mouse_mode (MouseDraw);
548 else set_mouse_mode (MouseObject);
552 if (next) set_mouse_mode (MouseZoom);
553 else set_mouse_mode (MouseRange);
558 if (Profile->get_sae()) {
559 set_mouse_mode (MouseTimeFX);
561 set_mouse_mode (MouseGain);
564 if (Profile->get_sae()) {
565 set_mouse_mode (MouseObject);
567 set_mouse_mode (MouseDraw);
573 if (next) set_mouse_mode (MouseTimeFX);
574 else set_mouse_mode (MouseZoom);
579 set_mouse_mode (MouseAudition);
581 if (Profile->get_sae()) {
582 set_mouse_mode (MouseZoom);
584 set_mouse_mode (MouseGain);
590 if (next) set_mouse_mode (MouseObject);
591 else set_mouse_mode (MouseTimeFX);
597 Editor::toggle_internal_editing_from_double_click (GdkEvent* event)
599 if (_drags->active()) {
600 _drags->end_grab (event);
603 ActionManager::do_action ("MouseMode", "toggle-internal-edit");
605 /* prevent reversion of edit cursor on button release */
607 pre_press_cursor = 0;
613 Editor::button_selection (ArdourCanvas::Item* /*item*/, GdkEvent* event, ItemType item_type)
615 /* in object/audition/timefx/gain-automation mode,
616 any button press sets the selection if the object
617 can be selected. this is a bit of hack, because
618 we want to avoid this if the mouse operation is a
621 note: not dbl-click or triple-click
623 Also note that there is no region selection in internal edit mode, otherwise
624 for operations operating on the selection (e.g. cut) it is not obvious whether
625 to cut notes or regions.
628 MouseMode eff_mouse_mode = mouse_mode;
630 if (get_smart_mode() && eff_mouse_mode == MouseRange && event->button.button == 3 && item_type == RegionItem) {
631 /* context clicks are always about object properties, even if
632 we're in range mode within smart mode.
634 eff_mouse_mode = MouseObject;
637 if (((mouse_mode != MouseObject) &&
638 (mouse_mode != MouseAudition || item_type != RegionItem) &&
639 (mouse_mode != MouseTimeFX || item_type != RegionItem) &&
640 (mouse_mode != MouseGain) &&
641 (mouse_mode != MouseDraw)) ||
642 ((event->type != GDK_BUTTON_PRESS && event->type != GDK_BUTTON_RELEASE) || event->button.button > 3) ||
643 (internal_editing() && mouse_mode != MouseTimeFX)) {
648 if (event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) {
650 if ((event->button.state & Keyboard::RelevantModifierKeyMask) && event->button.button != 1) {
652 /* almost no selection action on modified button-2 or button-3 events */
654 if (item_type != RegionItem && event->button.button != 2) {
660 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
661 bool press = (event->type == GDK_BUTTON_PRESS);
666 if (eff_mouse_mode != MouseRange) {
667 set_selected_regionview_from_click (press, op);
669 /* don't change the selection unless the
670 clicked track is not currently selected. if
671 so, "collapse" the selection to just this
674 if (!selection->selected (clicked_axisview)) {
675 set_selected_track_as_side_effect (Selection::Set);
679 if (eff_mouse_mode != MouseRange) {
680 set_selected_regionview_from_click (press, op);
685 case RegionViewNameHighlight:
687 case LeftFrameHandle:
688 case RightFrameHandle:
689 if (eff_mouse_mode != MouseRange) {
690 set_selected_regionview_from_click (press, op);
691 } else if (event->type == GDK_BUTTON_PRESS) {
692 set_selected_track_as_side_effect (op);
696 case FadeInHandleItem:
697 case FadeInTrimHandleItem:
699 case FadeOutHandleItem:
700 case FadeOutTrimHandleItem:
702 case StartCrossFadeItem:
703 case EndCrossFadeItem:
704 if (eff_mouse_mode != MouseRange) {
705 cerr << "Should be setting selected regionview\n";
706 set_selected_regionview_from_click (press, op);
707 } else if (event->type == GDK_BUTTON_PRESS) {
708 set_selected_track_as_side_effect (op);
712 case ControlPointItem:
713 set_selected_track_as_side_effect (op);
714 if (eff_mouse_mode != MouseRange) {
715 set_selected_control_point_from_click (press, op);
720 /* for context click, select track */
721 if (event->button.button == 3) {
722 selection->clear_tracks ();
723 set_selected_track_as_side_effect (op);
727 case AutomationTrackItem:
728 set_selected_track_as_side_effect (op);
737 Editor::button_press_handler_1 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
739 /* single mouse clicks on any of these item types operate
740 independent of mouse mode, mostly because they are
741 not on the main track canvas or because we want
746 case PlayheadCursorItem:
747 _drags->set (new CursorDrag (this, *playhead_cursor, true), event);
751 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
752 hide_marker (item, event);
754 _drags->set (new MarkerDrag (this, item), event);
758 case TempoMarkerItem:
760 TempoMarker* m = reinterpret_cast<TempoMarker*> (item->get_data ("marker"));
763 new TempoMarkerDrag (
766 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
773 case MeterMarkerItem:
775 MeterMarker* m = reinterpret_cast<MeterMarker*> (item->get_data ("marker"));
778 new MeterMarkerDrag (
781 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
789 _drags->set (new VideoTimeLineDrag (this, item), event);
796 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
797 _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
803 case RangeMarkerBarItem:
804 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
805 _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
807 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateRangeMarker), event);
812 case CdMarkerBarItem:
813 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
814 _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
816 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateCDMarker), event);
821 case TransportMarkerBarItem:
822 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
823 _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
825 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateTransportMarker), event);
834 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
835 /* special case: allow trim of range selections in joined object mode;
836 in theory eff should equal MouseRange in this case, but it doesn't
837 because entering the range selection canvas item results in entered_regionview
838 being set to 0, so update_join_object_range_location acts as if we aren't
841 if (item_type == StartSelectionTrimItem) {
842 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
843 } else if (item_type == EndSelectionTrimItem) {
844 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
848 Editing::MouseMode eff = effective_mouse_mode ();
850 /* special case: allow drag of region fade in/out in object mode with join object/range enabled */
851 if (item_type == FadeInHandleItem || item_type == FadeOutHandleItem) {
855 /* there is no Range mode when in internal edit mode */
856 if (eff == MouseRange && internal_editing()) {
863 case StartSelectionTrimItem:
864 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
867 case EndSelectionTrimItem:
868 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
872 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
873 start_selection_grab (item, event);
875 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
876 /* grab selection for moving */
877 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionMove), event);
879 double const y = event->button.y;
880 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
882 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
883 if ( get_smart_mode() && atv) {
884 /* smart "join" mode: drag automation */
885 _drags->set (new AutomationRangeDrag (this, atv, selection->time), event, _cursors->up_down);
887 /* this was debated, but decided the more common action was to
888 make a new selection */
889 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
896 if (internal_editing()) {
897 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
898 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
902 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::RangeSelectModifier)) {
903 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionExtend), event);
905 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
911 case RegionViewNameHighlight:
912 if (!clicked_regionview->region()->locked()) {
913 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
919 if (!internal_editing()) {
920 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::RangeSelectModifier)) {
921 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionExtend), event);
923 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
933 /* Existing note: allow trimming/motion */
934 if (internal_editing()) {
935 /* trim notes if we're in internal edit mode and near the ends of the note */
936 NoteBase* cn = reinterpret_cast<NoteBase*>(item->get_data ("notebase"));
938 if (cn->big_enough_to_trim() && cn->mouse_near_ends()) {
939 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
941 _drags->set (new NoteDrag (this, item), event);
947 if (internal_editing()) {
948 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
949 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
963 /* Existing note: allow trimming/motion */
964 if (internal_editing()) {
965 NoteBase* cn = reinterpret_cast<NoteBase*> (item->get_data ("notebase"));
967 if (cn->big_enough_to_trim() && cn->mouse_near_ends()) {
968 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
970 _drags->set (new NoteDrag (this, item), event);
980 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)) &&
981 event->type == GDK_BUTTON_PRESS) {
983 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
985 } else if (event->type == GDK_BUTTON_PRESS) {
988 case FadeInHandleItem:
990 _drags->set (new FadeInDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), selection->regions), event, _cursors->fade_in);
994 case FadeOutHandleItem:
996 _drags->set (new FadeOutDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), selection->regions), event, _cursors->fade_out);
1000 case StartCrossFadeItem:
1001 case EndCrossFadeItem:
1002 /* we might allow user to grab inside the fade to trim a region with preserve_fade_anchor. for not this is not fully implemented */
1003 // if (!clicked_regionview->region()->locked()) {
1004 // _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer(), true), event);
1009 case FeatureLineItem:
1011 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier)) {
1012 remove_transient(item);
1016 _drags->set (new FeatureLineDrag (this, item), event);
1022 if (dynamic_cast<AutomationRegionView*> (clicked_regionview)) {
1023 /* click on an automation region view; do nothing here and let the ARV's signal handler
1029 if (internal_editing ()) {
1033 /* click on a normal region view */
1034 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
1035 add_region_copy_drag (item, event, clicked_regionview);
1036 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
1037 add_region_brush_drag (item, event, clicked_regionview);
1039 add_region_drag (item, event, clicked_regionview);
1043 // if (!internal_editing() && (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE && !selection->regions.empty())) {
1044 // _drags->add (new SelectionDrag (this, clicked_axisview->get_selection_rect (clicked_selection)->rect, SelectionDrag::SelectionMove));
1047 _drags->start_grab (event);
1051 case RegionViewNameHighlight:
1052 case LeftFrameHandle:
1053 case RightFrameHandle:
1054 if (!clicked_regionview->region()->locked()) {
1055 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1060 case FadeInTrimHandleItem:
1061 case FadeOutTrimHandleItem:
1062 if (!clicked_regionview->region()->locked()) {
1063 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer(), true), event);
1068 case RegionViewName:
1070 /* rename happens on edit clicks */
1071 if (clicked_regionview->get_name_highlight()) {
1072 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer()), event);
1078 case ControlPointItem:
1079 _drags->set (new ControlPointDrag (this, item), event);
1083 case AutomationLineItem:
1084 _drags->set (new LineDrag (this, item), event);
1089 if (internal_editing()) {
1090 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
1091 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
1095 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
1099 case AutomationTrackItem:
1101 TimeAxisView* parent = clicked_axisview->get_parent ();
1102 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (clicked_axisview);
1104 if (parent && dynamic_cast<MidiTimeAxisView*> (parent) && atv->show_regions ()) {
1106 RouteTimeAxisView* p = dynamic_cast<RouteTimeAxisView*> (parent);
1108 boost::shared_ptr<Playlist> pl = p->track()->playlist ();
1109 if (pl->n_regions() == 0) {
1110 /* Parent has no regions; create one so that we have somewhere to put automation */
1111 _drags->set (new RegionCreateDrag (this, item, parent), event);
1113 /* See if there's a region before the click that we can extend, and extend it if so */
1114 framepos_t const t = canvas_event_sample (event);
1115 boost::shared_ptr<Region> prev = pl->find_next_region (t, End, -1);
1117 _drags->set (new RegionCreateDrag (this, item, parent), event);
1119 prev->set_length (t - prev->position ());
1123 /* rubberband drag to select automation points */
1124 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
1131 if ( get_smart_mode() ) {
1132 /* we're in "smart" joined mode, and we've clicked on a Selection */
1133 double const y = event->button.y;
1134 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
1136 /* if we're over an automation track, start a drag of its data */
1137 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
1139 _drags->set (new AutomationRangeDrag (this, atv, selection->time), event, _cursors->up_down);
1142 /* if we're over a track and a region, and in the `object' part of a region,
1143 put a selection around the region and drag both
1145 /* RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1146 if (rtv && _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
1147 boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track> (rtv->route ());
1149 boost::shared_ptr<Playlist> pl = t->playlist ();
1152 boost::shared_ptr<Region> r = pl->top_region_at (canvas_event_sample (event));
1154 RegionView* rv = rtv->view()->find_view (r);
1155 clicked_selection = select_range (rv->region()->position(),
1156 rv->region()->last_frame()+1);
1157 _drags->add (new SelectionDrag (this, item, SelectionDrag::SelectionMove));
1158 list<RegionView*> rvs;
1160 _drags->add (new RegionMoveDrag (this, item, rv, rvs, false, false));
1161 _drags->start_grab (event);
1185 switch (item_type) {
1187 _drags->set (new LineDrag (this, item), event);
1190 case ControlPointItem:
1191 _drags->set (new ControlPointDrag (this, item), event);
1197 AudioRegionView* arv = dynamic_cast<AudioRegionView *> (clicked_regionview);
1199 _drags->set (new AutomationRangeDrag (this, arv, selection->time), event, _cursors->up_down);
1200 _drags->start_grab (event);
1206 case AutomationLineItem:
1207 _drags->set (new LineDrag (this, item), event);
1217 if (event->type == GDK_BUTTON_PRESS) {
1218 _drags->set (new MouseZoomDrag (this, item), event);
1225 if (internal_editing() && item_type == NoteItem ) {
1226 /* drag notes if we're in internal edit mode */
1227 NoteBase* cn = reinterpret_cast<NoteBase*>(item->get_data ("notebase"));
1229 if (cn->big_enough_to_trim()) {
1230 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
1233 } else if (clicked_regionview) {
1235 _drags->set (new TimeFXDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1241 _drags->set (new ScrubDrag (this, item), event);
1242 scrub_reversals = 0;
1243 scrub_reverse_distance = 0;
1244 last_scrub_x = event->button.x;
1245 scrubbing_direction = 0;
1246 set_canvas_cursor (_cursors->transparent);
1258 Editor::button_press_handler_2 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1260 Editing::MouseMode const eff = effective_mouse_mode ();
1263 switch (item_type) {
1265 if (internal_editing ()) {
1266 /* no region drags in internal edit mode */
1270 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
1271 add_region_copy_drag (item, event, clicked_regionview);
1273 add_region_drag (item, event, clicked_regionview);
1275 _drags->start_grab (event);
1278 case ControlPointItem:
1279 _drags->set (new ControlPointDrag (this, item), event);
1287 switch (item_type) {
1288 case RegionViewNameHighlight:
1289 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1293 case LeftFrameHandle:
1294 case RightFrameHandle:
1295 if (!internal_editing ()) {
1296 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1301 case RegionViewName:
1302 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer()), event);
1316 /* relax till release */
1322 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1323 temporal_zoom_to_frame (false, canvas_event_sample (event));
1325 temporal_zoom_to_frame (true, canvas_event_sample(event));
1338 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1340 if (event->type == GDK_2BUTTON_PRESS) {
1341 _drags->mark_double_click ();
1342 gdk_pointer_ungrab (GDK_CURRENT_TIME);
1346 if (event->type != GDK_BUTTON_PRESS) {
1350 pre_press_cursor = current_canvas_cursor;
1352 _track_canvas->grab_focus();
1354 if (_session && _session->actively_recording()) {
1358 if (internal_editing()) {
1359 bool leave_internal_edit_mode = false;
1361 switch (item_type) {
1366 if (!dynamic_cast<MidiRegionView*> (clicked_regionview) && !dynamic_cast<AutomationRegionView*> (clicked_regionview)) {
1367 leave_internal_edit_mode = true;
1371 case PlayheadCursorItem:
1373 case TempoMarkerItem:
1374 case MeterMarkerItem:
1378 case RangeMarkerBarItem:
1379 case CdMarkerBarItem:
1380 case TransportMarkerBarItem:
1382 /* button press on these events never does anything to
1383 change the editing mode.
1391 if (leave_internal_edit_mode) {
1392 ActionManager::do_action ("MouseMode", "toggle-internal-edit");
1396 button_selection (item, event, item_type);
1398 if (!_drags->active () &&
1399 (Keyboard::is_delete_event (&event->button) ||
1400 Keyboard::is_context_menu_event (&event->button) ||
1401 Keyboard::is_edit_event (&event->button))) {
1403 /* handled by button release */
1407 //not rolling, range mode click + join_play_range : locate the PH here
1408 if ( !_drags->active () && !_session->transport_rolling() && ( effective_mouse_mode() == MouseRange ) && Config->get_always_play_range() ) {
1409 framepos_t where = canvas_event_sample (event);
1411 _session->request_locate (where, false);
1414 switch (event->button.button) {
1416 return button_press_handler_1 (item, event, item_type);
1420 return button_press_handler_2 (item, event, item_type);
1427 return button_press_dispatch (&event->button);
1436 Editor::button_press_dispatch (GdkEventButton* ev)
1438 /* this function is intended only for buttons 4 and above.
1441 Gtkmm2ext::MouseButton b (ev->state, ev->button);
1442 return button_bindings->activate (b, Gtkmm2ext::Bindings::Press);
1446 Editor::button_release_dispatch (GdkEventButton* ev)
1448 /* this function is intended only for buttons 4 and above.
1451 Gtkmm2ext::MouseButton b (ev->state, ev->button);
1452 return button_bindings->activate (b, Gtkmm2ext::Bindings::Release);
1456 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1458 framepos_t where = canvas_event_sample (event);
1459 AutomationTimeAxisView* atv = 0;
1461 if (pre_press_cursor) {
1462 set_canvas_cursor (pre_press_cursor);
1463 pre_press_cursor = 0;
1466 /* no action if we're recording */
1468 if (_session && _session->actively_recording()) {
1472 /* see if we're finishing a drag */
1474 bool were_dragging = false;
1475 if (_drags->active ()) {
1476 bool const r = _drags->end_grab (event);
1478 /* grab dragged, so do nothing else */
1482 were_dragging = true;
1485 update_region_layering_order_editor ();
1487 /* edit events get handled here */
1489 if (!_drags->active () && Keyboard::is_edit_event (&event->button)) {
1490 switch (item_type) {
1492 show_region_properties ();
1495 case TempoMarkerItem: {
1497 TempoMarker* tempo_marker;
1499 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
1500 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
1504 if ((tempo_marker = dynamic_cast<TempoMarker*> (marker)) == 0) {
1505 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
1509 edit_tempo_marker (*tempo_marker);
1513 case MeterMarkerItem: {
1515 MeterMarker* meter_marker;
1517 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
1518 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
1522 if ((meter_marker = dynamic_cast<MeterMarker*> (marker)) == 0) {
1523 fatal << _("programming error: marker for meter is not a meter marker!") << endmsg;
1526 edit_meter_marker (*meter_marker);
1530 case RegionViewName:
1531 if (clicked_regionview->name_active()) {
1532 return mouse_rename_region (item, event);
1536 case ControlPointItem:
1537 edit_control_point (item);
1546 /* context menu events get handled here */
1547 if (Keyboard::is_context_menu_event (&event->button)) {
1549 context_click_event = *event;
1551 if (!_drags->active ()) {
1553 /* no matter which button pops up the context menu, tell the menu
1554 widget to use button 1 to drive menu selection.
1557 switch (item_type) {
1559 case FadeInHandleItem:
1560 case FadeInTrimHandleItem:
1561 case StartCrossFadeItem:
1562 popup_xfade_in_context_menu (1, event->button.time, item, item_type);
1566 case FadeOutHandleItem:
1567 case FadeOutTrimHandleItem:
1568 case EndCrossFadeItem:
1569 popup_xfade_out_context_menu (1, event->button.time, item, item_type);
1573 popup_track_context_menu (1, event->button.time, item_type, false);
1577 case RegionViewNameHighlight:
1578 case LeftFrameHandle:
1579 case RightFrameHandle:
1580 case RegionViewName:
1581 popup_track_context_menu (1, event->button.time, item_type, false);
1585 popup_track_context_menu (1, event->button.time, item_type, true);
1588 case AutomationTrackItem:
1589 popup_track_context_menu (1, event->button.time, item_type, false);
1593 case RangeMarkerBarItem:
1594 case TransportMarkerBarItem:
1595 case CdMarkerBarItem:
1599 popup_ruler_menu (where, item_type);
1603 marker_context_menu (&event->button, item);
1606 case TempoMarkerItem:
1607 tempo_or_meter_marker_context_menu (&event->button, item);
1610 case MeterMarkerItem:
1611 tempo_or_meter_marker_context_menu (&event->button, item);
1614 case CrossfadeViewItem:
1615 popup_track_context_menu (1, event->button.time, item_type, false);
1618 case ControlPointItem:
1619 popup_control_point_context_menu (item, event);
1630 /* delete events get handled here */
1632 Editing::MouseMode const eff = effective_mouse_mode ();
1634 if (!_drags->active () && Keyboard::is_delete_event (&event->button)) {
1636 switch (item_type) {
1637 case TempoMarkerItem:
1638 remove_tempo_marker (item);
1641 case MeterMarkerItem:
1642 remove_meter_marker (item);
1646 remove_marker (*item, event);
1650 if (eff == MouseObject) {
1651 remove_clicked_region ();
1655 case ControlPointItem:
1656 remove_control_point (item);
1660 remove_midi_note (item, event);
1669 switch (event->button.button) {
1672 switch (item_type) {
1673 /* see comments in button_press_handler */
1674 case PlayheadCursorItem:
1677 case AutomationLineItem:
1678 case StartSelectionTrimItem:
1679 case EndSelectionTrimItem:
1683 if (!_dragging_playhead) {
1684 snap_to_with_modifier (where, event, 0, true);
1685 mouse_add_new_marker (where);
1689 case CdMarkerBarItem:
1690 if (!_dragging_playhead) {
1691 // if we get here then a dragged range wasn't done
1692 snap_to_with_modifier (where, event, 0, true);
1693 mouse_add_new_marker (where, true);
1698 if (!_dragging_playhead) {
1699 snap_to_with_modifier (where, event);
1700 mouse_add_new_tempo_event (where);
1705 if (!_dragging_playhead) {
1706 mouse_add_new_meter_event (pixel_to_sample (event->button.x));
1717 switch (item_type) {
1718 case AutomationTrackItem:
1719 atv = dynamic_cast<AutomationTimeAxisView*>(clicked_axisview);
1721 bool with_guard_points = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
1722 atv->add_automation_event (event, where, event->button.y, with_guard_points);
1732 switch (item_type) {
1735 /* check that we didn't drag before releasing, since
1736 its really annoying to create new control
1737 points when doing this.
1739 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (clicked_regionview);
1740 if (!were_dragging && arv) {
1741 bool with_guard_points = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
1742 arv->add_gain_point_event (item, event, with_guard_points);
1748 case AutomationTrackItem: {
1749 bool with_guard_points = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
1750 dynamic_cast<AutomationTimeAxisView*>(clicked_axisview)->
1751 add_automation_event (event, where, event->button.y, with_guard_points);
1761 set_canvas_cursor (current_canvas_cursor);
1762 if (scrubbing_direction == 0) {
1763 /* no drag, just a click */
1764 switch (item_type) {
1766 play_selected_region ();
1772 /* make sure we stop */
1773 _session->request_transport_speed (0.0);
1782 /* do any (de)selection operations that should occur on button release */
1783 button_selection (item, event, item_type);
1792 switch (item_type) {
1794 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1796 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier|Keyboard::SecondaryModifier))) {
1799 // Button2 click is unused
1814 // x_style_paste (where, 1.0);
1835 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1842 switch (item_type) {
1843 case ControlPointItem:
1844 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1845 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1850 at_y = cp->get_y ();
1851 cp->i2w (at_x, at_y);
1855 fraction = 1.0 - (cp->get_y() / cp->line().height());
1857 if (is_drawable() && !_drags->active ()) {
1858 set_canvas_cursor (_cursors->fader);
1861 _verbose_cursor->set (cp->line().get_verbose_cursor_string (fraction), at_x, at_y);
1862 _verbose_cursor->show ();
1867 if (mouse_mode == MouseGain) {
1868 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1870 line->set_outline_color (ARDOUR_UI::config()->get_canvasvar_EnteredGainLine());
1872 if (is_drawable()) {
1873 set_canvas_cursor (_cursors->fader);
1878 case AutomationLineItem:
1879 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1880 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1882 line->set_outline_color (ARDOUR_UI::config()->get_canvasvar_EnteredAutomationLine());
1884 if (is_drawable()) {
1885 set_canvas_cursor (_cursors->fader);
1890 case RegionViewNameHighlight:
1891 if (is_drawable() && effective_mouse_mode() == MouseObject && entered_regionview) {
1892 set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1893 _over_region_trim_target = true;
1897 case LeftFrameHandle:
1898 case RightFrameHandle:
1899 if (is_drawable() && effective_mouse_mode() == MouseObject && !internal_editing() && entered_regionview) {
1900 set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1905 switch (effective_mouse_mode()) {
1907 set_canvas_cursor (_cursors->selector);
1910 set_canvas_cursor (which_grabber_cursor());
1915 case StartSelectionTrimItem:
1916 if (is_drawable()) {
1917 set_canvas_cursor (_cursors->left_side_trim);
1920 case EndSelectionTrimItem:
1921 if (is_drawable()) {
1922 set_canvas_cursor (_cursors->right_side_trim);
1926 case PlayheadCursorItem:
1927 if (is_drawable()) {
1928 switch (_edit_point) {
1930 set_canvas_cursor (_cursors->grabber_edit_point);
1933 set_canvas_cursor (_cursors->grabber);
1940 case RegionViewName:
1942 /* when the name is not an active item, the entire name highlight is for trimming */
1944 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1945 if (mouse_mode == MouseObject && is_drawable()) {
1946 set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1947 _over_region_trim_target = true;
1953 case AutomationTrackItem:
1954 if (is_drawable()) {
1955 Gdk::Cursor *cursor;
1956 switch (mouse_mode) {
1958 cursor = _cursors->selector;
1961 cursor = _cursors->zoom_in;
1964 cursor = _cursors->cross_hair;
1968 set_canvas_cursor (cursor);
1970 AutomationTimeAxisView* atv;
1971 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1972 clear_entered_track = false;
1973 set_entered_track (atv);
1979 case RangeMarkerBarItem:
1980 case TransportMarkerBarItem:
1981 case CdMarkerBarItem:
1984 if (is_drawable()) {
1985 set_canvas_cursor (_cursors->timebar);
1990 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1993 entered_marker = marker;
1994 marker->set_color_rgba (ARDOUR_UI::config()->get_canvasvar_EnteredMarker());
1996 case MeterMarkerItem:
1997 case TempoMarkerItem:
1998 if (is_drawable()) {
1999 set_canvas_cursor (_cursors->timebar);
2003 case FadeInHandleItem:
2004 case FadeInTrimHandleItem:
2005 if (mouse_mode == MouseObject && !internal_editing()) {
2006 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
2008 RegionView* rv = static_cast<RegionView*>(item->get_data ("regionview"));
2009 rect->set_fill_color (rv->get_fill_color());
2010 set_canvas_cursor (_cursors->fade_in);
2015 case FadeOutHandleItem:
2016 case FadeOutTrimHandleItem:
2017 if (mouse_mode == MouseObject && !internal_editing()) {
2018 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
2020 RegionView* rv = static_cast<RegionView*>(item->get_data ("regionview"));
2021 rect->set_fill_color (rv->get_fill_color ());
2022 set_canvas_cursor (_cursors->fade_out);
2027 case FeatureLineItem:
2029 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
2030 line->set_outline_color (0xFF0000FF);
2035 if ( get_smart_mode() ) {
2036 set_canvas_cursor ();
2044 /* second pass to handle entered track status in a comprehensible way.
2047 switch (item_type) {
2049 case AutomationLineItem:
2050 case ControlPointItem:
2051 /* these do not affect the current entered track state */
2052 clear_entered_track = false;
2055 case AutomationTrackItem:
2056 /* handled above already */
2060 set_entered_track (0);
2068 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent*, ItemType item_type)
2076 switch (item_type) {
2077 case ControlPointItem:
2078 if (is_drawable()) {
2079 set_canvas_cursor (current_canvas_cursor);
2082 _verbose_cursor->hide ();
2085 case RegionViewNameHighlight:
2086 case LeftFrameHandle:
2087 case RightFrameHandle:
2088 case StartSelectionTrimItem:
2089 case EndSelectionTrimItem:
2090 case PlayheadCursorItem:
2092 _over_region_trim_target = false;
2094 if (is_drawable()) {
2095 set_canvas_cursor (current_canvas_cursor);
2100 case AutomationLineItem:
2101 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
2103 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
2105 line->set_outline_color (al->get_line_color());
2108 if (is_drawable()) {
2109 set_canvas_cursor (current_canvas_cursor);
2113 case RegionViewName:
2114 /* see enter_handler() for notes */
2115 _over_region_trim_target = false;
2117 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
2118 if (is_drawable() && mouse_mode == MouseObject) {
2119 set_canvas_cursor (current_canvas_cursor);
2124 case RangeMarkerBarItem:
2125 case TransportMarkerBarItem:
2126 case CdMarkerBarItem:
2130 if (is_drawable()) {
2131 set_canvas_cursor (current_canvas_cursor);
2136 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2140 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
2141 location_flags_changed (loc, this);
2144 case MeterMarkerItem:
2145 case TempoMarkerItem:
2147 if (is_drawable()) {
2148 set_canvas_cursor (current_canvas_cursor);
2153 case FadeInTrimHandleItem:
2154 case FadeOutTrimHandleItem:
2155 case FadeInHandleItem:
2156 case FadeOutHandleItem:
2158 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
2160 rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_InactiveFadeHandle());
2163 set_canvas_cursor (current_canvas_cursor);
2166 case AutomationTrackItem:
2167 if (is_drawable()) {
2168 set_canvas_cursor (current_canvas_cursor);
2169 clear_entered_track = true;
2170 Glib::signal_idle().connect (sigc::mem_fun(*this, &Editor::left_automation_track));
2173 case FeatureLineItem:
2175 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
2176 line->set_outline_color (ARDOUR_UI::config()->get_canvasvar_ZeroLine());
2188 Editor::left_automation_track ()
2190 if (clear_entered_track) {
2191 set_entered_track (0);
2192 clear_entered_track = false;
2198 Editor::scrub (framepos_t frame, double current_x)
2202 if (scrubbing_direction == 0) {
2204 _session->request_locate (frame, false);
2205 _session->request_transport_speed (0.1);
2206 scrubbing_direction = 1;
2210 if (last_scrub_x > current_x) {
2212 /* pointer moved to the left */
2214 if (scrubbing_direction > 0) {
2216 /* we reversed direction to go backwards */
2219 scrub_reverse_distance += (int) (last_scrub_x - current_x);
2223 /* still moving to the left (backwards) */
2225 scrub_reversals = 0;
2226 scrub_reverse_distance = 0;
2228 delta = 0.01 * (last_scrub_x - current_x);
2229 _session->request_transport_speed_nonzero (_session->transport_speed() - delta);
2233 /* pointer moved to the right */
2235 if (scrubbing_direction < 0) {
2236 /* we reversed direction to go forward */
2239 scrub_reverse_distance += (int) (current_x - last_scrub_x);
2242 /* still moving to the right */
2244 scrub_reversals = 0;
2245 scrub_reverse_distance = 0;
2247 delta = 0.01 * (current_x - last_scrub_x);
2248 _session->request_transport_speed_nonzero (_session->transport_speed() + delta);
2252 /* if there have been more than 2 opposite motion moves detected, or one that moves
2253 back more than 10 pixels, reverse direction
2256 if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
2258 if (scrubbing_direction > 0) {
2259 /* was forwards, go backwards */
2260 _session->request_transport_speed (-0.1);
2261 scrubbing_direction = -1;
2263 /* was backwards, go forwards */
2264 _session->request_transport_speed (0.1);
2265 scrubbing_direction = 1;
2268 scrub_reverse_distance = 0;
2269 scrub_reversals = 0;
2273 last_scrub_x = current_x;
2277 Editor::motion_handler (ArdourCanvas::Item* /*item*/, GdkEvent* event, bool from_autoscroll)
2279 _last_motion_y = event->motion.y;
2281 if (event->motion.is_hint) {
2284 /* We call this so that MOTION_NOTIFY events continue to be
2285 delivered to the canvas. We need to do this because we set
2286 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
2287 the density of the events, at the expense of a round-trip
2288 to the server. Given that this will mostly occur on cases
2289 where DISPLAY = :0.0, and given the cost of what the motion
2290 event might do, its a good tradeoff.
2293 _track_canvas->get_pointer (x, y);
2296 if (current_stepping_trackview) {
2297 /* don't keep the persistent stepped trackview if the mouse moves */
2298 current_stepping_trackview = 0;
2299 step_timeout.disconnect ();
2302 if (_session && _session->actively_recording()) {
2303 /* Sorry. no dragging stuff around while we record */
2307 JoinObjectRangeState const old = _join_object_range_state;
2308 update_join_object_range_location (event->motion.x, event->motion.y);
2310 if (!_internal_editing && _join_object_range_state != old) {
2311 set_canvas_cursor ();
2314 if (!_internal_editing && _over_region_trim_target) {
2315 set_canvas_cursor_for_region_view (event->motion.x, entered_regionview);
2318 bool handled = false;
2319 if (_drags->active ()) {
2320 handled = _drags->motion_handler (event, from_autoscroll);
2327 track_canvas_motion (event);
2332 Editor::can_remove_control_point (ArdourCanvas::Item* 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 AutomationLine& line = control_point->line ();
2342 if (dynamic_cast<AudioRegionGainLine*> (&line)) {
2343 /* we shouldn't remove the first or last gain point in region gain lines */
2344 if (line.is_last_point(*control_point) || line.is_first_point(*control_point)) {
2353 Editor::remove_control_point (ArdourCanvas::Item* item)
2355 if (!can_remove_control_point (item)) {
2359 ControlPoint* control_point;
2361 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2362 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2366 control_point->line().remove_point (*control_point);
2370 Editor::edit_control_point (ArdourCanvas::Item* item)
2372 ControlPoint* p = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"));
2375 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2379 ControlPointDialog d (p);
2382 if (d.run () != RESPONSE_ACCEPT) {
2386 p->line().modify_point_y (*p, d.get_y_fraction ());
2390 Editor::edit_notes (TimeAxisViewItem& tavi)
2392 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(&tavi);
2398 MidiRegionView::Selection const & s = mrv->selection();
2404 EditNoteDialog* d = new EditNoteDialog (&(*s.begin())->region_view(), s);
2408 d->signal_response().connect (sigc::bind (sigc::mem_fun (*this, &Editor::note_edit_done), d));
2412 Editor::note_edit_done (int r, EditNoteDialog* d)
2419 Editor::visible_order_range (int* low, int* high) const
2421 *low = TimeAxisView::max_order ();
2424 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
2426 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
2428 if (!rtv->hidden()) {
2430 if (*high < rtv->order()) {
2431 *high = rtv->order ();
2434 if (*low > rtv->order()) {
2435 *low = rtv->order ();
2442 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
2444 /* Either add to or set the set the region selection, unless
2445 this is an alignment click (control used)
2448 if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
2449 TimeAxisView* tv = &rv.get_time_axis_view();
2450 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
2452 if (rtv && rtv->is_track()) {
2453 speed = rtv->track()->speed();
2456 framepos_t where = get_preferred_edit_position();
2460 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
2462 align_region (rv.region(), SyncPoint, (framepos_t) (where * speed));
2464 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
2466 align_region (rv.region(), End, (framepos_t) (where * speed));
2470 align_region (rv.region(), Start, (framepos_t) (where * speed));
2477 Editor::collect_new_region_view (RegionView* rv)
2479 latest_regionviews.push_back (rv);
2483 Editor::collect_and_select_new_region_view (RegionView* rv)
2486 latest_regionviews.push_back (rv);
2490 Editor::cancel_selection ()
2492 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2493 (*i)->hide_selection ();
2496 selection->clear ();
2497 clicked_selection = 0;
2501 Editor::cancel_time_selection ()
2503 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2504 (*i)->hide_selection ();
2506 selection->time.clear ();
2507 clicked_selection = 0;
2511 Editor::point_trim (GdkEvent* event, framepos_t new_bound)
2513 RegionView* rv = clicked_regionview;
2515 /* Choose action dependant on which button was pressed */
2516 switch (event->button.button) {
2518 begin_reversible_command (_("start point trim"));
2520 if (selection->selected (rv)) {
2521 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
2522 i != selection->regions.by_layer().end(); ++i)
2524 if (!(*i)->region()->locked()) {
2525 (*i)->region()->clear_changes ();
2526 (*i)->region()->trim_front (new_bound);
2527 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2532 if (!rv->region()->locked()) {
2533 rv->region()->clear_changes ();
2534 rv->region()->trim_front (new_bound);
2535 _session->add_command(new StatefulDiffCommand (rv->region()));
2539 commit_reversible_command();
2543 begin_reversible_command (_("End point trim"));
2545 if (selection->selected (rv)) {
2547 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
2549 if (!(*i)->region()->locked()) {
2550 (*i)->region()->clear_changes();
2551 (*i)->region()->trim_end (new_bound);
2552 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2558 if (!rv->region()->locked()) {
2559 rv->region()->clear_changes ();
2560 rv->region()->trim_end (new_bound);
2561 _session->add_command (new StatefulDiffCommand (rv->region()));
2565 commit_reversible_command();
2574 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* /*event*/)
2579 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2580 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2584 Location* location = find_location_from_marker (marker, is_start);
2585 location->set_hidden (true, this);
2590 Editor::reposition_zoom_rect (framepos_t start, framepos_t end)
2592 double x1 = sample_to_pixel (start);
2593 double x2 = sample_to_pixel (end);
2594 double y2 = _full_canvas_height - 1.0;
2596 zoom_rect->set (ArdourCanvas::Rect (x1, 1.0, x2, y2));
2601 Editor::mouse_rename_region (ArdourCanvas::Item* /*item*/, GdkEvent* /*event*/)
2603 using namespace Gtkmm2ext;
2605 ArdourPrompter prompter (false);
2607 prompter.set_prompt (_("Name for region:"));
2608 prompter.set_initial_text (clicked_regionview->region()->name());
2609 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
2610 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
2611 prompter.show_all ();
2612 switch (prompter.run ()) {
2613 case Gtk::RESPONSE_ACCEPT:
2615 prompter.get_result(str);
2617 clicked_regionview->region()->set_name (str);
2626 Editor::mouse_brush_insert_region (RegionView* rv, framepos_t pos)
2628 /* no brushing without a useful snap setting */
2630 switch (_snap_mode) {
2632 return; /* can't work because it allows region to be placed anywhere */
2637 switch (_snap_type) {
2645 /* don't brush a copy over the original */
2647 if (pos == rv->region()->position()) {
2651 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
2653 if (rtv == 0 || !rtv->is_track()) {
2657 boost::shared_ptr<Playlist> playlist = rtv->playlist();
2658 double speed = rtv->track()->speed();
2660 playlist->clear_changes ();
2661 boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region(), true));
2662 playlist->add_region (new_region, (framepos_t) (pos * speed));
2663 _session->add_command (new StatefulDiffCommand (playlist));
2665 // playlist is frozen, so we have to update manually XXX this is disgusting
2667 playlist->RegionAdded (new_region); /* EMIT SIGNAL */
2671 Editor::track_height_step_timeout ()
2673 if (get_microseconds() - last_track_height_step_timestamp < 250000) {
2674 current_stepping_trackview = 0;
2681 Editor::add_region_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2683 assert (region_view);
2685 if (!region_view->region()->playlist()) {
2689 if (Config->get_edit_mode() == Splice) {
2690 _drags->add (new RegionSpliceDrag (this, item, region_view, selection->regions.by_layer()));
2692 _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), false, false));
2697 Editor::add_region_copy_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2699 assert (region_view);
2701 if (!region_view->region()->playlist()) {
2705 _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), false, true));
2709 Editor::add_region_brush_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2711 assert (region_view);
2713 if (!region_view->region()->playlist()) {
2717 if (Config->get_edit_mode() == Splice) {
2721 _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), true, false));
2723 begin_reversible_command (Operations::drag_region_brush);
2726 /** Start a grab where a time range is selected, track(s) are selected, and the
2727 * user clicks and drags a region with a modifier in order to create a new region containing
2728 * the section of the clicked region that lies within the time range.
2731 Editor::start_selection_grab (ArdourCanvas::Item* /*item*/, GdkEvent* event)
2733 if (clicked_regionview == 0) {
2737 /* lets try to create new Region for the selection */
2739 vector<boost::shared_ptr<Region> > new_regions;
2740 create_region_from_selection (new_regions);
2742 if (new_regions.empty()) {
2746 /* XXX fix me one day to use all new regions */
2748 boost::shared_ptr<Region> region (new_regions.front());
2750 /* add it to the current stream/playlist.
2752 tricky: the streamview for the track will add a new regionview. we will
2753 catch the signal it sends when it creates the regionview to
2754 set the regionview we want to then drag.
2757 latest_regionviews.clear();
2758 sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
2760 /* A selection grab currently creates two undo/redo operations, one for
2761 creating the new region and another for moving it.
2764 begin_reversible_command (Operations::selection_grab);
2766 boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
2768 playlist->clear_changes ();
2769 clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
2770 _session->add_command(new StatefulDiffCommand (playlist));
2772 commit_reversible_command ();
2776 if (latest_regionviews.empty()) {
2777 /* something went wrong */
2781 /* we need to deselect all other regionviews, and select this one
2782 i'm ignoring undo stuff, because the region creation will take care of it
2784 selection->set (latest_regionviews);
2786 _drags->set (new RegionMoveDrag (this, latest_regionviews.front()->get_canvas_group(), latest_regionviews.front(), latest_regionviews, false, false), event);
2792 if (_drags->active ()) {
2795 selection->clear ();
2800 Editor::set_internal_edit (bool yn)
2802 if (_internal_editing == yn) {
2806 _internal_editing = yn;
2809 pre_internal_mouse_mode = mouse_mode;
2810 pre_internal_snap_type = _snap_type;
2811 pre_internal_snap_mode = _snap_mode;
2813 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2814 (*i)->enter_internal_edit_mode ();
2817 set_snap_to (internal_snap_type);
2818 set_snap_mode (internal_snap_mode);
2822 internal_snap_mode = _snap_mode;
2823 internal_snap_type = _snap_type;
2825 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2826 (*i)->leave_internal_edit_mode ();
2829 if (mouse_mode == MouseDraw && pre_internal_mouse_mode != MouseDraw) {
2830 /* we were drawing .. flip back to something sensible */
2831 set_mouse_mode (pre_internal_mouse_mode);
2834 set_snap_to (pre_internal_snap_type);
2835 set_snap_mode (pre_internal_snap_mode);
2838 set_canvas_cursor ();
2841 /** Update _join_object_range_state which indicate whether we are over the top or bottom half of a region view,
2842 * used by the `join object/range' tool mode. Coordinates in canvas space.
2845 Editor::update_join_object_range_location (double /*x*/, double y)
2847 /* XXX: actually, this decides based on whether the mouse is in the top
2848 or bottom half of a the waveform part RouteTimeAxisView;
2850 Note that entered_{track,regionview} is not always setup (e.g. if
2851 the mouse is over a TimeSelection), and to get a Region
2852 that we're over requires searching the playlist.
2855 if ( !get_smart_mode() ) {
2856 _join_object_range_state = JOIN_OBJECT_RANGE_NONE;
2860 if (mouse_mode == MouseObject) {
2861 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2862 } else if (mouse_mode == MouseRange) {
2863 _join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
2866 /* XXX: maybe we should make entered_track work in all cases, rather than resorting to this */
2867 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y, false);
2871 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
2876 rtv->canvas_display()->canvas_to_item (cx, cy);
2878 double const c = cy / (rtv->view()->child_height() - TimeAxisViewItem::NAME_HIGHLIGHT_SIZE);
2880 _join_object_range_state = c <= 0.5 ? JOIN_OBJECT_RANGE_RANGE : JOIN_OBJECT_RANGE_OBJECT;
2886 Editor::effective_mouse_mode () const
2888 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
2890 } else if (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE) {
2898 Editor::remove_midi_note (ArdourCanvas::Item* item, GdkEvent *)
2900 NoteBase* e = reinterpret_cast<NoteBase*> (item->get_data ("notebase"));
2903 e->region_view().delete_note (e->note ());
2907 Editor::set_canvas_cursor_for_region_view (double x, RegionView* rv)
2909 /* XXX: this check should not be necessary */
2914 ArdourCanvas::Group* g = rv->get_canvas_group ();
2915 ArdourCanvas::Group* p = g->parent ();
2917 /* Compute x in region view parent coordinates */
2919 p->canvas_to_item (x, dy);
2921 boost::optional<ArdourCanvas::Rect> item_bbox = g->bounding_box ();
2923 ArdourCanvas::Rect parent_bbox = g->item_to_parent (item_bbox.get ());
2925 /* First or last 10% of region is used for trimming, if the whole
2926 region is wider than 20 pixels at the current zoom level.
2929 double const w = parent_bbox.width();
2931 if (w > 20.0 && x >= parent_bbox.x0 && x < parent_bbox.x1) {
2933 Trimmable::CanTrim ct = rv->region()->can_trim ();
2935 if (((x - parent_bbox.x0) / w) < 0.10) {
2936 if (ct & Trimmable::FrontTrimEarlier) {
2937 set_canvas_cursor (_cursors->left_side_trim, true);
2939 set_canvas_cursor (_cursors->left_side_trim_right_only, true);
2941 } else if (((parent_bbox.x1 - x) / w) < 0.10) {
2942 if (ct & Trimmable::EndTrimLater) {
2943 set_canvas_cursor (_cursors->right_side_trim, true);
2945 set_canvas_cursor (_cursors->right_side_trim_left_only, true);
2951 /** Obtain the pointer position in canvas coordinates */
2953 Editor::get_pointer_position (double& x, double& y) const
2956 _track_canvas->get_pointer (px, py);
2957 _track_canvas->window_to_canvas (px, py, x, y);