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 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer()), event);
1076 case ControlPointItem:
1077 _drags->set (new ControlPointDrag (this, item), event);
1081 case AutomationLineItem:
1082 _drags->set (new LineDrag (this, item), event);
1087 if (internal_editing()) {
1088 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
1089 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
1093 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
1097 case AutomationTrackItem:
1099 TimeAxisView* parent = clicked_axisview->get_parent ();
1100 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (clicked_axisview);
1102 if (parent && dynamic_cast<MidiTimeAxisView*> (parent) && atv->show_regions ()) {
1104 RouteTimeAxisView* p = dynamic_cast<RouteTimeAxisView*> (parent);
1106 boost::shared_ptr<Playlist> pl = p->track()->playlist ();
1107 if (pl->n_regions() == 0) {
1108 /* Parent has no regions; create one so that we have somewhere to put automation */
1109 _drags->set (new RegionCreateDrag (this, item, parent), event);
1111 /* See if there's a region before the click that we can extend, and extend it if so */
1112 framepos_t const t = canvas_event_sample (event);
1113 boost::shared_ptr<Region> prev = pl->find_next_region (t, End, -1);
1115 _drags->set (new RegionCreateDrag (this, item, parent), event);
1117 prev->set_length (t - prev->position ());
1121 /* rubberband drag to select automation points */
1122 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
1129 if ( get_smart_mode() ) {
1130 /* we're in "smart" joined mode, and we've clicked on a Selection */
1131 double const y = event->button.y;
1132 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
1134 /* if we're over an automation track, start a drag of its data */
1135 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
1137 _drags->set (new AutomationRangeDrag (this, atv, selection->time), event, _cursors->up_down);
1140 /* if we're over a track and a region, and in the `object' part of a region,
1141 put a selection around the region and drag both
1143 /* RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1144 if (rtv && _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
1145 boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track> (rtv->route ());
1147 boost::shared_ptr<Playlist> pl = t->playlist ();
1150 boost::shared_ptr<Region> r = pl->top_region_at (canvas_event_sample (event));
1152 RegionView* rv = rtv->view()->find_view (r);
1153 clicked_selection = select_range (rv->region()->position(),
1154 rv->region()->last_frame()+1);
1155 _drags->add (new SelectionDrag (this, item, SelectionDrag::SelectionMove));
1156 list<RegionView*> rvs;
1158 _drags->add (new RegionMoveDrag (this, item, rv, rvs, false, false));
1159 _drags->start_grab (event);
1183 switch (item_type) {
1185 _drags->set (new LineDrag (this, item), event);
1188 case ControlPointItem:
1189 _drags->set (new ControlPointDrag (this, item), event);
1195 AudioRegionView* arv = dynamic_cast<AudioRegionView *> (clicked_regionview);
1197 _drags->set (new AutomationRangeDrag (this, arv, selection->time), event, _cursors->up_down);
1198 _drags->start_grab (event);
1204 case AutomationLineItem:
1205 _drags->set (new LineDrag (this, item), event);
1215 if (event->type == GDK_BUTTON_PRESS) {
1216 _drags->set (new MouseZoomDrag (this, item), event);
1223 if (internal_editing() && item_type == NoteItem ) {
1224 /* drag notes if we're in internal edit mode */
1225 NoteBase* cn = reinterpret_cast<NoteBase*>(item->get_data ("notebase"));
1227 if (cn->big_enough_to_trim()) {
1228 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
1231 } else if (clicked_regionview) {
1233 _drags->set (new TimeFXDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1239 _drags->set (new ScrubDrag (this, item), event);
1240 scrub_reversals = 0;
1241 scrub_reverse_distance = 0;
1242 last_scrub_x = event->button.x;
1243 scrubbing_direction = 0;
1244 set_canvas_cursor (_cursors->transparent);
1256 Editor::button_press_handler_2 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1258 Editing::MouseMode const eff = effective_mouse_mode ();
1261 switch (item_type) {
1263 if (internal_editing ()) {
1264 /* no region drags in internal edit mode */
1268 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
1269 add_region_copy_drag (item, event, clicked_regionview);
1271 add_region_drag (item, event, clicked_regionview);
1273 _drags->start_grab (event);
1276 case ControlPointItem:
1277 _drags->set (new ControlPointDrag (this, item), event);
1285 switch (item_type) {
1286 case RegionViewNameHighlight:
1287 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1291 case LeftFrameHandle:
1292 case RightFrameHandle:
1293 if (!internal_editing ()) {
1294 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1299 case RegionViewName:
1300 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer()), event);
1314 /* relax till release */
1320 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1321 temporal_zoom_to_frame (false, canvas_event_sample (event));
1323 temporal_zoom_to_frame (true, canvas_event_sample(event));
1336 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1338 if (event->type == GDK_2BUTTON_PRESS) {
1339 _drags->mark_double_click ();
1340 gdk_pointer_ungrab (GDK_CURRENT_TIME);
1344 if (event->type != GDK_BUTTON_PRESS) {
1348 pre_press_cursor = current_canvas_cursor;
1350 _track_canvas->grab_focus();
1352 if (_session && _session->actively_recording()) {
1356 if (internal_editing()) {
1357 bool leave_internal_edit_mode = false;
1359 switch (item_type) {
1364 if (!dynamic_cast<MidiRegionView*> (clicked_regionview) && !dynamic_cast<AutomationRegionView*> (clicked_regionview)) {
1365 leave_internal_edit_mode = true;
1369 case PlayheadCursorItem:
1371 case TempoMarkerItem:
1372 case MeterMarkerItem:
1376 case RangeMarkerBarItem:
1377 case CdMarkerBarItem:
1378 case TransportMarkerBarItem:
1380 /* button press on these events never does anything to
1381 change the editing mode.
1389 if (leave_internal_edit_mode) {
1390 ActionManager::do_action ("MouseMode", "toggle-internal-edit");
1394 button_selection (item, event, item_type);
1396 if (!_drags->active () &&
1397 (Keyboard::is_delete_event (&event->button) ||
1398 Keyboard::is_context_menu_event (&event->button) ||
1399 Keyboard::is_edit_event (&event->button))) {
1401 /* handled by button release */
1405 //not rolling, range mode click + join_play_range : locate the PH here
1406 if ( !_drags->active () && !_session->transport_rolling() && ( effective_mouse_mode() == MouseRange ) && Config->get_always_play_range() ) {
1407 framepos_t where = canvas_event_sample (event);
1409 _session->request_locate (where, false);
1412 switch (event->button.button) {
1414 return button_press_handler_1 (item, event, item_type);
1418 return button_press_handler_2 (item, event, item_type);
1425 return button_press_dispatch (&event->button);
1434 Editor::button_press_dispatch (GdkEventButton* ev)
1436 /* this function is intended only for buttons 4 and above.
1439 Gtkmm2ext::MouseButton b (ev->state, ev->button);
1440 return button_bindings->activate (b, Gtkmm2ext::Bindings::Press);
1444 Editor::button_release_dispatch (GdkEventButton* ev)
1446 /* this function is intended only for buttons 4 and above.
1449 Gtkmm2ext::MouseButton b (ev->state, ev->button);
1450 return button_bindings->activate (b, Gtkmm2ext::Bindings::Release);
1454 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1456 framepos_t where = canvas_event_sample (event);
1457 AutomationTimeAxisView* atv = 0;
1459 if (pre_press_cursor) {
1460 set_canvas_cursor (pre_press_cursor);
1461 pre_press_cursor = 0;
1464 /* no action if we're recording */
1466 if (_session && _session->actively_recording()) {
1470 /* see if we're finishing a drag */
1472 bool were_dragging = false;
1473 if (_drags->active ()) {
1474 bool const r = _drags->end_grab (event);
1476 /* grab dragged, so do nothing else */
1480 were_dragging = true;
1483 update_region_layering_order_editor ();
1485 /* edit events get handled here */
1487 if (!_drags->active () && Keyboard::is_edit_event (&event->button)) {
1488 switch (item_type) {
1490 show_region_properties ();
1493 case TempoMarkerItem: {
1495 TempoMarker* tempo_marker;
1497 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
1498 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
1502 if ((tempo_marker = dynamic_cast<TempoMarker*> (marker)) == 0) {
1503 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
1507 edit_tempo_marker (*tempo_marker);
1511 case MeterMarkerItem: {
1513 MeterMarker* meter_marker;
1515 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
1516 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
1520 if ((meter_marker = dynamic_cast<MeterMarker*> (marker)) == 0) {
1521 fatal << _("programming error: marker for meter is not a meter marker!") << endmsg;
1524 edit_meter_marker (*meter_marker);
1528 case RegionViewName:
1529 if (clicked_regionview->name_active()) {
1530 return mouse_rename_region (item, event);
1534 case ControlPointItem:
1535 edit_control_point (item);
1544 /* context menu events get handled here */
1545 if (Keyboard::is_context_menu_event (&event->button)) {
1547 context_click_event = *event;
1549 if (!_drags->active ()) {
1551 /* no matter which button pops up the context menu, tell the menu
1552 widget to use button 1 to drive menu selection.
1555 switch (item_type) {
1557 case FadeInHandleItem:
1558 case FadeInTrimHandleItem:
1559 case StartCrossFadeItem:
1560 popup_xfade_in_context_menu (1, event->button.time, item, item_type);
1564 case FadeOutHandleItem:
1565 case FadeOutTrimHandleItem:
1566 case EndCrossFadeItem:
1567 popup_xfade_out_context_menu (1, event->button.time, item, item_type);
1571 popup_track_context_menu (1, event->button.time, item_type, false);
1575 case RegionViewNameHighlight:
1576 case LeftFrameHandle:
1577 case RightFrameHandle:
1578 case RegionViewName:
1579 popup_track_context_menu (1, event->button.time, item_type, false);
1583 popup_track_context_menu (1, event->button.time, item_type, true);
1586 case AutomationTrackItem:
1587 popup_track_context_menu (1, event->button.time, item_type, false);
1591 case RangeMarkerBarItem:
1592 case TransportMarkerBarItem:
1593 case CdMarkerBarItem:
1597 popup_ruler_menu (where, item_type);
1601 marker_context_menu (&event->button, item);
1604 case TempoMarkerItem:
1605 tempo_or_meter_marker_context_menu (&event->button, item);
1608 case MeterMarkerItem:
1609 tempo_or_meter_marker_context_menu (&event->button, item);
1612 case CrossfadeViewItem:
1613 popup_track_context_menu (1, event->button.time, item_type, false);
1616 case ControlPointItem:
1617 popup_control_point_context_menu (item, event);
1628 /* delete events get handled here */
1630 Editing::MouseMode const eff = effective_mouse_mode ();
1632 if (!_drags->active () && Keyboard::is_delete_event (&event->button)) {
1634 switch (item_type) {
1635 case TempoMarkerItem:
1636 remove_tempo_marker (item);
1639 case MeterMarkerItem:
1640 remove_meter_marker (item);
1644 remove_marker (*item, event);
1648 if (eff == MouseObject) {
1649 remove_clicked_region ();
1653 case ControlPointItem:
1654 remove_control_point (item);
1658 remove_midi_note (item, event);
1667 switch (event->button.button) {
1670 switch (item_type) {
1671 /* see comments in button_press_handler */
1672 case PlayheadCursorItem:
1675 case AutomationLineItem:
1676 case StartSelectionTrimItem:
1677 case EndSelectionTrimItem:
1681 if (!_dragging_playhead) {
1682 snap_to_with_modifier (where, event, 0, true);
1683 mouse_add_new_marker (where);
1687 case CdMarkerBarItem:
1688 if (!_dragging_playhead) {
1689 // if we get here then a dragged range wasn't done
1690 snap_to_with_modifier (where, event, 0, true);
1691 mouse_add_new_marker (where, true);
1696 if (!_dragging_playhead) {
1697 snap_to_with_modifier (where, event);
1698 mouse_add_new_tempo_event (where);
1703 if (!_dragging_playhead) {
1704 mouse_add_new_meter_event (pixel_to_sample (event->button.x));
1715 switch (item_type) {
1716 case AutomationTrackItem:
1717 atv = dynamic_cast<AutomationTimeAxisView*>(clicked_axisview);
1719 bool with_guard_points = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
1720 atv->add_automation_event (event, where, event->button.y, with_guard_points);
1730 switch (item_type) {
1733 /* check that we didn't drag before releasing, since
1734 its really annoying to create new control
1735 points when doing this.
1737 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (clicked_regionview);
1738 if (!were_dragging && arv) {
1739 bool with_guard_points = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
1740 arv->add_gain_point_event (item, event, with_guard_points);
1746 case AutomationTrackItem: {
1747 bool with_guard_points = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
1748 dynamic_cast<AutomationTimeAxisView*>(clicked_axisview)->
1749 add_automation_event (event, where, event->button.y, with_guard_points);
1759 set_canvas_cursor (current_canvas_cursor);
1760 if (scrubbing_direction == 0) {
1761 /* no drag, just a click */
1762 switch (item_type) {
1764 play_selected_region ();
1770 /* make sure we stop */
1771 _session->request_transport_speed (0.0);
1780 /* do any (de)selection operations that should occur on button release */
1781 button_selection (item, event, item_type);
1790 switch (item_type) {
1792 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1794 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier|Keyboard::SecondaryModifier))) {
1797 // Button2 click is unused
1812 // x_style_paste (where, 1.0);
1833 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1840 switch (item_type) {
1841 case ControlPointItem:
1842 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1843 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1848 at_y = cp->get_y ();
1849 cp->i2w (at_x, at_y);
1853 fraction = 1.0 - (cp->get_y() / cp->line().height());
1855 if (is_drawable() && !_drags->active ()) {
1856 set_canvas_cursor (_cursors->fader);
1859 _verbose_cursor->set (cp->line().get_verbose_cursor_string (fraction), at_x, at_y);
1860 _verbose_cursor->show ();
1865 if (mouse_mode == MouseGain) {
1866 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1868 line->set_outline_color (ARDOUR_UI::config()->get_canvasvar_EnteredGainLine());
1870 if (is_drawable()) {
1871 set_canvas_cursor (_cursors->fader);
1876 case AutomationLineItem:
1877 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1878 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1880 line->set_outline_color (ARDOUR_UI::config()->get_canvasvar_EnteredAutomationLine());
1882 if (is_drawable()) {
1883 set_canvas_cursor (_cursors->fader);
1888 case RegionViewNameHighlight:
1889 if (is_drawable() && effective_mouse_mode() == MouseObject && entered_regionview) {
1890 set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1891 _over_region_trim_target = true;
1895 case LeftFrameHandle:
1896 case RightFrameHandle:
1897 if (is_drawable() && effective_mouse_mode() == MouseObject && !internal_editing() && entered_regionview) {
1898 set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1903 switch (effective_mouse_mode()) {
1905 set_canvas_cursor (_cursors->selector);
1908 set_canvas_cursor (which_grabber_cursor());
1913 case StartSelectionTrimItem:
1914 if (is_drawable()) {
1915 set_canvas_cursor (_cursors->left_side_trim);
1918 case EndSelectionTrimItem:
1919 if (is_drawable()) {
1920 set_canvas_cursor (_cursors->right_side_trim);
1924 case PlayheadCursorItem:
1925 if (is_drawable()) {
1926 switch (_edit_point) {
1928 set_canvas_cursor (_cursors->grabber_edit_point);
1931 set_canvas_cursor (_cursors->grabber);
1938 case RegionViewName:
1940 /* when the name is not an active item, the entire name highlight is for trimming */
1942 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1943 if (mouse_mode == MouseObject && is_drawable()) {
1944 set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1945 _over_region_trim_target = true;
1951 case AutomationTrackItem:
1952 if (is_drawable()) {
1953 Gdk::Cursor *cursor;
1954 switch (mouse_mode) {
1956 cursor = _cursors->selector;
1959 cursor = _cursors->zoom_in;
1962 cursor = _cursors->cross_hair;
1966 set_canvas_cursor (cursor);
1968 AutomationTimeAxisView* atv;
1969 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1970 clear_entered_track = false;
1971 set_entered_track (atv);
1977 case RangeMarkerBarItem:
1978 case TransportMarkerBarItem:
1979 case CdMarkerBarItem:
1982 if (is_drawable()) {
1983 set_canvas_cursor (_cursors->timebar);
1988 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1991 entered_marker = marker;
1992 marker->set_color_rgba (ARDOUR_UI::config()->get_canvasvar_EnteredMarker());
1994 case MeterMarkerItem:
1995 case TempoMarkerItem:
1996 if (is_drawable()) {
1997 set_canvas_cursor (_cursors->timebar);
2001 case FadeInHandleItem:
2002 case FadeInTrimHandleItem:
2003 if (mouse_mode == MouseObject && !internal_editing()) {
2004 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
2006 RegionView* rv = static_cast<RegionView*>(item->get_data ("regionview"));
2007 rect->set_fill_color (rv->get_fill_color());
2008 set_canvas_cursor (_cursors->fade_in);
2013 case FadeOutHandleItem:
2014 case FadeOutTrimHandleItem:
2015 if (mouse_mode == MouseObject && !internal_editing()) {
2016 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
2018 RegionView* rv = static_cast<RegionView*>(item->get_data ("regionview"));
2019 rect->set_fill_color (rv->get_fill_color ());
2020 set_canvas_cursor (_cursors->fade_out);
2025 case FeatureLineItem:
2027 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
2028 line->set_outline_color (0xFF0000FF);
2033 if ( get_smart_mode() ) {
2034 set_canvas_cursor ();
2042 /* second pass to handle entered track status in a comprehensible way.
2045 switch (item_type) {
2047 case AutomationLineItem:
2048 case ControlPointItem:
2049 /* these do not affect the current entered track state */
2050 clear_entered_track = false;
2053 case AutomationTrackItem:
2054 /* handled above already */
2058 set_entered_track (0);
2066 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent*, ItemType item_type)
2074 switch (item_type) {
2075 case ControlPointItem:
2076 if (is_drawable()) {
2077 set_canvas_cursor (current_canvas_cursor);
2080 _verbose_cursor->hide ();
2083 case RegionViewNameHighlight:
2084 case LeftFrameHandle:
2085 case RightFrameHandle:
2086 case StartSelectionTrimItem:
2087 case EndSelectionTrimItem:
2088 case PlayheadCursorItem:
2090 _over_region_trim_target = false;
2092 if (is_drawable()) {
2093 set_canvas_cursor (current_canvas_cursor);
2098 case AutomationLineItem:
2099 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
2101 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
2103 line->set_outline_color (al->get_line_color());
2106 if (is_drawable()) {
2107 set_canvas_cursor (current_canvas_cursor);
2111 case RegionViewName:
2112 /* see enter_handler() for notes */
2113 _over_region_trim_target = false;
2115 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
2116 if (is_drawable() && mouse_mode == MouseObject) {
2117 set_canvas_cursor (current_canvas_cursor);
2122 case RangeMarkerBarItem:
2123 case TransportMarkerBarItem:
2124 case CdMarkerBarItem:
2128 if (is_drawable()) {
2129 set_canvas_cursor (current_canvas_cursor);
2134 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2138 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
2139 location_flags_changed (loc, this);
2142 case MeterMarkerItem:
2143 case TempoMarkerItem:
2145 if (is_drawable()) {
2146 set_canvas_cursor (current_canvas_cursor);
2151 case FadeInTrimHandleItem:
2152 case FadeOutTrimHandleItem:
2153 case FadeInHandleItem:
2154 case FadeOutHandleItem:
2156 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
2158 rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_InactiveFadeHandle());
2161 set_canvas_cursor (current_canvas_cursor);
2164 case AutomationTrackItem:
2165 if (is_drawable()) {
2166 set_canvas_cursor (current_canvas_cursor);
2167 clear_entered_track = true;
2168 Glib::signal_idle().connect (sigc::mem_fun(*this, &Editor::left_automation_track));
2171 case FeatureLineItem:
2173 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
2174 line->set_outline_color (ARDOUR_UI::config()->get_canvasvar_ZeroLine());
2186 Editor::left_automation_track ()
2188 if (clear_entered_track) {
2189 set_entered_track (0);
2190 clear_entered_track = false;
2196 Editor::scrub (framepos_t frame, double current_x)
2200 if (scrubbing_direction == 0) {
2202 _session->request_locate (frame, false);
2203 _session->request_transport_speed (0.1);
2204 scrubbing_direction = 1;
2208 if (last_scrub_x > current_x) {
2210 /* pointer moved to the left */
2212 if (scrubbing_direction > 0) {
2214 /* we reversed direction to go backwards */
2217 scrub_reverse_distance += (int) (last_scrub_x - current_x);
2221 /* still moving to the left (backwards) */
2223 scrub_reversals = 0;
2224 scrub_reverse_distance = 0;
2226 delta = 0.01 * (last_scrub_x - current_x);
2227 _session->request_transport_speed_nonzero (_session->transport_speed() - delta);
2231 /* pointer moved to the right */
2233 if (scrubbing_direction < 0) {
2234 /* we reversed direction to go forward */
2237 scrub_reverse_distance += (int) (current_x - last_scrub_x);
2240 /* still moving to the right */
2242 scrub_reversals = 0;
2243 scrub_reverse_distance = 0;
2245 delta = 0.01 * (current_x - last_scrub_x);
2246 _session->request_transport_speed_nonzero (_session->transport_speed() + delta);
2250 /* if there have been more than 2 opposite motion moves detected, or one that moves
2251 back more than 10 pixels, reverse direction
2254 if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
2256 if (scrubbing_direction > 0) {
2257 /* was forwards, go backwards */
2258 _session->request_transport_speed (-0.1);
2259 scrubbing_direction = -1;
2261 /* was backwards, go forwards */
2262 _session->request_transport_speed (0.1);
2263 scrubbing_direction = 1;
2266 scrub_reverse_distance = 0;
2267 scrub_reversals = 0;
2271 last_scrub_x = current_x;
2275 Editor::motion_handler (ArdourCanvas::Item* /*item*/, GdkEvent* event, bool from_autoscroll)
2277 _last_motion_y = event->motion.y;
2279 if (event->motion.is_hint) {
2282 /* We call this so that MOTION_NOTIFY events continue to be
2283 delivered to the canvas. We need to do this because we set
2284 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
2285 the density of the events, at the expense of a round-trip
2286 to the server. Given that this will mostly occur on cases
2287 where DISPLAY = :0.0, and given the cost of what the motion
2288 event might do, its a good tradeoff.
2291 _track_canvas->get_pointer (x, y);
2294 if (current_stepping_trackview) {
2295 /* don't keep the persistent stepped trackview if the mouse moves */
2296 current_stepping_trackview = 0;
2297 step_timeout.disconnect ();
2300 if (_session && _session->actively_recording()) {
2301 /* Sorry. no dragging stuff around while we record */
2305 JoinObjectRangeState const old = _join_object_range_state;
2306 update_join_object_range_location (event->motion.x, event->motion.y);
2308 if (!_internal_editing && _join_object_range_state != old) {
2309 set_canvas_cursor ();
2312 if (!_internal_editing && _over_region_trim_target) {
2313 set_canvas_cursor_for_region_view (event->motion.x, entered_regionview);
2316 bool handled = false;
2317 if (_drags->active ()) {
2318 handled = _drags->motion_handler (event, from_autoscroll);
2325 track_canvas_motion (event);
2330 Editor::can_remove_control_point (ArdourCanvas::Item* item)
2332 ControlPoint* control_point;
2334 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2335 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2339 AutomationLine& line = control_point->line ();
2340 if (dynamic_cast<AudioRegionGainLine*> (&line)) {
2341 /* we shouldn't remove the first or last gain point in region gain lines */
2342 if (line.is_last_point(*control_point) || line.is_first_point(*control_point)) {
2351 Editor::remove_control_point (ArdourCanvas::Item* item)
2353 if (!can_remove_control_point (item)) {
2357 ControlPoint* control_point;
2359 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2360 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2364 control_point->line().remove_point (*control_point);
2368 Editor::edit_control_point (ArdourCanvas::Item* item)
2370 ControlPoint* p = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"));
2373 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2377 ControlPointDialog d (p);
2380 if (d.run () != RESPONSE_ACCEPT) {
2384 p->line().modify_point_y (*p, d.get_y_fraction ());
2388 Editor::edit_notes (TimeAxisViewItem& tavi)
2390 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(&tavi);
2396 MidiRegionView::Selection const & s = mrv->selection();
2402 EditNoteDialog* d = new EditNoteDialog (&(*s.begin())->region_view(), s);
2406 d->signal_response().connect (sigc::bind (sigc::mem_fun (*this, &Editor::note_edit_done), d));
2410 Editor::note_edit_done (int r, EditNoteDialog* d)
2417 Editor::visible_order_range (int* low, int* high) const
2419 *low = TimeAxisView::max_order ();
2422 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
2424 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
2426 if (!rtv->hidden()) {
2428 if (*high < rtv->order()) {
2429 *high = rtv->order ();
2432 if (*low > rtv->order()) {
2433 *low = rtv->order ();
2440 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
2442 /* Either add to or set the set the region selection, unless
2443 this is an alignment click (control used)
2446 if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
2447 TimeAxisView* tv = &rv.get_time_axis_view();
2448 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
2450 if (rtv && rtv->is_track()) {
2451 speed = rtv->track()->speed();
2454 framepos_t where = get_preferred_edit_position();
2458 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
2460 align_region (rv.region(), SyncPoint, (framepos_t) (where * speed));
2462 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
2464 align_region (rv.region(), End, (framepos_t) (where * speed));
2468 align_region (rv.region(), Start, (framepos_t) (where * speed));
2475 Editor::collect_new_region_view (RegionView* rv)
2477 latest_regionviews.push_back (rv);
2481 Editor::collect_and_select_new_region_view (RegionView* rv)
2484 latest_regionviews.push_back (rv);
2488 Editor::cancel_selection ()
2490 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2491 (*i)->hide_selection ();
2494 selection->clear ();
2495 clicked_selection = 0;
2499 Editor::cancel_time_selection ()
2501 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2502 (*i)->hide_selection ();
2504 selection->time.clear ();
2505 clicked_selection = 0;
2509 Editor::point_trim (GdkEvent* event, framepos_t new_bound)
2511 RegionView* rv = clicked_regionview;
2513 /* Choose action dependant on which button was pressed */
2514 switch (event->button.button) {
2516 begin_reversible_command (_("start point trim"));
2518 if (selection->selected (rv)) {
2519 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
2520 i != selection->regions.by_layer().end(); ++i)
2522 if (!(*i)->region()->locked()) {
2523 (*i)->region()->clear_changes ();
2524 (*i)->region()->trim_front (new_bound);
2525 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2530 if (!rv->region()->locked()) {
2531 rv->region()->clear_changes ();
2532 rv->region()->trim_front (new_bound);
2533 _session->add_command(new StatefulDiffCommand (rv->region()));
2537 commit_reversible_command();
2541 begin_reversible_command (_("End point trim"));
2543 if (selection->selected (rv)) {
2545 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
2547 if (!(*i)->region()->locked()) {
2548 (*i)->region()->clear_changes();
2549 (*i)->region()->trim_end (new_bound);
2550 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2556 if (!rv->region()->locked()) {
2557 rv->region()->clear_changes ();
2558 rv->region()->trim_end (new_bound);
2559 _session->add_command (new StatefulDiffCommand (rv->region()));
2563 commit_reversible_command();
2572 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* /*event*/)
2577 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2578 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2582 Location* location = find_location_from_marker (marker, is_start);
2583 location->set_hidden (true, this);
2588 Editor::reposition_zoom_rect (framepos_t start, framepos_t end)
2590 double x1 = sample_to_pixel (start);
2591 double x2 = sample_to_pixel (end);
2592 double y2 = _full_canvas_height - 1.0;
2594 zoom_rect->set (ArdourCanvas::Rect (x1, 1.0, x2, y2));
2599 Editor::mouse_rename_region (ArdourCanvas::Item* /*item*/, GdkEvent* /*event*/)
2601 using namespace Gtkmm2ext;
2603 ArdourPrompter prompter (false);
2605 prompter.set_prompt (_("Name for region:"));
2606 prompter.set_initial_text (clicked_regionview->region()->name());
2607 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
2608 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
2609 prompter.show_all ();
2610 switch (prompter.run ()) {
2611 case Gtk::RESPONSE_ACCEPT:
2613 prompter.get_result(str);
2615 clicked_regionview->region()->set_name (str);
2624 Editor::mouse_brush_insert_region (RegionView* rv, framepos_t pos)
2626 /* no brushing without a useful snap setting */
2628 switch (_snap_mode) {
2630 return; /* can't work because it allows region to be placed anywhere */
2635 switch (_snap_type) {
2643 /* don't brush a copy over the original */
2645 if (pos == rv->region()->position()) {
2649 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
2651 if (rtv == 0 || !rtv->is_track()) {
2655 boost::shared_ptr<Playlist> playlist = rtv->playlist();
2656 double speed = rtv->track()->speed();
2658 playlist->clear_changes ();
2659 boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region(), true));
2660 playlist->add_region (new_region, (framepos_t) (pos * speed));
2661 _session->add_command (new StatefulDiffCommand (playlist));
2663 // playlist is frozen, so we have to update manually XXX this is disgusting
2665 playlist->RegionAdded (new_region); /* EMIT SIGNAL */
2669 Editor::track_height_step_timeout ()
2671 if (get_microseconds() - last_track_height_step_timestamp < 250000) {
2672 current_stepping_trackview = 0;
2679 Editor::add_region_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2681 assert (region_view);
2683 if (!region_view->region()->playlist()) {
2687 if (Config->get_edit_mode() == Splice) {
2688 _drags->add (new RegionSpliceDrag (this, item, region_view, selection->regions.by_layer()));
2690 _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), false, false));
2695 Editor::add_region_copy_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2697 assert (region_view);
2699 if (!region_view->region()->playlist()) {
2703 _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), false, true));
2707 Editor::add_region_brush_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2709 assert (region_view);
2711 if (!region_view->region()->playlist()) {
2715 if (Config->get_edit_mode() == Splice) {
2719 _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), true, false));
2721 begin_reversible_command (Operations::drag_region_brush);
2724 /** Start a grab where a time range is selected, track(s) are selected, and the
2725 * user clicks and drags a region with a modifier in order to create a new region containing
2726 * the section of the clicked region that lies within the time range.
2729 Editor::start_selection_grab (ArdourCanvas::Item* /*item*/, GdkEvent* event)
2731 if (clicked_regionview == 0) {
2735 /* lets try to create new Region for the selection */
2737 vector<boost::shared_ptr<Region> > new_regions;
2738 create_region_from_selection (new_regions);
2740 if (new_regions.empty()) {
2744 /* XXX fix me one day to use all new regions */
2746 boost::shared_ptr<Region> region (new_regions.front());
2748 /* add it to the current stream/playlist.
2750 tricky: the streamview for the track will add a new regionview. we will
2751 catch the signal it sends when it creates the regionview to
2752 set the regionview we want to then drag.
2755 latest_regionviews.clear();
2756 sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
2758 /* A selection grab currently creates two undo/redo operations, one for
2759 creating the new region and another for moving it.
2762 begin_reversible_command (Operations::selection_grab);
2764 boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
2766 playlist->clear_changes ();
2767 clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
2768 _session->add_command(new StatefulDiffCommand (playlist));
2770 commit_reversible_command ();
2774 if (latest_regionviews.empty()) {
2775 /* something went wrong */
2779 /* we need to deselect all other regionviews, and select this one
2780 i'm ignoring undo stuff, because the region creation will take care of it
2782 selection->set (latest_regionviews);
2784 _drags->set (new RegionMoveDrag (this, latest_regionviews.front()->get_canvas_group(), latest_regionviews.front(), latest_regionviews, false, false), event);
2790 if (_drags->active ()) {
2793 selection->clear ();
2798 Editor::set_internal_edit (bool yn)
2800 if (_internal_editing == yn) {
2804 _internal_editing = yn;
2807 pre_internal_mouse_mode = mouse_mode;
2808 pre_internal_snap_type = _snap_type;
2809 pre_internal_snap_mode = _snap_mode;
2811 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2812 (*i)->enter_internal_edit_mode ();
2815 set_snap_to (internal_snap_type);
2816 set_snap_mode (internal_snap_mode);
2820 internal_snap_mode = _snap_mode;
2821 internal_snap_type = _snap_type;
2823 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2824 (*i)->leave_internal_edit_mode ();
2827 if (mouse_mode == MouseDraw && pre_internal_mouse_mode != MouseDraw) {
2828 /* we were drawing .. flip back to something sensible */
2829 set_mouse_mode (pre_internal_mouse_mode);
2832 set_snap_to (pre_internal_snap_type);
2833 set_snap_mode (pre_internal_snap_mode);
2836 set_canvas_cursor ();
2839 /** Update _join_object_range_state which indicate whether we are over the top or bottom half of a region view,
2840 * used by the `join object/range' tool mode.
2843 Editor::update_join_object_range_location (double /*x*/, double y)
2845 /* XXX: actually, this decides based on whether the mouse is in the top
2846 or bottom half of a the waveform part RouteTimeAxisView;
2848 Note that entered_{track,regionview} is not always setup (e.g. if
2849 the mouse is over a TimeSelection), and to get a Region
2850 that we're over requires searching the playlist.
2853 if ( !get_smart_mode() ) {
2854 _join_object_range_state = JOIN_OBJECT_RANGE_NONE;
2858 if (mouse_mode == MouseObject) {
2859 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2860 } else if (mouse_mode == MouseRange) {
2861 _join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
2864 /* XXX: maybe we should make entered_track work in all cases, rather than resorting to this */
2865 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
2869 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
2874 rtv->canvas_display()->canvas_to_item (cx, cy);
2876 double const c = cy / (rtv->view()->child_height() - TimeAxisViewItem::NAME_HIGHLIGHT_SIZE);
2878 _join_object_range_state = c <= 0.5 ? JOIN_OBJECT_RANGE_RANGE : JOIN_OBJECT_RANGE_OBJECT;
2884 Editor::effective_mouse_mode () const
2886 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
2888 } else if (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE) {
2896 Editor::remove_midi_note (ArdourCanvas::Item* item, GdkEvent *)
2898 NoteBase* e = reinterpret_cast<NoteBase*> (item->get_data ("notebase"));
2901 e->region_view().delete_note (e->note ());
2905 Editor::set_canvas_cursor_for_region_view (double x, RegionView* rv)
2907 /* XXX: this check should not be necessary */
2912 ArdourCanvas::Group* g = rv->get_canvas_group ();
2913 ArdourCanvas::Group* p = g->parent ();
2915 /* Compute x in region view parent coordinates */
2917 p->canvas_to_item (x, dy);
2919 boost::optional<ArdourCanvas::Rect> item_bbox = g->bounding_box ();
2921 ArdourCanvas::Rect parent_bbox = g->item_to_parent (item_bbox.get ());
2923 /* First or last 10% of region is used for trimming, if the whole
2924 region is wider than 20 pixels at the current zoom level.
2927 double const w = parent_bbox.width();
2929 if (w > 20.0 && x >= parent_bbox.x0 && x < parent_bbox.x1) {
2931 Trimmable::CanTrim ct = rv->region()->can_trim ();
2933 if (((x - parent_bbox.x0) / w) < 0.10) {
2934 if (ct & Trimmable::FrontTrimEarlier) {
2935 set_canvas_cursor (_cursors->left_side_trim, true);
2937 set_canvas_cursor (_cursors->left_side_trim_right_only, true);
2939 } else if (((parent_bbox.x1 - x) / w) < 0.10) {
2940 if (ct & Trimmable::EndTrimLater) {
2941 set_canvas_cursor (_cursors->right_side_trim, true);
2943 set_canvas_cursor (_cursors->right_side_trim_left_only, true);
2949 /** Obtain the pointer position in canvas coordinates */
2951 Editor::get_pointer_position (double& x, double& y) const
2954 _track_canvas->get_pointer (px, py);
2955 _track_canvas->window_to_canvas (px, py, x, y);