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 case TimecodeRulerItem:
797 case SamplesRulerItem:
798 case MinsecRulerItem:
800 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
801 _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
807 case RangeMarkerBarItem:
808 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
809 _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
811 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateRangeMarker), event);
816 case CdMarkerBarItem:
817 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
818 _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
820 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateCDMarker), event);
825 case TransportMarkerBarItem:
826 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
827 _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
829 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateTransportMarker), event);
838 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
839 /* special case: allow trim of range selections in joined object mode;
840 in theory eff should equal MouseRange in this case, but it doesn't
841 because entering the range selection canvas item results in entered_regionview
842 being set to 0, so update_join_object_range_location acts as if we aren't
845 if (item_type == StartSelectionTrimItem) {
846 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
847 } else if (item_type == EndSelectionTrimItem) {
848 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
852 Editing::MouseMode eff = effective_mouse_mode ();
854 /* special case: allow drag of region fade in/out in object mode with join object/range enabled */
855 if (item_type == FadeInHandleItem || item_type == FadeOutHandleItem) {
859 /* there is no Range mode when in internal edit mode */
860 if (eff == MouseRange && internal_editing()) {
867 case StartSelectionTrimItem:
868 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
871 case EndSelectionTrimItem:
872 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
876 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
877 start_selection_grab (item, event);
879 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
880 /* grab selection for moving */
881 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionMove), event);
883 double const y = event->button.y;
884 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
886 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
887 if ( get_smart_mode() && atv) {
888 /* smart "join" mode: drag automation */
889 _drags->set (new AutomationRangeDrag (this, atv, selection->time), event, _cursors->up_down);
891 /* this was debated, but decided the more common action was to
892 make a new selection */
893 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
900 if (internal_editing()) {
901 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
902 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
906 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::RangeSelectModifier)) {
907 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionExtend), event);
909 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
915 case RegionViewNameHighlight:
916 if (!clicked_regionview->region()->locked()) {
917 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
923 if (!internal_editing()) {
924 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::RangeSelectModifier)) {
925 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionExtend), event);
927 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
937 /* Existing note: allow trimming/motion */
938 if (internal_editing()) {
939 /* trim notes if we're in internal edit mode and near the ends of the note */
940 NoteBase* cn = reinterpret_cast<NoteBase*>(item->get_data ("notebase"));
942 if (cn->big_enough_to_trim() && cn->mouse_near_ends()) {
943 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
945 _drags->set (new NoteDrag (this, item), event);
951 if (internal_editing()) {
952 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
953 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
967 /* Existing note: allow trimming/motion */
968 if (internal_editing()) {
969 NoteBase* cn = reinterpret_cast<NoteBase*> (item->get_data ("notebase"));
971 if (cn->big_enough_to_trim() && cn->mouse_near_ends()) {
972 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
974 _drags->set (new NoteDrag (this, item), event);
984 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)) &&
985 event->type == GDK_BUTTON_PRESS) {
987 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
989 } else if (event->type == GDK_BUTTON_PRESS) {
992 case FadeInHandleItem:
994 _drags->set (new FadeInDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), selection->regions), event, _cursors->fade_in);
998 case FadeOutHandleItem:
1000 _drags->set (new FadeOutDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), selection->regions), event, _cursors->fade_out);
1004 case StartCrossFadeItem:
1005 case EndCrossFadeItem:
1006 /* we might allow user to grab inside the fade to trim a region with preserve_fade_anchor. for not this is not fully implemented */
1007 // if (!clicked_regionview->region()->locked()) {
1008 // _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer(), true), event);
1013 case FeatureLineItem:
1015 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier)) {
1016 remove_transient(item);
1020 _drags->set (new FeatureLineDrag (this, item), event);
1026 if (dynamic_cast<AutomationRegionView*> (clicked_regionview)) {
1027 /* click on an automation region view; do nothing here and let the ARV's signal handler
1033 if (internal_editing ()) {
1037 /* click on a normal region view */
1038 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
1039 add_region_copy_drag (item, event, clicked_regionview);
1040 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
1041 add_region_brush_drag (item, event, clicked_regionview);
1043 add_region_drag (item, event, clicked_regionview);
1047 // if (!internal_editing() && (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE && !selection->regions.empty())) {
1048 // _drags->add (new SelectionDrag (this, clicked_axisview->get_selection_rect (clicked_selection)->rect, SelectionDrag::SelectionMove));
1051 _drags->start_grab (event);
1055 case RegionViewNameHighlight:
1056 case LeftFrameHandle:
1057 case RightFrameHandle:
1058 if (!clicked_regionview->region()->locked()) {
1059 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1064 case FadeInTrimHandleItem:
1065 case FadeOutTrimHandleItem:
1066 if (!clicked_regionview->region()->locked()) {
1067 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer(), true), event);
1072 case RegionViewName:
1074 /* rename happens on edit clicks */
1075 if (clicked_regionview->get_name_highlight()) {
1076 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer()), event);
1082 case ControlPointItem:
1083 _drags->set (new ControlPointDrag (this, item), event);
1087 case AutomationLineItem:
1088 _drags->set (new LineDrag (this, item), event);
1093 if (internal_editing()) {
1094 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
1095 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
1099 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
1103 case AutomationTrackItem:
1105 TimeAxisView* parent = clicked_axisview->get_parent ();
1106 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (clicked_axisview);
1108 if (parent && dynamic_cast<MidiTimeAxisView*> (parent) && atv->show_regions ()) {
1110 RouteTimeAxisView* p = dynamic_cast<RouteTimeAxisView*> (parent);
1112 boost::shared_ptr<Playlist> pl = p->track()->playlist ();
1113 if (pl->n_regions() == 0) {
1114 /* Parent has no regions; create one so that we have somewhere to put automation */
1115 _drags->set (new RegionCreateDrag (this, item, parent), event);
1117 /* See if there's a region before the click that we can extend, and extend it if so */
1118 framepos_t const t = canvas_event_sample (event);
1119 boost::shared_ptr<Region> prev = pl->find_next_region (t, End, -1);
1121 _drags->set (new RegionCreateDrag (this, item, parent), event);
1123 prev->set_length (t - prev->position ());
1127 /* rubberband drag to select automation points */
1128 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
1135 if ( get_smart_mode() ) {
1136 /* we're in "smart" joined mode, and we've clicked on a Selection */
1137 double const y = event->button.y;
1138 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
1140 /* if we're over an automation track, start a drag of its data */
1141 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
1143 _drags->set (new AutomationRangeDrag (this, atv, selection->time), event, _cursors->up_down);
1146 /* if we're over a track and a region, and in the `object' part of a region,
1147 put a selection around the region and drag both
1149 /* RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1150 if (rtv && _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
1151 boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track> (rtv->route ());
1153 boost::shared_ptr<Playlist> pl = t->playlist ();
1156 boost::shared_ptr<Region> r = pl->top_region_at (canvas_event_sample (event));
1158 RegionView* rv = rtv->view()->find_view (r);
1159 clicked_selection = select_range (rv->region()->position(),
1160 rv->region()->last_frame()+1);
1161 _drags->add (new SelectionDrag (this, item, SelectionDrag::SelectionMove));
1162 list<RegionView*> rvs;
1164 _drags->add (new RegionMoveDrag (this, item, rv, rvs, false, false));
1165 _drags->start_grab (event);
1189 switch (item_type) {
1191 _drags->set (new LineDrag (this, item), event);
1194 case ControlPointItem:
1195 _drags->set (new ControlPointDrag (this, item), event);
1201 AudioRegionView* arv = dynamic_cast<AudioRegionView *> (clicked_regionview);
1203 _drags->set (new AutomationRangeDrag (this, arv, selection->time), event, _cursors->up_down);
1204 _drags->start_grab (event);
1210 case AutomationLineItem:
1211 _drags->set (new LineDrag (this, item), event);
1221 if (event->type == GDK_BUTTON_PRESS) {
1222 _drags->set (new MouseZoomDrag (this, item), event);
1229 if (internal_editing() && item_type == NoteItem ) {
1230 /* drag notes if we're in internal edit mode */
1231 NoteBase* cn = reinterpret_cast<NoteBase*>(item->get_data ("notebase"));
1233 if (cn->big_enough_to_trim()) {
1234 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
1237 } else if (clicked_regionview) {
1239 _drags->set (new TimeFXDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1245 _drags->set (new ScrubDrag (this, item), event);
1246 scrub_reversals = 0;
1247 scrub_reverse_distance = 0;
1248 last_scrub_x = event->button.x;
1249 scrubbing_direction = 0;
1250 set_canvas_cursor (_cursors->transparent);
1262 Editor::button_press_handler_2 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1264 Editing::MouseMode const eff = effective_mouse_mode ();
1267 switch (item_type) {
1269 if (internal_editing ()) {
1270 /* no region drags in internal edit mode */
1274 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
1275 add_region_copy_drag (item, event, clicked_regionview);
1277 add_region_drag (item, event, clicked_regionview);
1279 _drags->start_grab (event);
1282 case ControlPointItem:
1283 _drags->set (new ControlPointDrag (this, item), event);
1291 switch (item_type) {
1292 case RegionViewNameHighlight:
1293 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1297 case LeftFrameHandle:
1298 case RightFrameHandle:
1299 if (!internal_editing ()) {
1300 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1305 case RegionViewName:
1306 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer()), event);
1320 /* relax till release */
1326 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1327 temporal_zoom_to_frame (false, canvas_event_sample (event));
1329 temporal_zoom_to_frame (true, canvas_event_sample(event));
1342 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1344 if (event->type == GDK_2BUTTON_PRESS) {
1345 _drags->mark_double_click ();
1346 gdk_pointer_ungrab (GDK_CURRENT_TIME);
1350 if (event->type != GDK_BUTTON_PRESS) {
1354 pre_press_cursor = current_canvas_cursor;
1356 _track_canvas->grab_focus();
1358 if (_session && _session->actively_recording()) {
1362 if (internal_editing()) {
1363 bool leave_internal_edit_mode = false;
1365 switch (item_type) {
1370 if (!dynamic_cast<MidiRegionView*> (clicked_regionview) && !dynamic_cast<AutomationRegionView*> (clicked_regionview)) {
1371 leave_internal_edit_mode = true;
1375 case PlayheadCursorItem:
1377 case TempoMarkerItem:
1378 case MeterMarkerItem:
1382 case RangeMarkerBarItem:
1383 case CdMarkerBarItem:
1384 case TransportMarkerBarItem:
1386 case TimecodeRulerItem:
1387 case SamplesRulerItem:
1388 case MinsecRulerItem:
1390 /* button press on these items never does anything to
1391 change the editing mode.
1399 if (leave_internal_edit_mode) {
1400 ActionManager::do_action ("MouseMode", "toggle-internal-edit");
1404 button_selection (item, event, item_type);
1406 if (!_drags->active () &&
1407 (Keyboard::is_delete_event (&event->button) ||
1408 Keyboard::is_context_menu_event (&event->button) ||
1409 Keyboard::is_edit_event (&event->button))) {
1411 /* handled by button release */
1415 //not rolling, range mode click + join_play_range : locate the PH here
1416 if ( !_drags->active () && !_session->transport_rolling() && ( effective_mouse_mode() == MouseRange ) && Config->get_always_play_range() ) {
1417 framepos_t where = canvas_event_sample (event);
1419 _session->request_locate (where, false);
1422 switch (event->button.button) {
1424 return button_press_handler_1 (item, event, item_type);
1428 return button_press_handler_2 (item, event, item_type);
1435 return button_press_dispatch (&event->button);
1444 Editor::button_press_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::Press);
1454 Editor::button_release_dispatch (GdkEventButton* ev)
1456 /* this function is intended only for buttons 4 and above.
1459 Gtkmm2ext::MouseButton b (ev->state, ev->button);
1460 return button_bindings->activate (b, Gtkmm2ext::Bindings::Release);
1464 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1466 framepos_t where = canvas_event_sample (event);
1467 AutomationTimeAxisView* atv = 0;
1469 if (pre_press_cursor) {
1470 set_canvas_cursor (pre_press_cursor);
1471 pre_press_cursor = 0;
1474 /* no action if we're recording */
1476 if (_session && _session->actively_recording()) {
1480 /* see if we're finishing a drag */
1482 bool were_dragging = false;
1483 if (_drags->active ()) {
1484 bool const r = _drags->end_grab (event);
1486 /* grab dragged, so do nothing else */
1490 were_dragging = true;
1493 update_region_layering_order_editor ();
1495 /* edit events get handled here */
1497 if (!_drags->active () && Keyboard::is_edit_event (&event->button)) {
1498 switch (item_type) {
1500 show_region_properties ();
1503 case TempoMarkerItem: {
1505 TempoMarker* tempo_marker;
1507 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
1508 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
1512 if ((tempo_marker = dynamic_cast<TempoMarker*> (marker)) == 0) {
1513 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
1517 edit_tempo_marker (*tempo_marker);
1521 case MeterMarkerItem: {
1523 MeterMarker* meter_marker;
1525 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
1526 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
1530 if ((meter_marker = dynamic_cast<MeterMarker*> (marker)) == 0) {
1531 fatal << _("programming error: marker for meter is not a meter marker!") << endmsg;
1534 edit_meter_marker (*meter_marker);
1538 case RegionViewName:
1539 if (clicked_regionview->name_active()) {
1540 return mouse_rename_region (item, event);
1544 case ControlPointItem:
1545 edit_control_point (item);
1554 /* context menu events get handled here */
1555 if (Keyboard::is_context_menu_event (&event->button)) {
1557 context_click_event = *event;
1559 if (!_drags->active ()) {
1561 /* no matter which button pops up the context menu, tell the menu
1562 widget to use button 1 to drive menu selection.
1565 switch (item_type) {
1567 case FadeInHandleItem:
1568 case FadeInTrimHandleItem:
1569 case StartCrossFadeItem:
1570 popup_xfade_in_context_menu (1, event->button.time, item, item_type);
1574 case FadeOutHandleItem:
1575 case FadeOutTrimHandleItem:
1576 case EndCrossFadeItem:
1577 popup_xfade_out_context_menu (1, event->button.time, item, item_type);
1581 popup_track_context_menu (1, event->button.time, item_type, false);
1585 case RegionViewNameHighlight:
1586 case LeftFrameHandle:
1587 case RightFrameHandle:
1588 case RegionViewName:
1589 popup_track_context_menu (1, event->button.time, item_type, false);
1593 popup_track_context_menu (1, event->button.time, item_type, true);
1596 case AutomationTrackItem:
1597 popup_track_context_menu (1, event->button.time, item_type, false);
1601 case RangeMarkerBarItem:
1602 case TransportMarkerBarItem:
1603 case CdMarkerBarItem:
1607 case TimecodeRulerItem:
1608 case SamplesRulerItem:
1609 case MinsecRulerItem:
1611 popup_ruler_menu (where, item_type);
1615 marker_context_menu (&event->button, item);
1618 case TempoMarkerItem:
1619 tempo_or_meter_marker_context_menu (&event->button, item);
1622 case MeterMarkerItem:
1623 tempo_or_meter_marker_context_menu (&event->button, item);
1626 case CrossfadeViewItem:
1627 popup_track_context_menu (1, event->button.time, item_type, false);
1630 case ControlPointItem:
1631 popup_control_point_context_menu (item, event);
1642 /* delete events get handled here */
1644 Editing::MouseMode const eff = effective_mouse_mode ();
1646 if (!_drags->active () && Keyboard::is_delete_event (&event->button)) {
1648 switch (item_type) {
1649 case TempoMarkerItem:
1650 remove_tempo_marker (item);
1653 case MeterMarkerItem:
1654 remove_meter_marker (item);
1658 remove_marker (*item, event);
1662 if (eff == MouseObject) {
1663 remove_clicked_region ();
1667 case ControlPointItem:
1668 remove_control_point (item);
1672 remove_midi_note (item, event);
1681 switch (event->button.button) {
1684 switch (item_type) {
1685 /* see comments in button_press_handler */
1686 case PlayheadCursorItem:
1689 case AutomationLineItem:
1690 case StartSelectionTrimItem:
1691 case EndSelectionTrimItem:
1695 if (!_dragging_playhead) {
1696 snap_to_with_modifier (where, event, 0, true);
1697 mouse_add_new_marker (where);
1701 case CdMarkerBarItem:
1702 if (!_dragging_playhead) {
1703 // if we get here then a dragged range wasn't done
1704 snap_to_with_modifier (where, event, 0, true);
1705 mouse_add_new_marker (where, true);
1710 if (!_dragging_playhead) {
1711 snap_to_with_modifier (where, event);
1712 mouse_add_new_tempo_event (where);
1717 if (!_dragging_playhead) {
1718 mouse_add_new_meter_event (pixel_to_sample (event->button.x));
1723 case TimecodeRulerItem:
1724 case SamplesRulerItem:
1725 case MinsecRulerItem:
1736 switch (item_type) {
1737 case AutomationTrackItem:
1738 atv = dynamic_cast<AutomationTimeAxisView*>(clicked_axisview);
1740 bool with_guard_points = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
1741 atv->add_automation_event (event, where, event->button.y, with_guard_points);
1751 switch (item_type) {
1754 /* check that we didn't drag before releasing, since
1755 its really annoying to create new control
1756 points when doing this.
1758 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (clicked_regionview);
1759 if (!were_dragging && arv) {
1760 bool with_guard_points = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
1761 arv->add_gain_point_event (item, event, with_guard_points);
1767 case AutomationTrackItem: {
1768 bool with_guard_points = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
1769 dynamic_cast<AutomationTimeAxisView*>(clicked_axisview)->
1770 add_automation_event (event, where, event->button.y, with_guard_points);
1780 set_canvas_cursor (current_canvas_cursor);
1781 if (scrubbing_direction == 0) {
1782 /* no drag, just a click */
1783 switch (item_type) {
1785 play_selected_region ();
1791 /* make sure we stop */
1792 _session->request_transport_speed (0.0);
1801 /* do any (de)selection operations that should occur on button release */
1802 button_selection (item, event, item_type);
1811 switch (item_type) {
1813 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1815 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier|Keyboard::SecondaryModifier))) {
1818 // Button2 click is unused
1833 // x_style_paste (where, 1.0);
1854 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1861 switch (item_type) {
1862 case ControlPointItem:
1863 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1864 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1869 at_y = cp->get_y ();
1870 cp->i2w (at_x, at_y);
1874 fraction = 1.0 - (cp->get_y() / cp->line().height());
1876 if (is_drawable() && !_drags->active ()) {
1877 set_canvas_cursor (_cursors->fader);
1880 _verbose_cursor->set (cp->line().get_verbose_cursor_string (fraction), at_x, at_y);
1881 _verbose_cursor->show ();
1886 if (mouse_mode == MouseGain) {
1887 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1889 line->set_outline_color (ARDOUR_UI::config()->get_canvasvar_EnteredGainLine());
1891 if (is_drawable()) {
1892 set_canvas_cursor (_cursors->fader);
1897 case AutomationLineItem:
1898 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1899 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1901 line->set_outline_color (ARDOUR_UI::config()->get_canvasvar_EnteredAutomationLine());
1903 if (is_drawable()) {
1904 set_canvas_cursor (_cursors->fader);
1909 case RegionViewNameHighlight:
1910 if (is_drawable() && effective_mouse_mode() == MouseObject && entered_regionview) {
1911 set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1912 _over_region_trim_target = true;
1916 case LeftFrameHandle:
1917 case RightFrameHandle:
1918 if (is_drawable() && effective_mouse_mode() == MouseObject && !internal_editing() && entered_regionview) {
1919 set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1924 switch (effective_mouse_mode()) {
1926 set_canvas_cursor (_cursors->selector);
1929 set_canvas_cursor (which_grabber_cursor());
1934 case StartSelectionTrimItem:
1935 if (is_drawable()) {
1936 set_canvas_cursor (_cursors->left_side_trim);
1939 case EndSelectionTrimItem:
1940 if (is_drawable()) {
1941 set_canvas_cursor (_cursors->right_side_trim);
1945 case PlayheadCursorItem:
1946 if (is_drawable()) {
1947 switch (_edit_point) {
1949 set_canvas_cursor (_cursors->grabber_edit_point);
1952 set_canvas_cursor (_cursors->grabber);
1959 case RegionViewName:
1961 /* when the name is not an active item, the entire name highlight is for trimming */
1963 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1964 if (mouse_mode == MouseObject && is_drawable()) {
1965 set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1966 _over_region_trim_target = true;
1972 case AutomationTrackItem:
1973 if (is_drawable()) {
1974 Gdk::Cursor *cursor;
1975 switch (mouse_mode) {
1977 cursor = _cursors->selector;
1980 cursor = _cursors->zoom_in;
1983 cursor = _cursors->cross_hair;
1987 set_canvas_cursor (cursor);
1989 AutomationTimeAxisView* atv;
1990 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1991 clear_entered_track = false;
1992 set_entered_track (atv);
1998 case RangeMarkerBarItem:
1999 case TransportMarkerBarItem:
2000 case CdMarkerBarItem:
2003 case TimecodeRulerItem:
2004 case SamplesRulerItem:
2005 case MinsecRulerItem:
2007 if (is_drawable()) {
2008 set_canvas_cursor (_cursors->timebar);
2013 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2016 entered_marker = marker;
2017 marker->set_color_rgba (ARDOUR_UI::config()->get_canvasvar_EnteredMarker());
2019 case MeterMarkerItem:
2020 case TempoMarkerItem:
2021 if (is_drawable()) {
2022 set_canvas_cursor (_cursors->timebar);
2026 case FadeInHandleItem:
2027 case FadeInTrimHandleItem:
2028 if (mouse_mode == MouseObject && !internal_editing()) {
2029 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
2031 RegionView* rv = static_cast<RegionView*>(item->get_data ("regionview"));
2032 rect->set_fill_color (rv->get_fill_color());
2033 set_canvas_cursor (_cursors->fade_in);
2038 case FadeOutHandleItem:
2039 case FadeOutTrimHandleItem:
2040 if (mouse_mode == MouseObject && !internal_editing()) {
2041 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
2043 RegionView* rv = static_cast<RegionView*>(item->get_data ("regionview"));
2044 rect->set_fill_color (rv->get_fill_color ());
2045 set_canvas_cursor (_cursors->fade_out);
2050 case FeatureLineItem:
2052 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
2053 line->set_outline_color (0xFF0000FF);
2058 if ( get_smart_mode() ) {
2059 set_canvas_cursor ();
2067 /* second pass to handle entered track status in a comprehensible way.
2070 switch (item_type) {
2072 case AutomationLineItem:
2073 case ControlPointItem:
2074 /* these do not affect the current entered track state */
2075 clear_entered_track = false;
2078 case AutomationTrackItem:
2079 /* handled above already */
2083 set_entered_track (0);
2091 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent*, ItemType item_type)
2099 switch (item_type) {
2100 case ControlPointItem:
2101 if (is_drawable()) {
2102 set_canvas_cursor (current_canvas_cursor);
2105 _verbose_cursor->hide ();
2108 case RegionViewNameHighlight:
2109 case LeftFrameHandle:
2110 case RightFrameHandle:
2111 case StartSelectionTrimItem:
2112 case EndSelectionTrimItem:
2113 case PlayheadCursorItem:
2115 _over_region_trim_target = false;
2117 if (is_drawable()) {
2118 set_canvas_cursor (current_canvas_cursor);
2123 case AutomationLineItem:
2124 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
2126 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
2128 line->set_outline_color (al->get_line_color());
2131 if (is_drawable()) {
2132 set_canvas_cursor (current_canvas_cursor);
2136 case RegionViewName:
2137 /* see enter_handler() for notes */
2138 _over_region_trim_target = false;
2140 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
2141 if (is_drawable() && mouse_mode == MouseObject) {
2142 set_canvas_cursor (current_canvas_cursor);
2147 case RangeMarkerBarItem:
2148 case TransportMarkerBarItem:
2149 case CdMarkerBarItem:
2153 case TimecodeRulerItem:
2154 case SamplesRulerItem:
2155 case MinsecRulerItem:
2157 if (is_drawable()) {
2158 set_canvas_cursor (current_canvas_cursor);
2163 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2167 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
2168 location_flags_changed (loc, this);
2171 case MeterMarkerItem:
2172 case TempoMarkerItem:
2174 if (is_drawable()) {
2175 set_canvas_cursor (current_canvas_cursor);
2180 case FadeInTrimHandleItem:
2181 case FadeOutTrimHandleItem:
2182 case FadeInHandleItem:
2183 case FadeOutHandleItem:
2185 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
2187 rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_InactiveFadeHandle());
2190 set_canvas_cursor (current_canvas_cursor);
2193 case AutomationTrackItem:
2194 if (is_drawable()) {
2195 set_canvas_cursor (current_canvas_cursor);
2196 clear_entered_track = true;
2197 Glib::signal_idle().connect (sigc::mem_fun(*this, &Editor::left_automation_track));
2200 case FeatureLineItem:
2202 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
2203 line->set_outline_color (ARDOUR_UI::config()->get_canvasvar_ZeroLine());
2215 Editor::left_automation_track ()
2217 if (clear_entered_track) {
2218 set_entered_track (0);
2219 clear_entered_track = false;
2225 Editor::scrub (framepos_t frame, double current_x)
2229 if (scrubbing_direction == 0) {
2231 _session->request_locate (frame, false);
2232 _session->request_transport_speed (0.1);
2233 scrubbing_direction = 1;
2237 if (last_scrub_x > current_x) {
2239 /* pointer moved to the left */
2241 if (scrubbing_direction > 0) {
2243 /* we reversed direction to go backwards */
2246 scrub_reverse_distance += (int) (last_scrub_x - current_x);
2250 /* still moving to the left (backwards) */
2252 scrub_reversals = 0;
2253 scrub_reverse_distance = 0;
2255 delta = 0.01 * (last_scrub_x - current_x);
2256 _session->request_transport_speed_nonzero (_session->transport_speed() - delta);
2260 /* pointer moved to the right */
2262 if (scrubbing_direction < 0) {
2263 /* we reversed direction to go forward */
2266 scrub_reverse_distance += (int) (current_x - last_scrub_x);
2269 /* still moving to the right */
2271 scrub_reversals = 0;
2272 scrub_reverse_distance = 0;
2274 delta = 0.01 * (current_x - last_scrub_x);
2275 _session->request_transport_speed_nonzero (_session->transport_speed() + delta);
2279 /* if there have been more than 2 opposite motion moves detected, or one that moves
2280 back more than 10 pixels, reverse direction
2283 if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
2285 if (scrubbing_direction > 0) {
2286 /* was forwards, go backwards */
2287 _session->request_transport_speed (-0.1);
2288 scrubbing_direction = -1;
2290 /* was backwards, go forwards */
2291 _session->request_transport_speed (0.1);
2292 scrubbing_direction = 1;
2295 scrub_reverse_distance = 0;
2296 scrub_reversals = 0;
2300 last_scrub_x = current_x;
2304 Editor::motion_handler (ArdourCanvas::Item* /*item*/, GdkEvent* event, bool from_autoscroll)
2306 _last_motion_y = event->motion.y;
2308 if (event->motion.is_hint) {
2311 /* We call this so that MOTION_NOTIFY events continue to be
2312 delivered to the canvas. We need to do this because we set
2313 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
2314 the density of the events, at the expense of a round-trip
2315 to the server. Given that this will mostly occur on cases
2316 where DISPLAY = :0.0, and given the cost of what the motion
2317 event might do, its a good tradeoff.
2320 _track_canvas->get_pointer (x, y);
2323 if (current_stepping_trackview) {
2324 /* don't keep the persistent stepped trackview if the mouse moves */
2325 current_stepping_trackview = 0;
2326 step_timeout.disconnect ();
2329 if (_session && _session->actively_recording()) {
2330 /* Sorry. no dragging stuff around while we record */
2334 JoinObjectRangeState const old = _join_object_range_state;
2335 update_join_object_range_location (event->motion.x, event->motion.y);
2337 if (!_internal_editing && _join_object_range_state != old) {
2338 set_canvas_cursor ();
2341 if (!_internal_editing && _over_region_trim_target) {
2342 set_canvas_cursor_for_region_view (event->motion.x, entered_regionview);
2345 bool handled = false;
2346 if (_drags->active ()) {
2347 handled = _drags->motion_handler (event, from_autoscroll);
2354 track_canvas_motion (event);
2359 Editor::can_remove_control_point (ArdourCanvas::Item* item)
2361 ControlPoint* control_point;
2363 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2364 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2368 AutomationLine& line = control_point->line ();
2369 if (dynamic_cast<AudioRegionGainLine*> (&line)) {
2370 /* we shouldn't remove the first or last gain point in region gain lines */
2371 if (line.is_last_point(*control_point) || line.is_first_point(*control_point)) {
2380 Editor::remove_control_point (ArdourCanvas::Item* item)
2382 if (!can_remove_control_point (item)) {
2386 ControlPoint* control_point;
2388 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2389 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2393 control_point->line().remove_point (*control_point);
2397 Editor::edit_control_point (ArdourCanvas::Item* item)
2399 ControlPoint* p = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"));
2402 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2406 ControlPointDialog d (p);
2409 if (d.run () != RESPONSE_ACCEPT) {
2413 p->line().modify_point_y (*p, d.get_y_fraction ());
2417 Editor::edit_notes (TimeAxisViewItem& tavi)
2419 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(&tavi);
2425 MidiRegionView::Selection const & s = mrv->selection();
2431 EditNoteDialog* d = new EditNoteDialog (&(*s.begin())->region_view(), s);
2435 d->signal_response().connect (sigc::bind (sigc::mem_fun (*this, &Editor::note_edit_done), d));
2439 Editor::note_edit_done (int r, EditNoteDialog* d)
2446 Editor::visible_order_range (int* low, int* high) const
2448 *low = TimeAxisView::max_order ();
2451 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
2453 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
2455 if (!rtv->hidden()) {
2457 if (*high < rtv->order()) {
2458 *high = rtv->order ();
2461 if (*low > rtv->order()) {
2462 *low = rtv->order ();
2469 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
2471 /* Either add to or set the set the region selection, unless
2472 this is an alignment click (control used)
2475 if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
2476 TimeAxisView* tv = &rv.get_time_axis_view();
2477 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
2479 if (rtv && rtv->is_track()) {
2480 speed = rtv->track()->speed();
2483 framepos_t where = get_preferred_edit_position();
2487 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
2489 align_region (rv.region(), SyncPoint, (framepos_t) (where * speed));
2491 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
2493 align_region (rv.region(), End, (framepos_t) (where * speed));
2497 align_region (rv.region(), Start, (framepos_t) (where * speed));
2504 Editor::collect_new_region_view (RegionView* rv)
2506 latest_regionviews.push_back (rv);
2510 Editor::collect_and_select_new_region_view (RegionView* rv)
2513 latest_regionviews.push_back (rv);
2517 Editor::cancel_selection ()
2519 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2520 (*i)->hide_selection ();
2523 selection->clear ();
2524 clicked_selection = 0;
2528 Editor::cancel_time_selection ()
2530 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2531 (*i)->hide_selection ();
2533 selection->time.clear ();
2534 clicked_selection = 0;
2538 Editor::point_trim (GdkEvent* event, framepos_t new_bound)
2540 RegionView* rv = clicked_regionview;
2542 /* Choose action dependant on which button was pressed */
2543 switch (event->button.button) {
2545 begin_reversible_command (_("start point trim"));
2547 if (selection->selected (rv)) {
2548 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
2549 i != selection->regions.by_layer().end(); ++i)
2551 if (!(*i)->region()->locked()) {
2552 (*i)->region()->clear_changes ();
2553 (*i)->region()->trim_front (new_bound);
2554 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2559 if (!rv->region()->locked()) {
2560 rv->region()->clear_changes ();
2561 rv->region()->trim_front (new_bound);
2562 _session->add_command(new StatefulDiffCommand (rv->region()));
2566 commit_reversible_command();
2570 begin_reversible_command (_("End point trim"));
2572 if (selection->selected (rv)) {
2574 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
2576 if (!(*i)->region()->locked()) {
2577 (*i)->region()->clear_changes();
2578 (*i)->region()->trim_end (new_bound);
2579 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2585 if (!rv->region()->locked()) {
2586 rv->region()->clear_changes ();
2587 rv->region()->trim_end (new_bound);
2588 _session->add_command (new StatefulDiffCommand (rv->region()));
2592 commit_reversible_command();
2601 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* /*event*/)
2606 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2607 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2611 Location* location = find_location_from_marker (marker, is_start);
2612 location->set_hidden (true, this);
2617 Editor::reposition_zoom_rect (framepos_t start, framepos_t end)
2619 double x1 = sample_to_pixel (start);
2620 double x2 = sample_to_pixel (end);
2621 double y2 = _full_canvas_height - 1.0;
2623 zoom_rect->set (ArdourCanvas::Rect (x1, 1.0, x2, y2));
2628 Editor::mouse_rename_region (ArdourCanvas::Item* /*item*/, GdkEvent* /*event*/)
2630 using namespace Gtkmm2ext;
2632 ArdourPrompter prompter (false);
2634 prompter.set_prompt (_("Name for region:"));
2635 prompter.set_initial_text (clicked_regionview->region()->name());
2636 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
2637 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
2638 prompter.show_all ();
2639 switch (prompter.run ()) {
2640 case Gtk::RESPONSE_ACCEPT:
2642 prompter.get_result(str);
2644 clicked_regionview->region()->set_name (str);
2653 Editor::mouse_brush_insert_region (RegionView* rv, framepos_t pos)
2655 /* no brushing without a useful snap setting */
2657 switch (_snap_mode) {
2659 return; /* can't work because it allows region to be placed anywhere */
2664 switch (_snap_type) {
2672 /* don't brush a copy over the original */
2674 if (pos == rv->region()->position()) {
2678 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
2680 if (rtv == 0 || !rtv->is_track()) {
2684 boost::shared_ptr<Playlist> playlist = rtv->playlist();
2685 double speed = rtv->track()->speed();
2687 playlist->clear_changes ();
2688 boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region(), true));
2689 playlist->add_region (new_region, (framepos_t) (pos * speed));
2690 _session->add_command (new StatefulDiffCommand (playlist));
2692 // playlist is frozen, so we have to update manually XXX this is disgusting
2694 playlist->RegionAdded (new_region); /* EMIT SIGNAL */
2698 Editor::track_height_step_timeout ()
2700 if (get_microseconds() - last_track_height_step_timestamp < 250000) {
2701 current_stepping_trackview = 0;
2708 Editor::add_region_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2710 assert (region_view);
2712 if (!region_view->region()->playlist()) {
2716 if (Config->get_edit_mode() == Splice) {
2717 _drags->add (new RegionSpliceDrag (this, item, region_view, selection->regions.by_layer()));
2719 _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), false, false));
2724 Editor::add_region_copy_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2726 assert (region_view);
2728 if (!region_view->region()->playlist()) {
2732 _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), false, true));
2736 Editor::add_region_brush_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2738 assert (region_view);
2740 if (!region_view->region()->playlist()) {
2744 if (Config->get_edit_mode() == Splice) {
2748 _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), true, false));
2750 begin_reversible_command (Operations::drag_region_brush);
2753 /** Start a grab where a time range is selected, track(s) are selected, and the
2754 * user clicks and drags a region with a modifier in order to create a new region containing
2755 * the section of the clicked region that lies within the time range.
2758 Editor::start_selection_grab (ArdourCanvas::Item* /*item*/, GdkEvent* event)
2760 if (clicked_regionview == 0) {
2764 /* lets try to create new Region for the selection */
2766 vector<boost::shared_ptr<Region> > new_regions;
2767 create_region_from_selection (new_regions);
2769 if (new_regions.empty()) {
2773 /* XXX fix me one day to use all new regions */
2775 boost::shared_ptr<Region> region (new_regions.front());
2777 /* add it to the current stream/playlist.
2779 tricky: the streamview for the track will add a new regionview. we will
2780 catch the signal it sends when it creates the regionview to
2781 set the regionview we want to then drag.
2784 latest_regionviews.clear();
2785 sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
2787 /* A selection grab currently creates two undo/redo operations, one for
2788 creating the new region and another for moving it.
2791 begin_reversible_command (Operations::selection_grab);
2793 boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
2795 playlist->clear_changes ();
2796 clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
2797 _session->add_command(new StatefulDiffCommand (playlist));
2799 commit_reversible_command ();
2803 if (latest_regionviews.empty()) {
2804 /* something went wrong */
2808 /* we need to deselect all other regionviews, and select this one
2809 i'm ignoring undo stuff, because the region creation will take care of it
2811 selection->set (latest_regionviews);
2813 _drags->set (new RegionMoveDrag (this, latest_regionviews.front()->get_canvas_group(), latest_regionviews.front(), latest_regionviews, false, false), event);
2819 if (_drags->active ()) {
2822 selection->clear ();
2827 Editor::set_internal_edit (bool yn)
2829 if (_internal_editing == yn) {
2833 _internal_editing = yn;
2836 pre_internal_mouse_mode = mouse_mode;
2837 pre_internal_snap_type = _snap_type;
2838 pre_internal_snap_mode = _snap_mode;
2840 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2841 (*i)->enter_internal_edit_mode ();
2844 set_snap_to (internal_snap_type);
2845 set_snap_mode (internal_snap_mode);
2849 internal_snap_mode = _snap_mode;
2850 internal_snap_type = _snap_type;
2852 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2853 (*i)->leave_internal_edit_mode ();
2856 if (mouse_mode == MouseDraw && pre_internal_mouse_mode != MouseDraw) {
2857 /* we were drawing .. flip back to something sensible */
2858 set_mouse_mode (pre_internal_mouse_mode);
2861 set_snap_to (pre_internal_snap_type);
2862 set_snap_mode (pre_internal_snap_mode);
2865 set_canvas_cursor ();
2868 /** Update _join_object_range_state which indicate whether we are over the top or bottom half of a region view,
2869 * used by the `join object/range' tool mode. Coordinates in canvas space.
2872 Editor::update_join_object_range_location (double /*x*/, double y)
2874 /* XXX: actually, this decides based on whether the mouse is in the top
2875 or bottom half of a the waveform part RouteTimeAxisView;
2877 Note that entered_{track,regionview} is not always setup (e.g. if
2878 the mouse is over a TimeSelection), and to get a Region
2879 that we're over requires searching the playlist.
2882 if ( !get_smart_mode() ) {
2883 _join_object_range_state = JOIN_OBJECT_RANGE_NONE;
2887 if (mouse_mode == MouseObject) {
2888 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2889 } else if (mouse_mode == MouseRange) {
2890 _join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
2893 /* XXX: maybe we should make entered_track work in all cases, rather than resorting to this */
2894 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y, false);
2898 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
2903 rtv->canvas_display()->canvas_to_item (cx, cy);
2905 double const c = cy / (rtv->view()->child_height() - TimeAxisViewItem::NAME_HIGHLIGHT_SIZE);
2907 _join_object_range_state = c <= 0.5 ? JOIN_OBJECT_RANGE_RANGE : JOIN_OBJECT_RANGE_OBJECT;
2913 Editor::effective_mouse_mode () const
2915 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
2917 } else if (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE) {
2925 Editor::remove_midi_note (ArdourCanvas::Item* item, GdkEvent *)
2927 NoteBase* e = reinterpret_cast<NoteBase*> (item->get_data ("notebase"));
2930 e->region_view().delete_note (e->note ());
2934 Editor::set_canvas_cursor_for_region_view (double x, RegionView* rv)
2936 /* XXX: this check should not be necessary */
2941 ArdourCanvas::Group* g = rv->get_canvas_group ();
2942 ArdourCanvas::Group* p = g->parent ();
2944 /* Compute x in region view parent coordinates */
2946 p->canvas_to_item (x, dy);
2948 boost::optional<ArdourCanvas::Rect> item_bbox = g->bounding_box ();
2950 ArdourCanvas::Rect parent_bbox = g->item_to_parent (item_bbox.get ());
2952 /* First or last 10% of region is used for trimming, if the whole
2953 region is wider than 20 pixels at the current zoom level.
2956 double const w = parent_bbox.width();
2958 if (w > 20.0 && x >= parent_bbox.x0 && x < parent_bbox.x1) {
2960 Trimmable::CanTrim ct = rv->region()->can_trim ();
2962 if (((x - parent_bbox.x0) / w) < 0.10) {
2963 if (ct & Trimmable::FrontTrimEarlier) {
2964 set_canvas_cursor (_cursors->left_side_trim, true);
2966 set_canvas_cursor (_cursors->left_side_trim_right_only, true);
2968 } else if (((parent_bbox.x1 - x) / w) < 0.10) {
2969 if (ct & Trimmable::EndTrimLater) {
2970 set_canvas_cursor (_cursors->right_side_trim, true);
2972 set_canvas_cursor (_cursors->right_side_trim_left_only, true);
2978 /** Obtain the pointer position in canvas coordinates */
2980 Editor::get_pointer_position (double& x, double& y) const
2983 _track_canvas->get_pointer (px, py);
2984 _track_canvas->window_to_canvas (px, py, x, y);