2 Copyright (C) 2000-2001 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
28 #include "pbd/error.h"
29 #include "pbd/enumwriter.h"
30 #include "pbd/memento_command.h"
31 #include "pbd/basename.h"
32 #include "pbd/stateful_diff_command.h"
34 #include "gtkmm2ext/bindings.h"
35 #include "gtkmm2ext/utils.h"
36 #include "gtkmm2ext/tearoff.h"
38 #include "ardour_ui.h"
40 #include "canvas-note.h"
42 #include "time_axis_view.h"
43 #include "audio_time_axis.h"
44 #include "audio_region_view.h"
45 #include "midi_region_view.h"
47 #include "streamview.h"
48 #include "region_gain_line.h"
49 #include "automation_time_axis.h"
50 #include "control_point.h"
53 #include "selection.h"
56 #include "rgb_macros.h"
57 #include "control_point_dialog.h"
58 #include "editor_drag.h"
59 #include "automation_region_view.h"
60 #include "edit_note_dialog.h"
61 #include "mouse_cursors.h"
62 #include "editor_cursors.h"
63 #include "verbose_cursor.h"
65 #include "ardour/types.h"
66 #include "ardour/profile.h"
67 #include "ardour/route.h"
68 #include "ardour/audio_track.h"
69 #include "ardour/playlist.h"
70 #include "ardour/audioplaylist.h"
71 #include "ardour/audioregion.h"
72 #include "ardour/midi_region.h"
73 #include "ardour/dB.h"
74 #include "ardour/utils.h"
75 #include "ardour/region_factory.h"
76 #include "ardour/source_factory.h"
77 #include "ardour/session.h"
78 #include "ardour/operations.h"
85 using namespace ARDOUR;
88 using namespace Editing;
89 using Gtkmm2ext::Keyboard;
92 Editor::mouse_frame (framepos_t& where, bool& in_track_canvas) const
96 Gdk::ModifierType mask;
97 Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->track_canvas->get_window();
98 Glib::RefPtr<const Gdk::Window> pointer_window;
100 if (!canvas_window) {
104 pointer_window = canvas_window->get_pointer (x, y, mask);
106 if (pointer_window == track_canvas->get_bin_window()) {
109 in_track_canvas = true;
112 in_track_canvas = false;
117 event.type = GDK_BUTTON_RELEASE;
121 where = event_frame (&event, 0, 0);
126 Editor::event_frame (GdkEvent const * event, double* pcx, double* pcy) const
140 switch (event->type) {
141 case GDK_BUTTON_RELEASE:
142 case GDK_BUTTON_PRESS:
143 case GDK_2BUTTON_PRESS:
144 case GDK_3BUTTON_PRESS:
145 *pcx = event->button.x;
146 *pcy = event->button.y;
147 _trackview_group->w2i(*pcx, *pcy);
149 case GDK_MOTION_NOTIFY:
150 *pcx = event->motion.x;
151 *pcy = event->motion.y;
152 _trackview_group->w2i(*pcx, *pcy);
154 case GDK_ENTER_NOTIFY:
155 case GDK_LEAVE_NOTIFY:
156 track_canvas->w2c(event->crossing.x, event->crossing.y, *pcx, *pcy);
159 case GDK_KEY_RELEASE:
160 // track_canvas->w2c(event->key.x, event->key.y, *pcx, *pcy);
163 warning << string_compose (_("Editor::event_frame() used on unhandled event type %1"), event->type) << endmsg;
167 /* note that pixel_to_frame() never returns less than zero, so even if the pixel
168 position is negative (as can be the case with motion events in particular),
169 the frame location is always positive.
172 return pixel_to_frame (*pcx);
176 Editor::which_grabber_cursor ()
178 Gdk::Cursor* c = _cursors->grabber;
180 if (_internal_editing) {
181 switch (mouse_mode) {
183 c = _cursors->midi_pencil;
187 c = _cursors->grabber_note;
191 c = _cursors->midi_resize;
200 switch (_edit_point) {
202 c = _cursors->grabber_edit_point;
205 boost::shared_ptr<Movable> m = _movable.lock();
206 if (m && m->locked()) {
207 c = _cursors->speaker;
217 Editor::set_current_trimmable (boost::shared_ptr<Trimmable> t)
219 boost::shared_ptr<Trimmable> st = _trimmable.lock();
221 if (!st || st == t) {
223 set_canvas_cursor ();
228 Editor::set_current_movable (boost::shared_ptr<Movable> m)
230 boost::shared_ptr<Movable> sm = _movable.lock();
232 if (!sm || sm != m) {
234 set_canvas_cursor ();
239 Editor::set_canvas_cursor ()
241 if (_internal_editing) {
243 switch (mouse_mode) {
245 current_canvas_cursor = _cursors->midi_pencil;
249 current_canvas_cursor = which_grabber_cursor();
253 current_canvas_cursor = _cursors->midi_resize;
262 switch (mouse_mode) {
264 current_canvas_cursor = _cursors->selector;
268 current_canvas_cursor = which_grabber_cursor();
272 /* shouldn't be possible, but just cover it anyway ... */
273 current_canvas_cursor = _cursors->midi_pencil;
277 current_canvas_cursor = _cursors->cross_hair;
281 if (Keyboard::the_keyboard().key_is_down (GDK_Control_L)) {
282 current_canvas_cursor = _cursors->zoom_out;
284 current_canvas_cursor = _cursors->zoom_in;
289 current_canvas_cursor = _cursors->time_fx; // just use playhead
293 current_canvas_cursor = _cursors->speaker;
298 switch (_join_object_range_state) {
299 case JOIN_OBJECT_RANGE_NONE:
301 case JOIN_OBJECT_RANGE_OBJECT:
302 current_canvas_cursor = which_grabber_cursor ();
304 case JOIN_OBJECT_RANGE_RANGE:
305 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 (join_object_range_button.get_active()) {
312 get_pointer_position (x, y);
313 ArdourCanvas::Item* i = track_canvas->get_item_at (x, y);
314 if (i && i->property_parent() && (*i->property_parent()).get_data (X_("timeselection"))) {
315 pair<TimeAxisView*, int> tvp = trackview_by_y_position (_last_motion_y + vertical_adjustment.get_value() - canvas_timebars_vsize);
316 if (dynamic_cast<AutomationTimeAxisView*> (tvp.first)) {
317 current_canvas_cursor = _cursors->up_down;
322 set_canvas_cursor (current_canvas_cursor, true);
326 Editor::set_mouse_mode (MouseMode m, bool force)
328 if (_drags->active ()) {
332 if (!force && m == mouse_mode) {
336 Glib::RefPtr<Action> act;
340 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-range"));
344 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-object"));
348 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-draw"));
352 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-gain"));
356 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-zoom"));
360 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-timefx"));
364 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-audition"));
370 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
373 /* go there and back to ensure that the toggled handler is called to set up mouse_mode */
374 tact->set_active (false);
375 tact->set_active (true);
377 MouseModeChanged (); /* EMIT SIGNAL */
381 Editor::mouse_mode_toggled (MouseMode m)
383 Glib::RefPtr<Action> act;
384 Glib::RefPtr<ToggleAction> tact;
388 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-range"));
392 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-object"));
396 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-draw"));
400 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-gain"));
404 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-zoom"));
408 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-timefx"));
412 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-audition"));
418 tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
421 if (!tact->get_active()) {
422 /* this was just the notification that the old mode has been
423 * left. we'll get called again with the new mode active in a
431 act = ActionManager::get_action (X_("MouseMode"), X_("toggle-internal-edit"));
432 tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
433 tact->set_active (true);
443 if (!internal_editing()) {
444 if (mouse_mode != MouseRange && _join_object_range_state == JOIN_OBJECT_RANGE_NONE) {
446 /* in all modes except range and joined object/range, hide the range selection,
447 show the object (region) selection.
450 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
451 (*i)->hide_selection ();
457 in range or object/range mode, show the range selection.
460 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
461 (*i)->show_selection (selection->time);
466 set_canvas_cursor ();
468 MouseModeChanged (); /* EMIT SIGNAL */
472 Editor::step_mouse_mode (bool next)
474 switch (current_mouse_mode()) {
477 if (Profile->get_sae()) {
478 set_mouse_mode (MouseZoom);
480 set_mouse_mode (MouseRange);
483 set_mouse_mode (MouseTimeFX);
488 if (next) set_mouse_mode (MouseDraw);
489 else set_mouse_mode (MouseObject);
493 if (next) set_mouse_mode (MouseZoom);
494 else set_mouse_mode (MouseRange);
499 if (Profile->get_sae()) {
500 set_mouse_mode (MouseTimeFX);
502 set_mouse_mode (MouseGain);
505 if (Profile->get_sae()) {
506 set_mouse_mode (MouseObject);
508 set_mouse_mode (MouseDraw);
514 if (next) set_mouse_mode (MouseTimeFX);
515 else set_mouse_mode (MouseZoom);
520 set_mouse_mode (MouseAudition);
522 if (Profile->get_sae()) {
523 set_mouse_mode (MouseZoom);
525 set_mouse_mode (MouseGain);
531 if (next) set_mouse_mode (MouseObject);
532 else set_mouse_mode (MouseTimeFX);
538 Editor::button_selection (ArdourCanvas::Item* /*item*/, GdkEvent* event, ItemType item_type)
540 /* in object/audition/timefx/gain-automation mode,
541 any button press sets the selection if the object
542 can be selected. this is a bit of hack, because
543 we want to avoid this if the mouse operation is a
546 note: not dbl-click or triple-click
548 Also note that there is no region selection in internal edit mode, otherwise
549 for operations operating on the selection (e.g. cut) it is not obvious whether
550 to cut notes or regions.
553 if (((mouse_mode != MouseObject) &&
554 (_join_object_range_state != JOIN_OBJECT_RANGE_OBJECT) &&
555 (mouse_mode != MouseAudition || item_type != RegionItem) &&
556 (mouse_mode != MouseTimeFX || item_type != RegionItem) &&
557 (mouse_mode != MouseGain) &&
558 (mouse_mode != MouseRange) &&
559 (mouse_mode != MouseDraw)) ||
560 ((event->type != GDK_BUTTON_PRESS && event->type != GDK_BUTTON_RELEASE) || event->button.button > 3) ||
561 internal_editing()) {
566 if (event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) {
568 if ((event->button.state & Keyboard::RelevantModifierKeyMask) && event->button.button != 1) {
570 /* almost no selection action on modified button-2 or button-3 events */
572 if (item_type != RegionItem && event->button.button != 2) {
578 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
579 bool press = (event->type == GDK_BUTTON_PRESS);
583 if (mouse_mode != MouseRange || _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
584 set_selected_regionview_from_click (press, op, true);
586 selection->clear_tracks ();
587 set_selected_track_as_side_effect (op, true);
589 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT && !selection->regions.empty()) {
590 clicked_selection = select_range_around_region (selection->regions.front());
594 case RegionViewNameHighlight:
596 case LeftFrameHandle:
597 case RightFrameHandle:
598 if (mouse_mode != MouseRange || _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
599 set_selected_regionview_from_click (press, op, true);
600 } else if (event->type == GDK_BUTTON_PRESS) {
601 set_selected_track_as_side_effect (op);
606 case FadeInHandleItem:
608 case FadeOutHandleItem:
610 if (mouse_mode != MouseRange || _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
611 set_selected_regionview_from_click (press, op, true);
612 } else if (event->type == GDK_BUTTON_PRESS) {
613 set_selected_track_as_side_effect (op);
617 case ControlPointItem:
618 set_selected_track_as_side_effect (op, true);
619 if (mouse_mode != MouseRange || _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
620 set_selected_control_point_from_click (op, false);
625 /* for context click, select track */
626 if (event->button.button == 3) {
627 selection->clear_tracks ();
628 set_selected_track_as_side_effect (op, true);
632 case AutomationTrackItem:
633 set_selected_track_as_side_effect (op, true);
642 Editor::button_press_handler_1 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
644 /* single mouse clicks on any of these item types operate
645 independent of mouse mode, mostly because they are
646 not on the main track canvas or because we want
651 case PlayheadCursorItem:
652 _drags->set (new CursorDrag (this, item, true), event);
656 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
657 hide_marker (item, event);
659 _drags->set (new MarkerDrag (this, item), event);
663 case TempoMarkerItem:
665 TempoMarker* m = reinterpret_cast<TempoMarker*> (item->get_data ("marker"));
667 if (m->tempo().movable ()) {
669 new TempoMarkerDrag (
672 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
682 case MeterMarkerItem:
684 MeterMarker* m = reinterpret_cast<MeterMarker*> (item->get_data ("marker"));
686 if (m->meter().movable ()) {
688 new MeterMarkerDrag (
691 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
704 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
705 _drags->set (new CursorDrag (this, &playhead_cursor->canvas_item, false), event);
711 case RangeMarkerBarItem:
712 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
713 _drags->set (new CursorDrag (this, &playhead_cursor->canvas_item, false), event);
715 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateRangeMarker), event);
720 case CdMarkerBarItem:
721 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
722 _drags->set (new CursorDrag (this, &playhead_cursor->canvas_item, false), event);
724 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateCDMarker), event);
729 case TransportMarkerBarItem:
730 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
731 _drags->set (new CursorDrag (this, &playhead_cursor->canvas_item, false), event);
733 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateTransportMarker), event);
742 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
743 /* special case: allow trim of range selections in joined object mode;
744 in theory eff should equal MouseRange in this case, but it doesn't
745 because entering the range selection canvas item results in entered_regionview
746 being set to 0, so update_join_object_range_location acts as if we aren't
749 if (item_type == StartSelectionTrimItem) {
750 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
751 } else if (item_type == EndSelectionTrimItem) {
752 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
756 Editing::MouseMode eff = effective_mouse_mode ();
758 /* special case: allow drag of region fade in/out in object mode with join object/range enabled */
759 if (item_type == FadeInHandleItem || item_type == FadeOutHandleItem) {
766 case StartSelectionTrimItem:
767 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
770 case EndSelectionTrimItem:
771 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
775 if (Keyboard::modifier_state_contains
776 (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier))) {
777 // contains and not equals because I can't use alt as a modifier alone.
778 start_selection_grab (item, event);
779 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
780 /* grab selection for moving */
781 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionMove), event);
783 double const y = event->button.y + vertical_adjustment.get_value() - canvas_timebars_vsize;
784 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
786 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
787 if (join_object_range_button.get_active() && atv) {
788 /* smart "join" mode: drag automation */
789 _drags->set (new AutomationRangeDrag (this, atv->base_item(), selection->time), event, _cursors->up_down);
791 /* this was debated, but decided the more common action was to
792 make a new selection */
793 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
800 if (internal_editing()) {
801 /* trim notes if we're in internal edit mode and near the ends of the note */
802 ArdourCanvas::CanvasNote* cn = dynamic_cast<ArdourCanvas::CanvasNote*> (item);
803 if (cn && cn->big_enough_to_trim() && cn->mouse_near_ends()) {
804 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
806 _drags->set (new NoteDrag (this, item), event);
812 if (internal_editing()) {
813 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
814 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
818 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
823 case RegionViewNameHighlight:
824 if (!clicked_regionview->region()->locked()) {
825 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
826 _drags->set (new TrimDrag (this, item, clicked_regionview, s.by_layer()), event);
831 case LeftFrameHandle:
832 case RightFrameHandle:
833 if (!internal_editing() && !clicked_regionview->region()->locked()) {
834 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
835 _drags->set (new TrimDrag (this, item, clicked_regionview, s.by_layer()), event);
841 if (!internal_editing()) {
842 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
851 if (internal_editing()) {
852 ArdourCanvas::CanvasNoteEvent* cn = dynamic_cast<ArdourCanvas::CanvasNoteEvent*> (item);
853 if (cn->mouse_near_ends()) {
854 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
856 _drags->set (new NoteDrag (this, item), event);
866 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)) &&
867 event->type == GDK_BUTTON_PRESS) {
869 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
871 } else if (event->type == GDK_BUTTON_PRESS) {
874 case FadeInHandleItem:
876 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
877 _drags->set (new FadeInDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), s), event, _cursors->fade_in);
881 case FadeOutHandleItem:
883 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
884 _drags->set (new FadeOutDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), s), event, _cursors->fade_out);
888 case FeatureLineItem:
890 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier)) {
891 remove_transient(item);
895 _drags->set (new FeatureLineDrag (this, item), event);
901 if (dynamic_cast<AutomationRegionView*> (clicked_regionview)) {
902 /* click on an automation region view; do nothing here and let the ARV's signal handler
908 if (internal_editing ()) {
909 if (event->type == GDK_2BUTTON_PRESS && event->button.button == 1) {
910 Glib::RefPtr<Action> act = ActionManager::get_action (X_("MouseMode"), X_("toggle-internal-edit"));
916 /* click on a normal region view */
917 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
918 add_region_copy_drag (item, event, clicked_regionview);
919 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
920 add_region_brush_drag (item, event, clicked_regionview);
922 add_region_drag (item, event, clicked_regionview);
925 if (!internal_editing() && (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT && !selection->regions.empty())) {
926 _drags->add (new SelectionDrag (this, clicked_axisview->get_selection_rect (clicked_selection)->rect, SelectionDrag::SelectionMove));
929 _drags->start_grab (event);
932 case RegionViewNameHighlight:
933 case LeftFrameHandle:
934 case RightFrameHandle:
935 if (!clicked_regionview->region()->locked()) {
936 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
937 _drags->set (new TrimDrag (this, item, clicked_regionview, s.by_layer()), event);
944 /* rename happens on edit clicks */
945 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
946 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, s.by_layer()), event);
951 case ControlPointItem:
952 _drags->set (new ControlPointDrag (this, item), event);
956 case AutomationLineItem:
957 _drags->set (new LineDrag (this, item), event);
962 if (internal_editing()) {
963 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
964 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
968 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
972 case AutomationTrackItem:
974 TimeAxisView* parent = clicked_axisview->get_parent ();
975 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (clicked_axisview);
977 if (parent && dynamic_cast<MidiTimeAxisView*> (parent) && atv->show_regions ()) {
978 /* create a MIDI region so that we have somewhere to put automation */
979 _drags->set (new RegionCreateDrag (this, item, parent), event);
981 /* rubberband drag to select automation points */
982 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
989 if (join_object_range_button.get_active()) {
990 /* we're in "smart" joined mode, and we've clicked on a Selection */
991 double const y = event->button.y + vertical_adjustment.get_value() - canvas_timebars_vsize;
992 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
994 /* if we're over an automation track, start a drag of its data */
995 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
997 _drags->set (new AutomationRangeDrag (this, atv->base_item(), selection->time), event, _cursors->up_down);
1000 /* if we're over a track and a region, and in the `object' part of a region,
1001 put a selection around the region and drag both
1003 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1004 if (rtv && _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
1005 boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track> (rtv->route ());
1007 boost::shared_ptr<Playlist> pl = t->playlist ();
1010 boost::shared_ptr<Region> r = pl->top_region_at (event_frame (event));
1012 RegionView* rv = rtv->view()->find_view (r);
1013 clicked_selection = select_range_around_region (rv);
1014 _drags->add (new SelectionDrag (this, item, SelectionDrag::SelectionMove));
1015 list<RegionView*> rvs;
1017 _drags->add (new RegionMoveDrag (this, item, rv, rvs, false, false));
1018 _drags->start_grab (event);
1029 case ImageFrameHandleStartItem:
1030 imageframe_start_handle_op(item, event) ;
1033 case ImageFrameHandleEndItem:
1034 imageframe_end_handle_op(item, event) ;
1037 case MarkerViewHandleStartItem:
1038 markerview_item_start_handle_op(item, event) ;
1041 case MarkerViewHandleEndItem:
1042 markerview_item_end_handle_op(item, event) ;
1045 case MarkerViewItem:
1046 start_markerview_grab(item, event) ;
1048 case ImageFrameItem:
1049 start_imageframe_grab(item, event) ;
1065 switch (item_type) {
1067 /* start a grab so that if we finish after moving
1068 we can tell what happened.
1070 _drags->set (new RegionGainDrag (this, item), event, current_canvas_cursor);
1074 _drags->set (new LineDrag (this, item), event);
1077 case ControlPointItem:
1078 _drags->set (new ControlPointDrag (this, item), event);
1088 switch (item_type) {
1089 case ControlPointItem:
1090 _drags->set (new ControlPointDrag (this, item), event);
1093 case AutomationLineItem:
1094 _drags->set (new LineDrag (this, item), event);
1098 // XXX need automation mode to identify which
1100 // start_line_grab_from_regionview (item, event);
1110 if (event->type == GDK_BUTTON_PRESS) {
1111 _drags->set (new MouseZoomDrag (this, item), event);
1118 if (internal_editing() && item_type == NoteItem) {
1119 /* drag notes if we're in internal edit mode */
1120 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
1122 } else if ((!internal_editing() || dynamic_cast<AudioRegionView*> (clicked_regionview)) && clicked_regionview) {
1123 /* do time-FX if we're not in internal edit mode, or we are but we clicked on an audio region */
1124 _drags->set (new TimeFXDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1130 _drags->set (new ScrubDrag (this, item), event);
1131 scrub_reversals = 0;
1132 scrub_reverse_distance = 0;
1133 last_scrub_x = event->button.x;
1134 scrubbing_direction = 0;
1135 set_canvas_cursor (_cursors->transparent);
1147 Editor::button_press_handler_2 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1149 Editing::MouseMode const eff = effective_mouse_mode ();
1152 switch (item_type) {
1154 if (internal_editing ()) {
1155 /* no region drags in internal edit mode */
1159 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
1160 add_region_copy_drag (item, event, clicked_regionview);
1162 add_region_drag (item, event, clicked_regionview);
1164 _drags->start_grab (event);
1167 case ControlPointItem:
1168 _drags->set (new ControlPointDrag (this, item), event);
1176 switch (item_type) {
1177 case RegionViewNameHighlight:
1178 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1182 case LeftFrameHandle:
1183 case RightFrameHandle:
1184 if (!internal_editing ()) {
1185 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1190 case RegionViewName:
1191 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer()), event);
1205 /* relax till release */
1211 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1212 temporal_zoom_to_frame (false, event_frame (event));
1214 temporal_zoom_to_frame (true, event_frame(event));
1227 Editor::toggle_internal_editing_from_double_click (GdkEvent* event)
1229 if (_drags->active()) {
1230 _drags->end_grab (event);
1232 Glib::RefPtr<Action> act = ActionManager::get_action (X_("MouseMode"), X_("toggle-internal-edit"));
1235 /* prevent reversion of edit cursor on button release */
1237 pre_press_cursor = 0;
1243 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1245 if (event->type != GDK_BUTTON_PRESS) {
1249 Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->track_canvas->get_window();
1251 if (canvas_window) {
1252 Glib::RefPtr<const Gdk::Window> pointer_window;
1255 Gdk::ModifierType mask;
1257 pointer_window = canvas_window->get_pointer (x, y, mask);
1259 if (pointer_window == track_canvas->get_bin_window()) {
1260 track_canvas->window_to_world (x, y, wx, wy);
1264 pre_press_cursor = current_canvas_cursor;
1266 track_canvas->grab_focus();
1268 if (_session && _session->actively_recording()) {
1272 button_selection (item, event, item_type);
1274 if (!_drags->active () &&
1275 (Keyboard::is_delete_event (&event->button) ||
1276 Keyboard::is_context_menu_event (&event->button) ||
1277 Keyboard::is_edit_event (&event->button))) {
1279 /* handled by button release */
1283 switch (event->button.button) {
1285 return button_press_handler_1 (item, event, item_type);
1289 return button_press_handler_2 (item, event, item_type);
1296 return button_press_dispatch (&event->button);
1305 Editor::button_press_dispatch (GdkEventButton* ev)
1307 /* this function is intended only for buttons 4 and above.
1310 Gtkmm2ext::MouseButton b (ev->state, ev->button);
1311 return button_bindings->activate (b, Gtkmm2ext::Bindings::Press);
1315 Editor::button_release_dispatch (GdkEventButton* ev)
1317 /* this function is intended only for buttons 4 and above.
1320 Gtkmm2ext::MouseButton b (ev->state, ev->button);
1321 return button_bindings->activate (b, Gtkmm2ext::Bindings::Release);
1325 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1327 framepos_t where = event_frame (event, 0, 0);
1328 AutomationTimeAxisView* atv = 0;
1330 if (pre_press_cursor) {
1331 set_canvas_cursor (pre_press_cursor);
1332 pre_press_cursor = 0;
1335 /* no action if we're recording */
1337 if (_session && _session->actively_recording()) {
1341 /* see if we're finishing a drag */
1343 bool were_dragging = false;
1344 if (_drags->active ()) {
1345 bool const r = _drags->end_grab (event);
1347 /* grab dragged, so do nothing else */
1351 were_dragging = true;
1354 update_region_layering_order_editor ();
1356 /* edit events get handled here */
1358 if (!_drags->active () && Keyboard::is_edit_event (&event->button)) {
1359 switch (item_type) {
1361 show_region_properties ();
1364 case TempoMarkerItem:
1365 edit_tempo_marker (item);
1368 case MeterMarkerItem:
1369 edit_meter_marker (item);
1372 case RegionViewName:
1373 if (clicked_regionview->name_active()) {
1374 return mouse_rename_region (item, event);
1378 case ControlPointItem:
1379 edit_control_point (item);
1392 /* context menu events get handled here */
1394 if (Keyboard::is_context_menu_event (&event->button)) {
1396 context_click_event = *event;
1398 if (!_drags->active ()) {
1400 /* no matter which button pops up the context menu, tell the menu
1401 widget to use button 1 to drive menu selection.
1404 switch (item_type) {
1406 case FadeInHandleItem:
1408 case FadeOutHandleItem:
1409 popup_fade_context_menu (1, event->button.time, item, item_type);
1413 popup_track_context_menu (1, event->button.time, item_type, false);
1417 case RegionViewNameHighlight:
1418 case LeftFrameHandle:
1419 case RightFrameHandle:
1420 case RegionViewName:
1421 popup_track_context_menu (1, event->button.time, item_type, false);
1425 popup_track_context_menu (1, event->button.time, item_type, true);
1428 case AutomationTrackItem:
1429 popup_track_context_menu (1, event->button.time, item_type, false);
1433 case RangeMarkerBarItem:
1434 case TransportMarkerBarItem:
1435 case CdMarkerBarItem:
1438 popup_ruler_menu (where, item_type);
1442 marker_context_menu (&event->button, item);
1445 case TempoMarkerItem:
1446 tempo_or_meter_marker_context_menu (&event->button, item);
1449 case MeterMarkerItem:
1450 tempo_or_meter_marker_context_menu (&event->button, item);
1453 case CrossfadeViewItem:
1454 popup_track_context_menu (1, event->button.time, item_type, false);
1457 case ControlPointItem:
1458 popup_control_point_context_menu (item, event);
1462 case ImageFrameItem:
1463 popup_imageframe_edit_menu(1, event->button.time, item, true) ;
1465 case ImageFrameTimeAxisItem:
1466 popup_imageframe_edit_menu(1, event->button.time, item, false) ;
1468 case MarkerViewItem:
1469 popup_marker_time_axis_edit_menu(1, event->button.time, item, true) ;
1471 case MarkerTimeAxisItem:
1472 popup_marker_time_axis_edit_menu(1, event->button.time, item, false) ;
1484 /* delete events get handled here */
1486 Editing::MouseMode const eff = effective_mouse_mode ();
1488 if (!_drags->active () && Keyboard::is_delete_event (&event->button)) {
1490 switch (item_type) {
1491 case TempoMarkerItem:
1492 remove_tempo_marker (item);
1495 case MeterMarkerItem:
1496 remove_meter_marker (item);
1500 remove_marker (*item, event);
1504 if (eff == MouseObject) {
1505 remove_clicked_region ();
1509 case ControlPointItem:
1510 remove_control_point (item);
1514 remove_midi_note (item, event);
1523 switch (event->button.button) {
1526 switch (item_type) {
1527 /* see comments in button_press_handler */
1528 case PlayheadCursorItem:
1531 case AutomationLineItem:
1532 case StartSelectionTrimItem:
1533 case EndSelectionTrimItem:
1537 if (!_dragging_playhead) {
1538 snap_to_with_modifier (where, event, 0, true);
1539 mouse_add_new_marker (where);
1543 case CdMarkerBarItem:
1544 if (!_dragging_playhead) {
1545 // if we get here then a dragged range wasn't done
1546 snap_to_with_modifier (where, event, 0, true);
1547 mouse_add_new_marker (where, true);
1552 if (!_dragging_playhead) {
1553 snap_to_with_modifier (where, event);
1554 mouse_add_new_tempo_event (where);
1559 if (!_dragging_playhead) {
1560 mouse_add_new_meter_event (pixel_to_frame (event->button.x));
1571 switch (item_type) {
1572 case AutomationTrackItem:
1573 atv = dynamic_cast<AutomationTimeAxisView*>(clicked_axisview);
1575 atv->add_automation_event (event, where, event->button.y);
1586 switch (item_type) {
1589 /* check that we didn't drag before releasing, since
1590 its really annoying to create new control
1591 points when doing this.
1593 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (clicked_regionview);
1594 if (were_dragging && arv) {
1595 arv->add_gain_point_event (item, event);
1601 case AutomationTrackItem:
1602 dynamic_cast<AutomationTimeAxisView*>(clicked_axisview)->
1603 add_automation_event (event, where, event->button.y);
1612 set_canvas_cursor (current_canvas_cursor);
1613 if (scrubbing_direction == 0) {
1614 /* no drag, just a click */
1615 switch (item_type) {
1617 play_selected_region ();
1623 /* make sure we stop */
1624 _session->request_transport_speed (0.0);
1633 /* do any (de)selection operations that should occur on button release */
1634 button_selection (item, event, item_type);
1643 switch (item_type) {
1645 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1647 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier|Keyboard::SecondaryModifier))) {
1650 // Button2 click is unused
1665 // x_style_paste (where, 1.0);
1686 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1693 switch (item_type) {
1694 case ControlPointItem:
1695 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1696 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1697 cp->set_visible (true);
1701 at_y = cp->get_y ();
1702 cp->i2w (at_x, at_y);
1706 fraction = 1.0 - (cp->get_y() / cp->line().height());
1708 if (is_drawable() && !_drags->active ()) {
1709 set_canvas_cursor (_cursors->fader);
1712 _verbose_cursor->set (cp->line().get_verbose_cursor_string (fraction), at_x, at_y);
1713 _verbose_cursor->show ();
1718 if (mouse_mode == MouseGain) {
1719 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1721 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredGainLine.get();
1722 if (is_drawable()) {
1723 set_canvas_cursor (_cursors->fader);
1728 case AutomationLineItem:
1729 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1731 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1733 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredAutomationLine.get();
1735 if (is_drawable()) {
1736 set_canvas_cursor (_cursors->fader);
1741 case RegionViewNameHighlight:
1742 if (is_drawable() && mouse_mode == MouseObject && entered_regionview) {
1743 set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1744 _over_region_trim_target = true;
1748 case LeftFrameHandle:
1749 case RightFrameHandle:
1750 if (is_drawable() && mouse_mode == MouseObject && !internal_editing() && entered_regionview) {
1751 set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1755 case StartSelectionTrimItem:
1757 case ImageFrameHandleStartItem:
1758 case MarkerViewHandleStartItem:
1760 if (is_drawable()) {
1761 set_canvas_cursor (_cursors->left_side_trim);
1764 case EndSelectionTrimItem:
1766 case ImageFrameHandleEndItem:
1767 case MarkerViewHandleEndItem:
1769 if (is_drawable()) {
1770 set_canvas_cursor (_cursors->right_side_trim);
1774 case PlayheadCursorItem:
1775 if (is_drawable()) {
1776 switch (_edit_point) {
1778 set_canvas_cursor (_cursors->grabber_edit_point);
1781 set_canvas_cursor (_cursors->grabber);
1787 case RegionViewName:
1789 /* when the name is not an active item, the entire name highlight is for trimming */
1791 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1792 if (mouse_mode == MouseObject && is_drawable()) {
1793 set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1794 _over_region_trim_target = true;
1800 case AutomationTrackItem:
1801 if (is_drawable()) {
1802 Gdk::Cursor *cursor;
1803 switch (mouse_mode) {
1805 cursor = _cursors->selector;
1808 cursor = _cursors->zoom_in;
1811 cursor = _cursors->cross_hair;
1815 set_canvas_cursor (cursor);
1817 AutomationTimeAxisView* atv;
1818 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1819 clear_entered_track = false;
1820 set_entered_track (atv);
1826 case RangeMarkerBarItem:
1827 case TransportMarkerBarItem:
1828 case CdMarkerBarItem:
1831 if (is_drawable()) {
1832 set_canvas_cursor (_cursors->timebar);
1837 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1840 entered_marker = marker;
1841 marker->set_color_rgba (ARDOUR_UI::config()->canvasvar_EnteredMarker.get());
1843 case MeterMarkerItem:
1844 case TempoMarkerItem:
1845 if (is_drawable()) {
1846 set_canvas_cursor (_cursors->timebar);
1850 case FadeInHandleItem:
1851 if (mouse_mode == MouseObject && !internal_editing()) {
1852 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1854 rect->property_fill_color_rgba() = 0xBBBBBBAA;
1856 set_canvas_cursor (_cursors->fade_in);
1860 case FadeOutHandleItem:
1861 if (mouse_mode == MouseObject && !internal_editing()) {
1862 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1864 rect->property_fill_color_rgba() = 0xBBBBBBAA;
1866 set_canvas_cursor (_cursors->fade_out);
1869 case FeatureLineItem:
1871 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1872 line->property_fill_color_rgba() = 0xFF0000FF;
1876 if (join_object_range_button.get_active()) {
1877 set_canvas_cursor ();
1885 /* second pass to handle entered track status in a comprehensible way.
1888 switch (item_type) {
1890 case AutomationLineItem:
1891 case ControlPointItem:
1892 /* these do not affect the current entered track state */
1893 clear_entered_track = false;
1896 case AutomationTrackItem:
1897 /* handled above already */
1901 set_entered_track (0);
1909 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent*, ItemType item_type)
1919 switch (item_type) {
1920 case ControlPointItem:
1921 cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
1922 if (cp->line().the_list()->interpolation() != AutomationList::Discrete) {
1923 if (cp->line().npoints() > 1 && !cp->get_selected()) {
1924 cp->set_visible (false);
1928 if (is_drawable()) {
1929 set_canvas_cursor (current_canvas_cursor);
1932 _verbose_cursor->hide ();
1935 case RegionViewNameHighlight:
1936 case LeftFrameHandle:
1937 case RightFrameHandle:
1938 case StartSelectionTrimItem:
1939 case EndSelectionTrimItem:
1940 case PlayheadCursorItem:
1943 case ImageFrameHandleStartItem:
1944 case ImageFrameHandleEndItem:
1945 case MarkerViewHandleStartItem:
1946 case MarkerViewHandleEndItem:
1949 _over_region_trim_target = false;
1951 if (is_drawable()) {
1952 set_canvas_cursor (current_canvas_cursor);
1957 case AutomationLineItem:
1958 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1960 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1962 line->property_fill_color_rgba() = al->get_line_color();
1964 if (is_drawable()) {
1965 set_canvas_cursor (current_canvas_cursor);
1969 case RegionViewName:
1970 /* see enter_handler() for notes */
1971 _over_region_trim_target = false;
1973 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1974 if (is_drawable() && mouse_mode == MouseObject) {
1975 set_canvas_cursor (current_canvas_cursor);
1980 case RangeMarkerBarItem:
1981 case TransportMarkerBarItem:
1982 case CdMarkerBarItem:
1986 if (is_drawable()) {
1987 set_canvas_cursor (current_canvas_cursor);
1992 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1996 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
1997 location_flags_changed (loc, this);
2000 case MeterMarkerItem:
2001 case TempoMarkerItem:
2003 if (is_drawable()) {
2004 set_canvas_cursor (_cursors->timebar);
2009 case FadeInHandleItem:
2010 case FadeOutHandleItem:
2011 rv = static_cast<RegionView*>(item->get_data ("regionview"));
2013 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
2015 rect->property_fill_color_rgba() = rv->get_fill_color();
2016 rect->property_outline_pixels() = 0;
2019 set_canvas_cursor (current_canvas_cursor);
2022 case AutomationTrackItem:
2023 if (is_drawable()) {
2024 set_canvas_cursor (current_canvas_cursor);
2025 clear_entered_track = true;
2026 Glib::signal_idle().connect (sigc::mem_fun(*this, &Editor::left_automation_track));
2029 case FeatureLineItem:
2031 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
2032 line->property_fill_color_rgba() = (guint) ARDOUR_UI::config()->canvasvar_ZeroLine.get();;
2044 Editor::left_automation_track ()
2046 if (clear_entered_track) {
2047 set_entered_track (0);
2048 clear_entered_track = false;
2054 Editor::scrub (framepos_t frame, double current_x)
2058 if (scrubbing_direction == 0) {
2060 _session->request_locate (frame, false);
2061 _session->request_transport_speed (0.1);
2062 scrubbing_direction = 1;
2066 if (last_scrub_x > current_x) {
2068 /* pointer moved to the left */
2070 if (scrubbing_direction > 0) {
2072 /* we reversed direction to go backwards */
2075 scrub_reverse_distance += (int) (last_scrub_x - current_x);
2079 /* still moving to the left (backwards) */
2081 scrub_reversals = 0;
2082 scrub_reverse_distance = 0;
2084 delta = 0.01 * (last_scrub_x - current_x);
2085 _session->request_transport_speed_nonzero (_session->transport_speed() - delta);
2089 /* pointer moved to the right */
2091 if (scrubbing_direction < 0) {
2092 /* we reversed direction to go forward */
2095 scrub_reverse_distance += (int) (current_x - last_scrub_x);
2098 /* still moving to the right */
2100 scrub_reversals = 0;
2101 scrub_reverse_distance = 0;
2103 delta = 0.01 * (current_x - last_scrub_x);
2104 _session->request_transport_speed_nonzero (_session->transport_speed() + delta);
2108 /* if there have been more than 2 opposite motion moves detected, or one that moves
2109 back more than 10 pixels, reverse direction
2112 if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
2114 if (scrubbing_direction > 0) {
2115 /* was forwards, go backwards */
2116 _session->request_transport_speed (-0.1);
2117 scrubbing_direction = -1;
2119 /* was backwards, go forwards */
2120 _session->request_transport_speed (0.1);
2121 scrubbing_direction = 1;
2124 scrub_reverse_distance = 0;
2125 scrub_reversals = 0;
2129 last_scrub_x = current_x;
2133 Editor::motion_handler (ArdourCanvas::Item* /*item*/, GdkEvent* event, bool from_autoscroll)
2135 _last_motion_y = event->motion.y;
2137 if (event->motion.is_hint) {
2140 /* We call this so that MOTION_NOTIFY events continue to be
2141 delivered to the canvas. We need to do this because we set
2142 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
2143 the density of the events, at the expense of a round-trip
2144 to the server. Given that this will mostly occur on cases
2145 where DISPLAY = :0.0, and given the cost of what the motion
2146 event might do, its a good tradeoff.
2149 track_canvas->get_pointer (x, y);
2152 if (current_stepping_trackview) {
2153 /* don't keep the persistent stepped trackview if the mouse moves */
2154 current_stepping_trackview = 0;
2155 step_timeout.disconnect ();
2158 if (_session && _session->actively_recording()) {
2159 /* Sorry. no dragging stuff around while we record */
2163 JoinObjectRangeState const old = _join_object_range_state;
2164 update_join_object_range_location (event->motion.x, event->motion.y);
2165 if (_join_object_range_state != old) {
2166 set_canvas_cursor ();
2169 if (_over_region_trim_target) {
2170 set_canvas_cursor_for_region_view (event->motion.x, entered_regionview);
2173 bool handled = false;
2174 if (_drags->active ()) {
2175 handled = _drags->motion_handler (event, from_autoscroll);
2182 track_canvas_motion (event);
2187 Editor::can_remove_control_point (ArdourCanvas::Item* item)
2189 ControlPoint* control_point;
2191 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2192 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2196 AutomationLine& line = control_point->line ();
2197 if (dynamic_cast<AudioRegionGainLine*> (&line)) {
2198 /* we shouldn't remove the first or last gain point in region gain lines */
2199 if (line.is_last_point(*control_point) || line.is_first_point(*control_point)) {
2208 Editor::remove_control_point (ArdourCanvas::Item* item)
2210 if (!can_remove_control_point (item)) {
2214 ControlPoint* control_point;
2216 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2217 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2221 control_point->line().remove_point (*control_point);
2225 Editor::edit_control_point (ArdourCanvas::Item* item)
2227 ControlPoint* p = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"));
2230 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2234 ControlPointDialog d (p);
2235 d.set_position (Gtk::WIN_POS_MOUSE);
2238 if (d.run () != RESPONSE_ACCEPT) {
2242 p->line().modify_point_y (*p, d.get_y_fraction ());
2246 Editor::edit_note (ArdourCanvas::Item* item)
2248 ArdourCanvas::CanvasNoteEvent* e = dynamic_cast<ArdourCanvas::CanvasNoteEvent*> (item);
2251 EditNoteDialog d (&e->region_view(), e);
2252 d.set_position (Gtk::WIN_POS_MOUSE);
2260 Editor::visible_order_range (int* low, int* high) const
2262 *low = TimeAxisView::max_order ();
2265 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
2267 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
2269 if (!rtv->hidden()) {
2271 if (*high < rtv->order()) {
2272 *high = rtv->order ();
2275 if (*low > rtv->order()) {
2276 *low = rtv->order ();
2283 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
2285 /* Either add to or set the set the region selection, unless
2286 this is an alignment click (control used)
2289 if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
2290 TimeAxisView* tv = &rv.get_time_axis_view();
2291 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
2293 if (rtv && rtv->is_track()) {
2294 speed = rtv->track()->speed();
2297 framepos_t where = get_preferred_edit_position();
2301 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
2303 align_region (rv.region(), SyncPoint, (framepos_t) (where * speed));
2305 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
2307 align_region (rv.region(), End, (framepos_t) (where * speed));
2311 align_region (rv.region(), Start, (framepos_t) (where * speed));
2318 Editor::collect_new_region_view (RegionView* rv)
2320 latest_regionviews.push_back (rv);
2324 Editor::collect_and_select_new_region_view (RegionView* rv)
2327 latest_regionviews.push_back (rv);
2331 Editor::cancel_selection ()
2333 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2334 (*i)->hide_selection ();
2337 selection->clear ();
2338 clicked_selection = 0;
2343 Editor::point_trim (GdkEvent* event, framepos_t new_bound)
2345 RegionView* rv = clicked_regionview;
2347 /* Choose action dependant on which button was pressed */
2348 switch (event->button.button) {
2350 begin_reversible_command (_("start point trim"));
2352 if (selection->selected (rv)) {
2353 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
2354 i != selection->regions.by_layer().end(); ++i)
2357 cerr << "region view contains null region" << endl;
2360 if (!(*i)->region()->locked()) {
2361 (*i)->region()->clear_changes ();
2362 (*i)->region()->trim_front (new_bound);
2363 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2368 if (!rv->region()->locked()) {
2369 rv->region()->clear_changes ();
2370 rv->region()->trim_front (new_bound);
2371 _session->add_command(new StatefulDiffCommand (rv->region()));
2375 commit_reversible_command();
2379 begin_reversible_command (_("End point trim"));
2381 if (selection->selected (rv)) {
2383 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
2385 if (!(*i)->region()->locked()) {
2386 (*i)->region()->clear_changes();
2387 (*i)->region()->trim_end (new_bound);
2388 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2394 if (!rv->region()->locked()) {
2395 rv->region()->clear_changes ();
2396 rv->region()->trim_end (new_bound);
2397 _session->add_command (new StatefulDiffCommand (rv->region()));
2401 commit_reversible_command();
2410 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* /*event*/)
2415 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2416 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2420 Location* location = find_location_from_marker (marker, is_start);
2421 location->set_hidden (true, this);
2426 Editor::reposition_zoom_rect (framepos_t start, framepos_t end)
2428 double x1 = frame_to_pixel (start);
2429 double x2 = frame_to_pixel (end);
2430 double y2 = full_canvas_height - 1.0;
2432 zoom_rect->property_x1() = x1;
2433 zoom_rect->property_y1() = 1.0;
2434 zoom_rect->property_x2() = x2;
2435 zoom_rect->property_y2() = y2;
2440 Editor::mouse_rename_region (ArdourCanvas::Item* /*item*/, GdkEvent* /*event*/)
2442 using namespace Gtkmm2ext;
2444 ArdourPrompter prompter (false);
2446 prompter.set_prompt (_("Name for region:"));
2447 prompter.set_initial_text (clicked_regionview->region()->name());
2448 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
2449 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
2450 prompter.show_all ();
2451 switch (prompter.run ()) {
2452 case Gtk::RESPONSE_ACCEPT:
2454 prompter.get_result(str);
2456 clicked_regionview->region()->set_name (str);
2465 Editor::mouse_brush_insert_region (RegionView* rv, framepos_t pos)
2467 /* no brushing without a useful snap setting */
2469 switch (_snap_mode) {
2471 return; /* can't work because it allows region to be placed anywhere */
2476 switch (_snap_type) {
2484 /* don't brush a copy over the original */
2486 if (pos == rv->region()->position()) {
2490 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
2492 if (rtv == 0 || !rtv->is_track()) {
2496 boost::shared_ptr<Playlist> playlist = rtv->playlist();
2497 double speed = rtv->track()->speed();
2499 playlist->clear_changes ();
2500 boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region(), true));
2501 playlist->add_region (new_region, (framepos_t) (pos * speed));
2502 _session->add_command (new StatefulDiffCommand (playlist));
2504 // playlist is frozen, so we have to update manually XXX this is disgusting
2506 playlist->RegionAdded (new_region); /* EMIT SIGNAL */
2510 Editor::track_height_step_timeout ()
2512 if (get_microseconds() - last_track_height_step_timestamp < 250000) {
2513 current_stepping_trackview = 0;
2520 Editor::add_region_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2522 assert (region_view);
2524 if (!region_view->region()->playlist()) {
2528 _region_motion_group->raise_to_top ();
2530 if (Config->get_edit_mode() == Splice) {
2531 _drags->add (new RegionSpliceDrag (this, item, region_view, selection->regions.by_layer()));
2533 RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id);
2534 _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), false, false));
2537 /* sync the canvas to what we think is its current state */
2538 update_canvas_now();
2542 Editor::add_region_copy_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2544 assert (region_view);
2546 if (!region_view->region()->playlist()) {
2550 _region_motion_group->raise_to_top ();
2552 RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id);
2553 _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), false, true));
2557 Editor::add_region_brush_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2559 assert (region_view);
2561 if (!region_view->region()->playlist()) {
2565 if (Config->get_edit_mode() == Splice) {
2569 RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id);
2570 _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), true, false));
2572 begin_reversible_command (Operations::drag_region_brush);
2575 /** Start a grab where a time range is selected, track(s) are selected, and the
2576 * user clicks and drags a region with a modifier in order to create a new region containing
2577 * the section of the clicked region that lies within the time range.
2580 Editor::start_selection_grab (ArdourCanvas::Item* /*item*/, GdkEvent* event)
2582 if (clicked_regionview == 0) {
2586 /* lets try to create new Region for the selection */
2588 vector<boost::shared_ptr<Region> > new_regions;
2589 create_region_from_selection (new_regions);
2591 if (new_regions.empty()) {
2595 /* XXX fix me one day to use all new regions */
2597 boost::shared_ptr<Region> region (new_regions.front());
2599 /* add it to the current stream/playlist.
2601 tricky: the streamview for the track will add a new regionview. we will
2602 catch the signal it sends when it creates the regionview to
2603 set the regionview we want to then drag.
2606 latest_regionviews.clear();
2607 sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
2609 /* A selection grab currently creates two undo/redo operations, one for
2610 creating the new region and another for moving it.
2613 begin_reversible_command (Operations::selection_grab);
2615 boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
2617 playlist->clear_changes ();
2618 clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
2619 _session->add_command(new StatefulDiffCommand (playlist));
2621 commit_reversible_command ();
2625 if (latest_regionviews.empty()) {
2626 /* something went wrong */
2630 /* we need to deselect all other regionviews, and select this one
2631 i'm ignoring undo stuff, because the region creation will take care of it
2633 selection->set (latest_regionviews);
2635 _drags->set (new RegionMoveDrag (this, latest_regionviews.front()->get_canvas_group(), latest_regionviews.front(), latest_regionviews, false, false), event);
2641 if (_drags->active ()) {
2644 selection->clear ();
2649 Editor::set_internal_edit (bool yn)
2651 _internal_editing = yn;
2654 pre_internal_mouse_mode = mouse_mode;
2656 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2657 (*i)->enter_internal_edit_mode ();
2661 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2662 (*i)->leave_internal_edit_mode ();
2665 if (mouse_mode == MouseDraw && pre_internal_mouse_mode != MouseDraw) {
2666 /* we were drawing .. flip back to something sensible */
2667 set_mouse_mode (pre_internal_mouse_mode);
2671 set_canvas_cursor ();
2674 /** Update _join_object_range_state which indicate whether we are over the top or bottom half of a region view,
2675 * used by the `join object/range' tool mode.
2678 Editor::update_join_object_range_location (double /*x*/, double y)
2680 /* XXX: actually, this decides based on whether the mouse is in the top or bottom half of a RouteTimeAxisView;
2681 entered_{track,regionview} is not always setup (e.g. if the mouse is over a TimeSelection), and to get a Region
2682 that we're over requires searching the playlist.
2685 if (join_object_range_button.get_active() == false || (mouse_mode != MouseRange && mouse_mode != MouseObject)) {
2686 _join_object_range_state = JOIN_OBJECT_RANGE_NONE;
2690 if (mouse_mode == MouseObject) {
2691 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2692 } else if (mouse_mode == MouseRange) {
2693 _join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
2696 /* XXX: maybe we should make entered_track work in all cases, rather than resorting to this */
2697 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y + vertical_adjustment.get_value() - canvas_timebars_vsize);
2701 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
2706 rtv->canvas_display()->w2i (cx, cy);
2708 double const c = cy / rtv->view()->child_height();
2710 double const f = modf (c, &d);
2712 _join_object_range_state = f < 0.5 ? JOIN_OBJECT_RANGE_RANGE : JOIN_OBJECT_RANGE_OBJECT;
2718 Editor::effective_mouse_mode () const
2720 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
2722 } else if (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE) {
2730 Editor::remove_midi_note (ArdourCanvas::Item* item, GdkEvent *)
2732 ArdourCanvas::CanvasNoteEvent* e = dynamic_cast<ArdourCanvas::CanvasNoteEvent*> (item);
2735 e->region_view().delete_note (e->note ());
2739 Editor::set_canvas_cursor_for_region_view (double x, RegionView* rv)
2743 ArdourCanvas::Group* g = rv->get_canvas_group ();
2744 ArdourCanvas::Group* p = g->get_parent_group ();
2746 /* Compute x in region view parent coordinates */
2750 double x1, x2, y1, y2;
2751 g->get_bounds (x1, y1, x2, y2);
2753 /* Halfway across the region */
2754 double const h = (x1 + x2) / 2;
2756 Trimmable::CanTrim ct = rv->region()->can_trim ();
2758 if (ct & Trimmable::FrontTrimEarlier) {
2759 set_canvas_cursor (_cursors->left_side_trim);
2761 set_canvas_cursor (_cursors->left_side_trim_right_only);
2764 if (ct & Trimmable::EndTrimLater) {
2765 set_canvas_cursor (_cursors->right_side_trim);
2767 set_canvas_cursor (_cursors->right_side_trim_left_only);
2772 /** Obtain the pointer position in world coordinates */
2774 Editor::get_pointer_position (double& x, double& y) const
2777 track_canvas->get_pointer (px, py);
2778 track_canvas->window_to_world (px, py, x, y);