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) {
106 Gdk::ModifierType mask;
107 Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->_track_canvas_viewport->get_bin_window();
108 Glib::RefPtr<const Gdk::Window> pointer_window;
110 if (!canvas_window) {
114 pointer_window = canvas_window->get_pointer (x, y, mask);
116 if (pointer_window == _track_canvas->get_window()) {
119 in_track_canvas = true;
121 in_track_canvas = false;
126 event.type = GDK_BUTTON_RELEASE;
130 where = event_frame (&event, 0, 0);
135 Editor::event_frame (GdkEvent const * event, double* pcx, double* pcy) const
149 /* The event coordinates will be canvas coordinates */
151 switch (event->type) {
152 case GDK_BUTTON_RELEASE:
153 case GDK_BUTTON_PRESS:
154 case GDK_2BUTTON_PRESS:
155 case GDK_3BUTTON_PRESS:
156 *pcx = event->button.x;
157 *pcy = event->button.y;
158 _trackview_group->canvas_to_item (*pcx, *pcy);
160 case GDK_MOTION_NOTIFY:
161 *pcx = event->motion.x;
162 *pcy = event->motion.y;
163 _trackview_group->canvas_to_item (*pcx, *pcy);
165 case GDK_ENTER_NOTIFY:
166 case GDK_LEAVE_NOTIFY:
167 *pcx = event->crossing.x;
168 *pcy = event->crossing.y;
170 // track_canvas->w2c(event->crossing.x, event->crossing.y, *pcx, *pcy);
173 case GDK_KEY_RELEASE:
174 // track_canvas->w2c(event->key.x, event->key.y, *pcx, *pcy);
177 warning << string_compose (_("Editor::event_frame() used on unhandled event type %1"), event->type) << endmsg;
181 /* note that pixel_to_frame() never returns less than zero, so even if the pixel
182 position is negative (as can be the case with motion events in particular),
183 the frame location is always positive.
186 return pixel_to_frame (*pcx);
190 Editor::which_grabber_cursor ()
192 Gdk::Cursor* c = _cursors->grabber;
194 if (_internal_editing) {
195 switch (mouse_mode) {
197 c = _cursors->midi_pencil;
201 c = _cursors->grabber_note;
205 c = _cursors->midi_resize;
209 c = _cursors->grabber_note;
218 switch (_edit_point) {
220 c = _cursors->grabber_edit_point;
223 boost::shared_ptr<Movable> m = _movable.lock();
224 if (m && m->locked()) {
225 c = _cursors->speaker;
235 Editor::set_current_trimmable (boost::shared_ptr<Trimmable> t)
237 boost::shared_ptr<Trimmable> st = _trimmable.lock();
239 if (!st || st == t) {
241 set_canvas_cursor ();
246 Editor::set_current_movable (boost::shared_ptr<Movable> m)
248 boost::shared_ptr<Movable> sm = _movable.lock();
250 if (!sm || sm != m) {
252 set_canvas_cursor ();
257 Editor::set_canvas_cursor ()
259 switch (mouse_mode) {
261 current_canvas_cursor = _cursors->selector;
262 if (_internal_editing) {
263 current_canvas_cursor = which_grabber_cursor();
268 current_canvas_cursor = which_grabber_cursor();
272 current_canvas_cursor = _cursors->midi_pencil;
276 current_canvas_cursor = _cursors->cross_hair;
280 if (Keyboard::the_keyboard().key_is_down (GDK_Control_L)) {
281 current_canvas_cursor = _cursors->zoom_out;
283 current_canvas_cursor = _cursors->zoom_in;
288 current_canvas_cursor = _cursors->time_fx; // just use playhead
292 current_canvas_cursor = _cursors->speaker;
296 if (!_internal_editing) {
297 switch (_join_object_range_state) {
298 case JOIN_OBJECT_RANGE_NONE:
300 case JOIN_OBJECT_RANGE_OBJECT:
301 current_canvas_cursor = which_grabber_cursor ();
303 case JOIN_OBJECT_RANGE_RANGE:
304 current_canvas_cursor = _cursors->selector;
309 /* up-down cursor as a cue that automation can be dragged up and down when in join object/range mode */
310 if (!_internal_editing && get_smart_mode() ) {
313 get_pointer_position (x, y);
315 if (x >= 0 && y >= 0) {
317 vector<ArdourCanvas::Item const *> items;
319 _track_canvas->root()->add_items_at_point (ArdourCanvas::Duple (x,y), items);
321 // first item will be the upper most
323 if (!items.empty()) {
324 const ArdourCanvas::Item* i = items.front();
326 if (i && i->parent() && i->parent()->get_data (X_("timeselection"))) {
327 pair<TimeAxisView*, int> tvp = trackview_by_y_position (_last_motion_y + vertical_adjustment.get_value());
328 if (dynamic_cast<AutomationTimeAxisView*> (tvp.first)) {
329 current_canvas_cursor = _cursors->up_down;
336 set_canvas_cursor (current_canvas_cursor, true);
340 Editor::mouse_mode_object_range_toggled()
342 MouseMode m = mouse_mode;
344 Glib::RefPtr<Action> act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-range"));
346 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
348 if (tact->get_active())
349 m = MouseObject; //Smart mode turned to ON, force editing to Object mode
351 set_mouse_mode(m, true); //call this so the button styles can get updated
355 Editor::set_mouse_mode (MouseMode m, bool force)
357 if (_drags->active ()) {
361 if (!force && m == mouse_mode) {
365 Glib::RefPtr<Action> act;
369 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-range"));
373 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-object"));
377 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-draw"));
381 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-gain"));
385 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-zoom"));
389 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-timefx"));
393 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-audition"));
399 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
402 /* go there and back to ensure that the toggled handler is called to set up mouse_mode */
403 tact->set_active (false);
404 tact->set_active (true);
406 //NOTE: this will result in a call to mouse_mode_toggled which does the heavy lifting
410 Editor::mouse_mode_toggled (MouseMode m)
412 Glib::RefPtr<Action> act;
413 Glib::RefPtr<ToggleAction> tact;
417 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-range"));
421 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-object"));
425 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-draw"));
429 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-gain"));
433 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-zoom"));
437 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-timefx"));
441 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-audition"));
447 tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
450 if (!tact->get_active()) {
451 /* this was just the notification that the old mode has been
452 * left. we'll get called again with the new mode active in a
460 act = ActionManager::get_action (X_("MouseMode"), X_("toggle-internal-edit"));
461 tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
462 tact->set_active (true);
468 if (_session && mouse_mode == MouseAudition) {
469 /* stop transport and reset default speed to avoid oddness with
471 _session->request_transport_speed (0.0, true);
478 //TODO: set button styles for smart buttons
480 if ( smart_mode_action->get_active() ) {
481 if( mouse_mode == MouseObject ) { //smart active and object active
482 smart_mode_button.set_active(1);
483 smart_mode_button.set_name("smart mode button");
484 mouse_move_button.set_name("smart mode button");
485 } else { //smart active but object inactive
486 smart_mode_button.set_active(0);
487 smart_mode_button.set_name("smart mode button");
488 mouse_move_button.set_name("mouse mode button");
491 smart_mode_button.set_active(0);
492 smart_mode_button.set_name("mouse mode button");
493 mouse_move_button.set_name("mouse mode button");
497 set_canvas_cursor ();
498 set_gain_envelope_visibility ();
500 MouseModeChanged (); /* EMIT SIGNAL */
504 Editor::step_mouse_mode (bool next)
506 switch (current_mouse_mode()) {
509 if (Profile->get_sae()) {
510 set_mouse_mode (MouseZoom);
512 set_mouse_mode (MouseRange);
515 set_mouse_mode (MouseTimeFX);
520 if (next) set_mouse_mode (MouseDraw);
521 else set_mouse_mode (MouseObject);
525 if (next) set_mouse_mode (MouseZoom);
526 else set_mouse_mode (MouseRange);
531 if (Profile->get_sae()) {
532 set_mouse_mode (MouseTimeFX);
534 set_mouse_mode (MouseGain);
537 if (Profile->get_sae()) {
538 set_mouse_mode (MouseObject);
540 set_mouse_mode (MouseDraw);
546 if (next) set_mouse_mode (MouseTimeFX);
547 else set_mouse_mode (MouseZoom);
552 set_mouse_mode (MouseAudition);
554 if (Profile->get_sae()) {
555 set_mouse_mode (MouseZoom);
557 set_mouse_mode (MouseGain);
563 if (next) set_mouse_mode (MouseObject);
564 else set_mouse_mode (MouseTimeFX);
570 Editor::toggle_internal_editing_from_double_click (GdkEvent* event)
572 if (_drags->active()) {
573 _drags->end_grab (event);
576 ActionManager::do_action ("MouseMode", "toggle-internal-edit");
578 /* prevent reversion of edit cursor on button release */
580 pre_press_cursor = 0;
586 Editor::button_selection (ArdourCanvas::Item* /*item*/, GdkEvent* event, ItemType item_type)
588 /* in object/audition/timefx/gain-automation mode,
589 any button press sets the selection if the object
590 can be selected. this is a bit of hack, because
591 we want to avoid this if the mouse operation is a
594 note: not dbl-click or triple-click
596 Also note that there is no region selection in internal edit mode, otherwise
597 for operations operating on the selection (e.g. cut) it is not obvious whether
598 to cut notes or regions.
601 if (((mouse_mode != MouseObject) &&
602 (mouse_mode != MouseAudition || item_type != RegionItem) &&
603 (mouse_mode != MouseTimeFX || item_type != RegionItem) &&
604 (mouse_mode != MouseGain) &&
605 (mouse_mode != MouseDraw)) ||
606 ((event->type != GDK_BUTTON_PRESS && event->type != GDK_BUTTON_RELEASE) || event->button.button > 3) ||
607 (internal_editing() && mouse_mode != MouseTimeFX)) {
612 if (event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) {
614 if ((event->button.state & Keyboard::RelevantModifierKeyMask) && event->button.button != 1) {
616 /* almost no selection action on modified button-2 or button-3 events */
618 if (item_type != RegionItem && event->button.button != 2) {
624 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
625 bool press = (event->type == GDK_BUTTON_PRESS);
629 if (!get_smart_mode() || (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT)) {
631 if (mouse_mode != MouseRange) {
632 set_selected_regionview_from_click (press, op);
634 /* don't change the selection unless the
635 clicked track is not currently selected. if
636 so, "collapse" the selection to just this
639 if (!selection->selected (clicked_axisview)) {
640 set_selected_track_as_side_effect (Selection::Set);
644 if (mouse_mode != MouseRange) {
645 set_selected_regionview_from_click (press, op);
651 case RegionViewNameHighlight:
653 case LeftFrameHandle:
654 case RightFrameHandle:
655 if ( mouse_mode != MouseRange ) {
656 set_selected_regionview_from_click (press, op);
657 } else if (event->type == GDK_BUTTON_PRESS) {
658 set_selected_track_as_side_effect (op);
662 case FadeInHandleItem:
664 case FadeOutHandleItem:
666 case StartCrossFadeItem:
667 case EndCrossFadeItem:
668 if ( mouse_mode != MouseRange ) {
669 set_selected_regionview_from_click (press, op);
670 } else if (event->type == GDK_BUTTON_PRESS) {
671 set_selected_track_as_side_effect (op);
675 case ControlPointItem:
676 set_selected_track_as_side_effect (op);
677 if ( mouse_mode != MouseRange ) {
678 set_selected_control_point_from_click (press, op);
683 /* for context click, select track */
684 if (event->button.button == 3) {
685 selection->clear_tracks ();
686 set_selected_track_as_side_effect (op);
690 case AutomationTrackItem:
691 set_selected_track_as_side_effect (op);
700 Editor::button_press_handler_1 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
702 /* single mouse clicks on any of these item types operate
703 independent of mouse mode, mostly because they are
704 not on the main track canvas or because we want
709 case PlayheadCursorItem:
710 _drags->set (new CursorDrag (this, item, true), event);
714 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
715 hide_marker (item, event);
717 _drags->set (new MarkerDrag (this, item), event);
721 case TempoMarkerItem:
723 TempoMarker* m = reinterpret_cast<TempoMarker*> (item->get_data ("marker"));
725 if (m->tempo().movable ()) {
727 new TempoMarkerDrag (
730 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
740 case MeterMarkerItem:
742 MeterMarker* m = reinterpret_cast<MeterMarker*> (item->get_data ("marker"));
744 if (m->meter().movable ()) {
746 new MeterMarkerDrag (
749 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
760 _drags->set (new VideoTimeLineDrag (this, item), event);
767 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
768 _drags->set (new CursorDrag (this, &playhead_cursor->track_canvas_item (), false), event);
774 case RangeMarkerBarItem:
775 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
776 _drags->set (new CursorDrag (this, &playhead_cursor->track_canvas_item (), false), event);
778 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateRangeMarker), event);
783 case CdMarkerBarItem:
784 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
785 _drags->set (new CursorDrag (this, &playhead_cursor->track_canvas_item (), false), event);
787 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateCDMarker), event);
792 case TransportMarkerBarItem:
793 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
794 _drags->set (new CursorDrag (this, &playhead_cursor->track_canvas_item (), false), event);
796 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateTransportMarker), event);
805 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
806 /* special case: allow trim of range selections in joined object mode;
807 in theory eff should equal MouseRange in this case, but it doesn't
808 because entering the range selection canvas item results in entered_regionview
809 being set to 0, so update_join_object_range_location acts as if we aren't
812 if (item_type == StartSelectionTrimItem) {
813 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
814 } else if (item_type == EndSelectionTrimItem) {
815 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
819 Editing::MouseMode eff = effective_mouse_mode ();
821 /* special case: allow drag of region fade in/out in object mode with join object/range enabled */
822 if (item_type == FadeInHandleItem || item_type == FadeOutHandleItem) {
826 /* there is no Range mode when in internal edit mode */
827 if (eff == MouseRange && internal_editing()) {
834 case StartSelectionTrimItem:
835 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
838 case EndSelectionTrimItem:
839 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
843 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
844 start_selection_grab (item, event);
846 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
847 /* grab selection for moving */
848 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionMove), event);
850 double const y = event->button.y + vertical_adjustment.get_value();
851 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
853 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
854 if ( get_smart_mode() && atv) {
855 /* smart "join" mode: drag automation */
856 _drags->set (new AutomationRangeDrag (this, atv, selection->time), event, _cursors->up_down);
858 /* this was debated, but decided the more common action was to
859 make a new selection */
860 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
867 if (internal_editing()) {
868 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
869 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
873 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::RangeSelectModifier)) {
874 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionExtend), event);
876 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
882 case RegionViewNameHighlight:
883 if (!clicked_regionview->region()->locked()) {
884 RegionSelection s = get_equivalent_regions (selection->regions, Properties::select.property_id);
885 _drags->set (new TrimDrag (this, item, clicked_regionview, s.by_layer()), event);
891 if (!internal_editing()) {
892 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::RangeSelectModifier)) {
893 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionExtend), event);
895 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
905 if (internal_editing()) {
906 /* trim notes if we're in internal edit mode and near the ends of the note */
907 NoteBase* cn = reinterpret_cast<NoteBase*>(item->get_data ("notebase"));
909 if (cn && cn->big_enough_to_trim() && cn->mouse_near_ends()) {
910 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
912 _drags->set (new NoteDrag (this, item), event);
918 if (internal_editing()) {
919 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
920 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
934 if (internal_editing()) {
935 NoteBase* cn = reinterpret_cast<NoteBase*> (item->get_data ("notebase"));
937 if (cn->mouse_near_ends()) {
938 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
940 _drags->set (new NoteDrag (this, item), event);
950 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)) &&
951 event->type == GDK_BUTTON_PRESS) {
953 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
955 } else if (event->type == GDK_BUTTON_PRESS) {
958 case FadeInHandleItem:
960 RegionSelection s = get_equivalent_regions (selection->regions, Properties::select.property_id);
961 _drags->set (new FadeInDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), s), event, _cursors->fade_in);
965 case FadeOutHandleItem:
967 RegionSelection s = get_equivalent_regions (selection->regions, Properties::select.property_id);
968 _drags->set (new FadeOutDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), s), event, _cursors->fade_out);
972 case StartCrossFadeItem:
973 case EndCrossFadeItem:
974 /* we might allow user to grab inside the fade to trim a region with preserve_fade_anchor. for not this is not fully implemented */
975 // if (!clicked_regionview->region()->locked()) {
976 // RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
977 // _drags->set (new TrimDrag (this, item, clicked_regionview, s.by_layer(), true), event);
982 case FeatureLineItem:
984 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier)) {
985 remove_transient(item);
989 _drags->set (new FeatureLineDrag (this, item), event);
995 if (dynamic_cast<AutomationRegionView*> (clicked_regionview)) {
996 /* click on an automation region view; do nothing here and let the ARV's signal handler
1002 if (internal_editing ()) {
1003 if (event->type == GDK_2BUTTON_PRESS && event->button.button == 1) {
1004 Glib::RefPtr<Action> act = ActionManager::get_action (X_("MouseMode"), X_("toggle-internal-edit"));
1010 /* click on a normal region view */
1011 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
1012 add_region_copy_drag (item, event, clicked_regionview);
1013 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
1014 add_region_brush_drag (item, event, clicked_regionview);
1016 add_region_drag (item, event, clicked_regionview);
1020 // if (!internal_editing() && (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE && !selection->regions.empty())) {
1021 // _drags->add (new SelectionDrag (this, clicked_axisview->get_selection_rect (clicked_selection)->rect, SelectionDrag::SelectionMove));
1024 _drags->start_grab (event);
1028 case RegionViewNameHighlight:
1029 case LeftFrameHandle:
1030 case RightFrameHandle:
1031 if (!clicked_regionview->region()->locked()) {
1032 RegionSelection s = get_equivalent_regions (selection->regions, Properties::select.property_id);
1033 _drags->set (new TrimDrag (this, item, clicked_regionview, s.by_layer()), event);
1038 case RegionViewName:
1040 /* rename happens on edit clicks */
1041 RegionSelection s = get_equivalent_regions (selection->regions, Properties::select.property_id);
1042 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, s.by_layer()), event);
1047 case ControlPointItem:
1048 _drags->set (new ControlPointDrag (this, item), event);
1052 case AutomationLineItem:
1053 _drags->set (new LineDrag (this, item), event);
1058 if (internal_editing()) {
1059 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
1060 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
1064 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
1068 case AutomationTrackItem:
1070 TimeAxisView* parent = clicked_axisview->get_parent ();
1071 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (clicked_axisview);
1073 if (parent && dynamic_cast<MidiTimeAxisView*> (parent) && atv->show_regions ()) {
1075 RouteTimeAxisView* p = dynamic_cast<RouteTimeAxisView*> (parent);
1077 boost::shared_ptr<Playlist> pl = p->track()->playlist ();
1078 if (pl->n_regions() == 0) {
1079 /* Parent has no regions; create one so that we have somewhere to put automation */
1080 _drags->set (new RegionCreateDrag (this, item, parent), event);
1082 /* See if there's a region before the click that we can extend, and extend it if so */
1083 framepos_t const t = event_frame (event);
1084 boost::shared_ptr<Region> prev = pl->find_next_region (t, End, -1);
1086 _drags->set (new RegionCreateDrag (this, item, parent), event);
1088 prev->set_length (t - prev->position ());
1092 /* rubberband drag to select automation points */
1093 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
1100 if ( get_smart_mode() ) {
1101 /* we're in "smart" joined mode, and we've clicked on a Selection */
1102 double const y = event->button.y + vertical_adjustment.get_value();
1103 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
1105 /* if we're over an automation track, start a drag of its data */
1106 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
1108 _drags->set (new AutomationRangeDrag (this, atv, selection->time), event, _cursors->up_down);
1111 /* if we're over a track and a region, and in the `object' part of a region,
1112 put a selection around the region and drag both
1114 /* RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1115 if (rtv && _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
1116 boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track> (rtv->route ());
1118 boost::shared_ptr<Playlist> pl = t->playlist ();
1121 boost::shared_ptr<Region> r = pl->top_region_at (event_frame (event));
1123 RegionView* rv = rtv->view()->find_view (r);
1124 clicked_selection = select_range (rv->region()->position(),
1125 rv->region()->last_frame()+1);
1126 _drags->add (new SelectionDrag (this, item, SelectionDrag::SelectionMove));
1127 list<RegionView*> rvs;
1129 _drags->add (new RegionMoveDrag (this, item, rv, rvs, false, false));
1130 _drags->start_grab (event);
1143 case ImageFrameHandleStartItem:
1144 imageframe_start_handle_op(item, event) ;
1147 case ImageFrameHandleEndItem:
1148 imageframe_end_handle_op(item, event) ;
1151 case MarkerViewHandleStartItem:
1152 markerview_item_start_handle_op(item, event) ;
1155 case MarkerViewHandleEndItem:
1156 markerview_item_end_handle_op(item, event) ;
1159 case MarkerViewItem:
1160 start_markerview_grab(item, event) ;
1162 case ImageFrameItem:
1163 start_imageframe_grab(item, event) ;
1179 switch (item_type) {
1181 _drags->set (new LineDrag (this, item), event);
1184 case ControlPointItem:
1185 _drags->set (new ControlPointDrag (this, item), event);
1191 AudioRegionView* arv = dynamic_cast<AudioRegionView *> (clicked_regionview);
1193 _drags->set (new AutomationRangeDrag (this, arv, selection->time), event, _cursors->up_down);
1194 _drags->start_grab (event);
1200 case AutomationLineItem:
1201 _drags->set (new LineDrag (this, item), event);
1211 if (event->type == GDK_BUTTON_PRESS) {
1212 _drags->set (new MouseZoomDrag (this, item), event);
1219 if (internal_editing() && item_type == NoteItem) {
1220 /* drag notes if we're in internal edit mode */
1221 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
1223 } else if (clicked_regionview) {
1225 _drags->set (new TimeFXDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1231 _drags->set (new ScrubDrag (this, item), event);
1232 scrub_reversals = 0;
1233 scrub_reverse_distance = 0;
1234 last_scrub_x = event->button.x;
1235 scrubbing_direction = 0;
1236 set_canvas_cursor (_cursors->transparent);
1248 Editor::button_press_handler_2 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1250 Editing::MouseMode const eff = effective_mouse_mode ();
1253 switch (item_type) {
1255 if (internal_editing ()) {
1256 /* no region drags in internal edit mode */
1260 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
1261 add_region_copy_drag (item, event, clicked_regionview);
1263 add_region_drag (item, event, clicked_regionview);
1265 _drags->start_grab (event);
1268 case ControlPointItem:
1269 _drags->set (new ControlPointDrag (this, item), event);
1277 switch (item_type) {
1278 case RegionViewNameHighlight:
1279 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1283 case LeftFrameHandle:
1284 case RightFrameHandle:
1285 if (!internal_editing ()) {
1286 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1291 case RegionViewName:
1292 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer()), event);
1306 /* relax till release */
1312 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1313 temporal_zoom_to_frame (false, event_frame (event));
1315 temporal_zoom_to_frame (true, event_frame(event));
1328 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1330 if (event->type != GDK_BUTTON_PRESS) {
1334 Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->_track_canvas_viewport->get_window();
1336 if (canvas_window) {
1337 Glib::RefPtr<const Gdk::Window> pointer_window;
1340 Gdk::ModifierType mask;
1342 pointer_window = canvas_window->get_pointer (x, y, mask);
1344 if (pointer_window == _track_canvas->get_window()) {
1345 _track_canvas_viewport->window_to_canvas (x, y, wx, wy);
1349 pre_press_cursor = current_canvas_cursor;
1351 _track_canvas->grab_focus();
1353 if (_session && _session->actively_recording()) {
1357 if (internal_editing()) {
1358 bool leave_internal_edit_mode = false;
1360 switch (item_type) {
1365 if (!dynamic_cast<MidiRegionView*> (clicked_regionview) && !dynamic_cast<AutomationRegionView*> (clicked_regionview)) {
1366 leave_internal_edit_mode = true;
1370 case PlayheadCursorItem:
1372 case TempoMarkerItem:
1373 case MeterMarkerItem:
1377 case RangeMarkerBarItem:
1378 case CdMarkerBarItem:
1379 case TransportMarkerBarItem:
1381 /* button press on these events never does anything to
1382 change the editing mode.
1390 if (leave_internal_edit_mode) {
1391 ActionManager::do_action ("MouseMode", "toggle-internal-edit");
1395 button_selection (item, event, item_type);
1397 if (!_drags->active () &&
1398 (Keyboard::is_delete_event (&event->button) ||
1399 Keyboard::is_context_menu_event (&event->button) ||
1400 Keyboard::is_edit_event (&event->button))) {
1402 /* handled by button release */
1406 //not rolling, range mode click + join_play_range : locate the PH here
1407 if ( !_drags->active () && !_session->transport_rolling() && ( effective_mouse_mode() == MouseRange ) && Config->get_always_play_range() ) {
1408 framepos_t where = event_frame (event, 0, 0);
1410 _session->request_locate (where, false);
1413 switch (event->button.button) {
1415 return button_press_handler_1 (item, event, item_type);
1419 return button_press_handler_2 (item, event, item_type);
1426 return button_press_dispatch (&event->button);
1435 Editor::button_press_dispatch (GdkEventButton* ev)
1437 /* this function is intended only for buttons 4 and above.
1440 Gtkmm2ext::MouseButton b (ev->state, ev->button);
1441 return button_bindings->activate (b, Gtkmm2ext::Bindings::Press);
1445 Editor::button_release_dispatch (GdkEventButton* ev)
1447 /* this function is intended only for buttons 4 and above.
1450 Gtkmm2ext::MouseButton b (ev->state, ev->button);
1451 return button_bindings->activate (b, Gtkmm2ext::Bindings::Release);
1455 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1457 framepos_t where = event_frame (event, 0, 0);
1458 AutomationTimeAxisView* atv = 0;
1460 if (pre_press_cursor) {
1461 set_canvas_cursor (pre_press_cursor);
1462 pre_press_cursor = 0;
1465 /* no action if we're recording */
1467 if (_session && _session->actively_recording()) {
1471 /* see if we're finishing a drag */
1473 bool were_dragging = false;
1474 if (_drags->active ()) {
1475 bool const r = _drags->end_grab (event);
1477 /* grab dragged, so do nothing else */
1481 were_dragging = true;
1484 update_region_layering_order_editor ();
1486 /* edit events get handled here */
1488 if (!_drags->active () && Keyboard::is_edit_event (&event->button)) {
1489 switch (item_type) {
1491 show_region_properties ();
1494 case TempoMarkerItem:
1495 edit_tempo_marker (item);
1498 case MeterMarkerItem:
1499 edit_meter_marker (item);
1502 case RegionViewName:
1503 if (clicked_regionview->name_active()) {
1504 return mouse_rename_region (item, event);
1508 case ControlPointItem:
1509 edit_control_point (item);
1514 NoteBase* e = reinterpret_cast<NoteBase*> (item->get_data ("notebase"));
1516 edit_notes (e->region_view().selection ());
1526 /* context menu events get handled here */
1527 if (Keyboard::is_context_menu_event (&event->button)) {
1529 context_click_event = *event;
1531 if (!_drags->active ()) {
1533 /* no matter which button pops up the context menu, tell the menu
1534 widget to use button 1 to drive menu selection.
1537 switch (item_type) {
1539 case FadeInHandleItem:
1541 case FadeOutHandleItem:
1542 popup_fade_context_menu (1, event->button.time, item, item_type);
1545 case StartCrossFadeItem:
1546 popup_xfade_in_context_menu (1, event->button.time, item, item_type);
1549 case EndCrossFadeItem:
1550 popup_xfade_out_context_menu (1, event->button.time, item, item_type);
1554 popup_track_context_menu (1, event->button.time, item_type, false);
1558 case RegionViewNameHighlight:
1559 case LeftFrameHandle:
1560 case RightFrameHandle:
1561 case RegionViewName:
1562 popup_track_context_menu (1, event->button.time, item_type, false);
1566 popup_track_context_menu (1, event->button.time, item_type, true);
1569 case AutomationTrackItem:
1570 popup_track_context_menu (1, event->button.time, item_type, false);
1574 case RangeMarkerBarItem:
1575 case TransportMarkerBarItem:
1576 case CdMarkerBarItem:
1580 popup_ruler_menu (where, item_type);
1584 marker_context_menu (&event->button, item);
1587 case TempoMarkerItem:
1588 tempo_or_meter_marker_context_menu (&event->button, item);
1591 case MeterMarkerItem:
1592 tempo_or_meter_marker_context_menu (&event->button, item);
1595 case CrossfadeViewItem:
1596 popup_track_context_menu (1, event->button.time, item_type, false);
1599 case ControlPointItem:
1600 popup_control_point_context_menu (item, event);
1604 case ImageFrameItem:
1605 popup_imageframe_edit_menu(1, event->button.time, item, true) ;
1607 case ImageFrameTimeAxisItem:
1608 popup_imageframe_edit_menu(1, event->button.time, item, false) ;
1610 case MarkerViewItem:
1611 popup_marker_time_axis_edit_menu(1, event->button.time, item, true) ;
1613 case MarkerTimeAxisItem:
1614 popup_marker_time_axis_edit_menu(1, event->button.time, item, false) ;
1626 /* delete events get handled here */
1628 Editing::MouseMode const eff = effective_mouse_mode ();
1630 if (!_drags->active () && Keyboard::is_delete_event (&event->button)) {
1632 switch (item_type) {
1633 case TempoMarkerItem:
1634 remove_tempo_marker (item);
1637 case MeterMarkerItem:
1638 remove_meter_marker (item);
1642 remove_marker (*item, event);
1646 if (eff == MouseObject) {
1647 remove_clicked_region ();
1651 case ControlPointItem:
1652 remove_control_point (item);
1656 remove_midi_note (item, event);
1665 switch (event->button.button) {
1668 switch (item_type) {
1669 /* see comments in button_press_handler */
1670 case PlayheadCursorItem:
1673 case AutomationLineItem:
1674 case StartSelectionTrimItem:
1675 case EndSelectionTrimItem:
1679 if (!_dragging_playhead) {
1680 snap_to_with_modifier (where, event, 0, true);
1681 mouse_add_new_marker (where);
1685 case CdMarkerBarItem:
1686 if (!_dragging_playhead) {
1687 // if we get here then a dragged range wasn't done
1688 snap_to_with_modifier (where, event, 0, true);
1689 mouse_add_new_marker (where, true);
1694 if (!_dragging_playhead) {
1695 snap_to_with_modifier (where, event);
1696 mouse_add_new_tempo_event (where);
1701 if (!_dragging_playhead) {
1702 mouse_add_new_meter_event (pixel_to_frame (event->button.x));
1713 switch (item_type) {
1714 case AutomationTrackItem:
1715 atv = dynamic_cast<AutomationTimeAxisView*>(clicked_axisview);
1717 atv->add_automation_event (event, where, event->button.y);
1727 switch (item_type) {
1730 /* check that we didn't drag before releasing, since
1731 its really annoying to create new control
1732 points when doing this.
1734 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (clicked_regionview);
1735 if (!were_dragging && arv) {
1736 arv->add_gain_point_event (item, event);
1742 case AutomationTrackItem:
1743 dynamic_cast<AutomationTimeAxisView*>(clicked_axisview)->
1744 add_automation_event (event, where, event->button.y);
1753 set_canvas_cursor (current_canvas_cursor);
1754 if (scrubbing_direction == 0) {
1755 /* no drag, just a click */
1756 switch (item_type) {
1758 play_selected_region ();
1764 /* make sure we stop */
1765 _session->request_transport_speed (0.0);
1774 /* do any (de)selection operations that should occur on button release */
1775 button_selection (item, event, item_type);
1784 switch (item_type) {
1786 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1788 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier|Keyboard::SecondaryModifier))) {
1791 // Button2 click is unused
1806 // x_style_paste (where, 1.0);
1827 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1834 switch (item_type) {
1835 case ControlPointItem:
1836 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1837 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1838 cp->set_visible (true);
1842 at_y = cp->get_y ();
1843 cp->i2w (at_x, at_y);
1847 fraction = 1.0 - (cp->get_y() / cp->line().height());
1849 if (is_drawable() && !_drags->active ()) {
1850 set_canvas_cursor (_cursors->fader);
1853 _verbose_cursor->set (cp->line().get_verbose_cursor_string (fraction), at_x, at_y);
1854 _verbose_cursor->show ();
1859 if (mouse_mode == MouseGain) {
1860 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1862 line->set_outline_color (ARDOUR_UI::config()->canvasvar_EnteredGainLine.get());
1864 if (is_drawable()) {
1865 set_canvas_cursor (_cursors->fader);
1870 case AutomationLineItem:
1871 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1872 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1874 line->set_outline_color (ARDOUR_UI::config()->canvasvar_EnteredAutomationLine.get());
1876 if (is_drawable()) {
1877 set_canvas_cursor (_cursors->fader);
1882 case RegionViewNameHighlight:
1883 if (is_drawable() && effective_mouse_mode() == MouseObject && entered_regionview) {
1884 set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1885 _over_region_trim_target = true;
1889 case LeftFrameHandle:
1890 case RightFrameHandle:
1891 if (is_drawable() && effective_mouse_mode() == MouseObject && !internal_editing() && entered_regionview) {
1892 set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1896 case StartSelectionTrimItem:
1898 case ImageFrameHandleStartItem:
1899 case MarkerViewHandleStartItem:
1901 if (is_drawable()) {
1902 set_canvas_cursor (_cursors->left_side_trim);
1905 case EndSelectionTrimItem:
1907 case ImageFrameHandleEndItem:
1908 case MarkerViewHandleEndItem:
1910 if (is_drawable()) {
1911 set_canvas_cursor (_cursors->right_side_trim);
1915 case PlayheadCursorItem:
1916 if (is_drawable()) {
1917 switch (_edit_point) {
1919 set_canvas_cursor (_cursors->grabber_edit_point);
1922 set_canvas_cursor (_cursors->grabber);
1928 case RegionViewName:
1930 /* when the name is not an active item, the entire name highlight is for trimming */
1932 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1933 if (mouse_mode == MouseObject && is_drawable()) {
1934 set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1935 _over_region_trim_target = true;
1941 case AutomationTrackItem:
1942 if (is_drawable()) {
1943 Gdk::Cursor *cursor;
1944 switch (mouse_mode) {
1946 cursor = _cursors->selector;
1949 cursor = _cursors->zoom_in;
1952 cursor = _cursors->cross_hair;
1956 set_canvas_cursor (cursor);
1958 AutomationTimeAxisView* atv;
1959 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1960 clear_entered_track = false;
1961 set_entered_track (atv);
1967 case RangeMarkerBarItem:
1968 case TransportMarkerBarItem:
1969 case CdMarkerBarItem:
1972 if (is_drawable()) {
1973 set_canvas_cursor (_cursors->timebar);
1978 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1981 entered_marker = marker;
1982 marker->set_color_rgba (ARDOUR_UI::config()->canvasvar_EnteredMarker.get());
1984 case MeterMarkerItem:
1985 case TempoMarkerItem:
1986 if (is_drawable()) {
1987 set_canvas_cursor (_cursors->timebar);
1991 case FadeInHandleItem:
1992 if (mouse_mode == MouseObject && !internal_editing()) {
1993 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
1995 rect->set_fill_color (0xBBBBBBAA);
1997 set_canvas_cursor (_cursors->fade_in);
2001 case FadeOutHandleItem:
2002 if (mouse_mode == MouseObject && !internal_editing()) {
2003 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
2005 rect->set_fill_color (0xBBBBBBAA);
2007 set_canvas_cursor (_cursors->fade_out);
2010 case FeatureLineItem:
2012 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
2013 line->set_outline_color (0xFF0000FF);
2018 if ( get_smart_mode() ) {
2019 set_canvas_cursor ();
2027 /* second pass to handle entered track status in a comprehensible way.
2030 switch (item_type) {
2032 case AutomationLineItem:
2033 case ControlPointItem:
2034 /* these do not affect the current entered track state */
2035 clear_entered_track = false;
2038 case AutomationTrackItem:
2039 /* handled above already */
2043 set_entered_track (0);
2051 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent*, ItemType item_type)
2061 switch (item_type) {
2062 case ControlPointItem:
2063 cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
2064 if (cp->line().the_list()->interpolation() != AutomationList::Discrete) {
2065 if (cp->line().npoints() > 1 && !cp->get_selected()) {
2066 cp->set_visible (false);
2070 if (is_drawable()) {
2071 set_canvas_cursor (current_canvas_cursor);
2074 _verbose_cursor->hide ();
2077 case RegionViewNameHighlight:
2078 case LeftFrameHandle:
2079 case RightFrameHandle:
2080 case StartSelectionTrimItem:
2081 case EndSelectionTrimItem:
2082 case PlayheadCursorItem:
2085 case ImageFrameHandleStartItem:
2086 case ImageFrameHandleEndItem:
2087 case MarkerViewHandleStartItem:
2088 case MarkerViewHandleEndItem:
2091 _over_region_trim_target = false;
2093 if (is_drawable()) {
2094 set_canvas_cursor (current_canvas_cursor);
2099 case AutomationLineItem:
2100 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
2102 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
2104 line->set_outline_color (al->get_line_color());
2107 if (is_drawable()) {
2108 set_canvas_cursor (current_canvas_cursor);
2112 case RegionViewName:
2113 /* see enter_handler() for notes */
2114 _over_region_trim_target = false;
2116 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
2117 if (is_drawable() && mouse_mode == MouseObject) {
2118 set_canvas_cursor (current_canvas_cursor);
2123 case RangeMarkerBarItem:
2124 case TransportMarkerBarItem:
2125 case CdMarkerBarItem:
2129 if (is_drawable()) {
2130 set_canvas_cursor (current_canvas_cursor);
2135 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2139 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
2140 location_flags_changed (loc, this);
2143 case MeterMarkerItem:
2144 case TempoMarkerItem:
2146 if (is_drawable()) {
2147 set_canvas_cursor (current_canvas_cursor);
2152 case FadeInHandleItem:
2153 case FadeOutHandleItem:
2154 rv = static_cast<RegionView*>(item->get_data ("regionview"));
2156 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
2158 rect->set_fill_color (rv->get_fill_color());
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()->canvasvar_ZeroLine.get());
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);
2378 d.set_position (Gtk::WIN_POS_MOUSE);
2381 if (d.run () != RESPONSE_ACCEPT) {
2385 p->line().modify_point_y (*p, d.get_y_fraction ());
2389 Editor::edit_notes (MidiRegionView::Selection const & s)
2395 EditNoteDialog d (&(*s.begin())->region_view(), s);
2396 d.set_position (Gtk::WIN_POS_MOUSE);
2404 Editor::visible_order_range (int* low, int* high) const
2406 *low = TimeAxisView::max_order ();
2409 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
2411 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
2413 if (!rtv->hidden()) {
2415 if (*high < rtv->order()) {
2416 *high = rtv->order ();
2419 if (*low > rtv->order()) {
2420 *low = rtv->order ();
2427 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
2429 /* Either add to or set the set the region selection, unless
2430 this is an alignment click (control used)
2433 if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
2434 TimeAxisView* tv = &rv.get_time_axis_view();
2435 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
2437 if (rtv && rtv->is_track()) {
2438 speed = rtv->track()->speed();
2441 framepos_t where = get_preferred_edit_position();
2445 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
2447 align_region (rv.region(), SyncPoint, (framepos_t) (where * speed));
2449 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
2451 align_region (rv.region(), End, (framepos_t) (where * speed));
2455 align_region (rv.region(), Start, (framepos_t) (where * speed));
2462 Editor::collect_new_region_view (RegionView* rv)
2464 latest_regionviews.push_back (rv);
2468 Editor::collect_and_select_new_region_view (RegionView* rv)
2471 latest_regionviews.push_back (rv);
2475 Editor::cancel_selection ()
2477 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2478 (*i)->hide_selection ();
2481 selection->clear ();
2482 clicked_selection = 0;
2486 Editor::cancel_time_selection ()
2488 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2489 (*i)->hide_selection ();
2491 selection->time.clear ();
2492 clicked_selection = 0;
2496 Editor::point_trim (GdkEvent* event, framepos_t new_bound)
2498 RegionView* rv = clicked_regionview;
2500 /* Choose action dependant on which button was pressed */
2501 switch (event->button.button) {
2503 begin_reversible_command (_("start point trim"));
2505 if (selection->selected (rv)) {
2506 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
2507 i != selection->regions.by_layer().end(); ++i)
2509 if (!(*i)->region()->locked()) {
2510 (*i)->region()->clear_changes ();
2511 (*i)->region()->trim_front (new_bound);
2512 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2517 if (!rv->region()->locked()) {
2518 rv->region()->clear_changes ();
2519 rv->region()->trim_front (new_bound);
2520 _session->add_command(new StatefulDiffCommand (rv->region()));
2524 commit_reversible_command();
2528 begin_reversible_command (_("End point trim"));
2530 if (selection->selected (rv)) {
2532 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
2534 if (!(*i)->region()->locked()) {
2535 (*i)->region()->clear_changes();
2536 (*i)->region()->trim_end (new_bound);
2537 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2543 if (!rv->region()->locked()) {
2544 rv->region()->clear_changes ();
2545 rv->region()->trim_end (new_bound);
2546 _session->add_command (new StatefulDiffCommand (rv->region()));
2550 commit_reversible_command();
2559 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* /*event*/)
2564 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2565 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2569 Location* location = find_location_from_marker (marker, is_start);
2570 location->set_hidden (true, this);
2575 Editor::reposition_zoom_rect (framepos_t start, framepos_t end)
2577 double x1 = frame_to_pixel (start);
2578 double x2 = frame_to_pixel (end);
2579 double y2 = _full_canvas_height - 1.0;
2581 zoom_rect->set (ArdourCanvas::Rect (x1, 1.0, x2, y2));
2586 Editor::mouse_rename_region (ArdourCanvas::Item* /*item*/, GdkEvent* /*event*/)
2588 using namespace Gtkmm2ext;
2590 ArdourPrompter prompter (false);
2592 prompter.set_prompt (_("Name for region:"));
2593 prompter.set_initial_text (clicked_regionview->region()->name());
2594 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
2595 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
2596 prompter.show_all ();
2597 switch (prompter.run ()) {
2598 case Gtk::RESPONSE_ACCEPT:
2600 prompter.get_result(str);
2602 clicked_regionview->region()->set_name (str);
2611 Editor::mouse_brush_insert_region (RegionView* rv, framepos_t pos)
2613 /* no brushing without a useful snap setting */
2615 switch (_snap_mode) {
2617 return; /* can't work because it allows region to be placed anywhere */
2622 switch (_snap_type) {
2630 /* don't brush a copy over the original */
2632 if (pos == rv->region()->position()) {
2636 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
2638 if (rtv == 0 || !rtv->is_track()) {
2642 boost::shared_ptr<Playlist> playlist = rtv->playlist();
2643 double speed = rtv->track()->speed();
2645 playlist->clear_changes ();
2646 boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region(), true));
2647 playlist->add_region (new_region, (framepos_t) (pos * speed));
2648 _session->add_command (new StatefulDiffCommand (playlist));
2650 // playlist is frozen, so we have to update manually XXX this is disgusting
2652 playlist->RegionAdded (new_region); /* EMIT SIGNAL */
2656 Editor::track_height_step_timeout ()
2658 if (get_microseconds() - last_track_height_step_timestamp < 250000) {
2659 current_stepping_trackview = 0;
2666 Editor::add_region_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2668 assert (region_view);
2670 if (!region_view->region()->playlist()) {
2674 _region_motion_group->raise_to_top ();
2676 if (Config->get_edit_mode() == Splice) {
2677 _drags->add (new RegionSpliceDrag (this, item, region_view, selection->regions.by_layer()));
2679 RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::select.property_id);
2680 _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), false, false));
2683 /* sync the canvas to what we think is its current state */
2684 update_canvas_now();
2688 Editor::add_region_copy_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2690 assert (region_view);
2692 if (!region_view->region()->playlist()) {
2696 _region_motion_group->raise_to_top ();
2698 RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::select.property_id);
2699 _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), false, true));
2703 Editor::add_region_brush_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2705 assert (region_view);
2707 if (!region_view->region()->playlist()) {
2711 if (Config->get_edit_mode() == Splice) {
2715 RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::select.property_id);
2716 _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), true, false));
2718 begin_reversible_command (Operations::drag_region_brush);
2721 /** Start a grab where a time range is selected, track(s) are selected, and the
2722 * user clicks and drags a region with a modifier in order to create a new region containing
2723 * the section of the clicked region that lies within the time range.
2726 Editor::start_selection_grab (ArdourCanvas::Item* /*item*/, GdkEvent* event)
2728 if (clicked_regionview == 0) {
2732 /* lets try to create new Region for the selection */
2734 vector<boost::shared_ptr<Region> > new_regions;
2735 create_region_from_selection (new_regions);
2737 if (new_regions.empty()) {
2741 /* XXX fix me one day to use all new regions */
2743 boost::shared_ptr<Region> region (new_regions.front());
2745 /* add it to the current stream/playlist.
2747 tricky: the streamview for the track will add a new regionview. we will
2748 catch the signal it sends when it creates the regionview to
2749 set the regionview we want to then drag.
2752 latest_regionviews.clear();
2753 sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
2755 /* A selection grab currently creates two undo/redo operations, one for
2756 creating the new region and another for moving it.
2759 begin_reversible_command (Operations::selection_grab);
2761 boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
2763 playlist->clear_changes ();
2764 clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
2765 _session->add_command(new StatefulDiffCommand (playlist));
2767 commit_reversible_command ();
2771 if (latest_regionviews.empty()) {
2772 /* something went wrong */
2776 /* we need to deselect all other regionviews, and select this one
2777 i'm ignoring undo stuff, because the region creation will take care of it
2779 selection->set (latest_regionviews);
2781 _drags->set (new RegionMoveDrag (this, latest_regionviews.front()->get_canvas_group(), latest_regionviews.front(), latest_regionviews, false, false), event);
2787 if (_drags->active ()) {
2790 selection->clear ();
2795 Editor::set_internal_edit (bool yn)
2797 if (_internal_editing == yn) {
2801 _internal_editing = yn;
2804 pre_internal_mouse_mode = mouse_mode;
2805 pre_internal_snap_type = _snap_type;
2806 pre_internal_snap_mode = _snap_mode;
2808 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2809 (*i)->enter_internal_edit_mode ();
2812 set_snap_to (internal_snap_type);
2813 set_snap_mode (internal_snap_mode);
2817 internal_snap_mode = _snap_mode;
2818 internal_snap_type = _snap_type;
2820 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2821 (*i)->leave_internal_edit_mode ();
2824 if (mouse_mode == MouseDraw && pre_internal_mouse_mode != MouseDraw) {
2825 /* we were drawing .. flip back to something sensible */
2826 set_mouse_mode (pre_internal_mouse_mode);
2829 set_snap_to (pre_internal_snap_type);
2830 set_snap_mode (pre_internal_snap_mode);
2833 set_canvas_cursor ();
2836 /** Update _join_object_range_state which indicate whether we are over the top or bottom half of a region view,
2837 * used by the `join object/range' tool mode.
2840 Editor::update_join_object_range_location (double /*x*/, double y)
2842 /* XXX: actually, this decides based on whether the mouse is in the top
2843 or bottom half of a the waveform part RouteTimeAxisView;
2845 Note that entered_{track,regionview} is not always setup (e.g. if
2846 the mouse is over a TimeSelection), and to get a Region
2847 that we're over requires searching the playlist.
2850 if ( !get_smart_mode() ) {
2851 _join_object_range_state = JOIN_OBJECT_RANGE_NONE;
2855 if (mouse_mode == MouseObject) {
2856 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2857 } else if (mouse_mode == MouseRange) {
2858 _join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
2861 /* XXX: maybe we should make entered_track work in all cases, rather than resorting to this */
2862 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y + vertical_adjustment.get_value());
2866 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
2871 rtv->canvas_display()->canvas_to_item (cx, cy);
2873 double const c = cy / (rtv->view()->child_height() - TimeAxisViewItem::NAME_HIGHLIGHT_SIZE);
2875 _join_object_range_state = c <= 0.5 ? JOIN_OBJECT_RANGE_RANGE : JOIN_OBJECT_RANGE_OBJECT;
2881 Editor::effective_mouse_mode () const
2883 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
2885 } else if (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE) {
2893 Editor::remove_midi_note (ArdourCanvas::Item* item, GdkEvent *)
2895 NoteBase* e = reinterpret_cast<NoteBase*> (item->get_data ("notebase"));
2898 e->region_view().delete_note (e->note ());
2902 Editor::set_canvas_cursor_for_region_view (double x, RegionView* rv)
2904 /* XXX: this check should not be necessary */
2911 ArdourCanvas::Group* g = rv->get_canvas_group ();
2912 ArdourCanvas::Group* p = g->parent ();
2914 /* Compute x in region view parent coordinates */
2916 p->canvas_to_item (x, dy);
2918 boost::optional<ArdourCanvas::Rect> item_bbox = g->bounding_box ();
2920 ArdourCanvas::Rect parent_bbox = g->item_to_parent (item_bbox.get ());
2922 /* Halfway across the region */
2923 double const h = (parent_bbox.x0 + parent_bbox.x1) / 2;
2925 Trimmable::CanTrim ct = rv->region()->can_trim ();
2927 if (ct & Trimmable::FrontTrimEarlier) {
2928 set_canvas_cursor (_cursors->left_side_trim);
2930 set_canvas_cursor (_cursors->left_side_trim_right_only);
2933 if (ct & Trimmable::EndTrimLater) {
2934 set_canvas_cursor (_cursors->right_side_trim);
2936 set_canvas_cursor (_cursors->right_side_trim_left_only);
2941 /** Obtain the pointer position in canvas coordinates */
2943 Editor::get_pointer_position (double& x, double& y) const
2946 _track_canvas->get_pointer (px, py);
2947 _track_canvas_viewport->window_to_canvas (px, py, x, y);