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 current_canvas_cursor = _cursors->cross_hair;
276 if (Keyboard::the_keyboard().key_is_down (GDK_Control_L)) {
277 current_canvas_cursor = _cursors->zoom_out;
279 current_canvas_cursor = _cursors->zoom_in;
284 current_canvas_cursor = _cursors->time_fx; // just use playhead
288 current_canvas_cursor = _cursors->speaker;
293 switch (_join_object_range_state) {
294 case JOIN_OBJECT_RANGE_NONE:
296 case JOIN_OBJECT_RANGE_OBJECT:
297 current_canvas_cursor = which_grabber_cursor ();
299 case JOIN_OBJECT_RANGE_RANGE:
300 current_canvas_cursor = _cursors->selector;
304 /* up-down cursor as a cue that automation can be dragged up and down when in join object/range mode */
305 if (join_object_range_button.get_active()) {
307 get_pointer_position (x, y);
308 ArdourCanvas::Item* i = track_canvas->get_item_at (x, y);
309 if (i && i->property_parent() && (*i->property_parent()).get_data (X_("timeselection"))) {
310 pair<TimeAxisView*, int> tvp = trackview_by_y_position (_last_motion_y + vertical_adjustment.get_value() - canvas_timebars_vsize);
311 if (dynamic_cast<AutomationTimeAxisView*> (tvp.first)) {
312 current_canvas_cursor = _cursors->up_down;
317 set_canvas_cursor (current_canvas_cursor, true);
321 Editor::set_mouse_mode (MouseMode m, bool force)
323 if (_drags->active ()) {
327 if (!force && m == mouse_mode) {
331 Glib::RefPtr<Action> act;
335 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-range"));
339 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-object"));
343 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-gain"));
347 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-zoom"));
351 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-timefx"));
355 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-audition"));
361 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
364 /* go there and back to ensure that the toggled handler is called to set up mouse_mode */
365 tact->set_active (false);
366 tact->set_active (true);
368 MouseModeChanged (); /* EMIT SIGNAL */
372 Editor::mouse_mode_toggled (MouseMode m)
378 if (!internal_editing()) {
379 if (mouse_mode != MouseRange && _join_object_range_state == JOIN_OBJECT_RANGE_NONE) {
381 /* in all modes except range and joined object/range, hide the range selection,
382 show the object (region) selection.
385 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
386 (*i)->hide_selection ();
392 in range or object/range mode, show the range selection.
395 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
396 (*i)->show_selection (selection->time);
401 set_canvas_cursor ();
403 MouseModeChanged (); /* EMIT SIGNAL */
407 Editor::step_mouse_mode (bool next)
409 switch (current_mouse_mode()) {
412 if (Profile->get_sae()) {
413 set_mouse_mode (MouseZoom);
415 set_mouse_mode (MouseRange);
418 set_mouse_mode (MouseTimeFX);
423 if (next) set_mouse_mode (MouseZoom);
424 else set_mouse_mode (MouseObject);
429 if (Profile->get_sae()) {
430 set_mouse_mode (MouseTimeFX);
432 set_mouse_mode (MouseGain);
435 if (Profile->get_sae()) {
436 set_mouse_mode (MouseObject);
438 set_mouse_mode (MouseRange);
444 if (next) set_mouse_mode (MouseTimeFX);
445 else set_mouse_mode (MouseZoom);
450 set_mouse_mode (MouseAudition);
452 if (Profile->get_sae()) {
453 set_mouse_mode (MouseZoom);
455 set_mouse_mode (MouseGain);
461 if (next) set_mouse_mode (MouseObject);
462 else set_mouse_mode (MouseTimeFX);
468 Editor::button_selection (ArdourCanvas::Item* /*item*/, GdkEvent* event, ItemType item_type)
470 /* in object/audition/timefx/gain-automation mode,
471 any button press sets the selection if the object
472 can be selected. this is a bit of hack, because
473 we want to avoid this if the mouse operation is a
476 note: not dbl-click or triple-click
478 Also note that there is no region selection in internal edit mode, otherwise
479 for operations operating on the selection (e.g. cut) it is not obvious whether
480 to cut notes or regions.
483 if (((mouse_mode != MouseObject) &&
484 (_join_object_range_state != JOIN_OBJECT_RANGE_OBJECT) &&
485 (mouse_mode != MouseAudition || item_type != RegionItem) &&
486 (mouse_mode != MouseTimeFX || item_type != RegionItem) &&
487 (mouse_mode != MouseGain) &&
488 (mouse_mode != MouseRange)) ||
489 ((event->type != GDK_BUTTON_PRESS && event->type != GDK_BUTTON_RELEASE) || event->button.button > 3) ||
490 internal_editing()) {
495 if (event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) {
497 if ((event->button.state & Keyboard::RelevantModifierKeyMask) && event->button.button != 1) {
499 /* almost no selection action on modified button-2 or button-3 events */
501 if (item_type != RegionItem && event->button.button != 2) {
507 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
508 bool press = (event->type == GDK_BUTTON_PRESS);
512 if (mouse_mode != MouseRange || _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
513 set_selected_regionview_from_click (press, op, true);
514 } else if (event->type == GDK_BUTTON_PRESS) {
515 selection->clear_tracks ();
516 set_selected_track_as_side_effect (op, true);
518 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT && !selection->regions.empty()) {
519 clicked_selection = select_range_around_region (selection->regions.front());
523 case RegionViewNameHighlight:
525 case LeftFrameHandle:
526 case RightFrameHandle:
527 if (mouse_mode != MouseRange || _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
528 set_selected_regionview_from_click (press, op, true);
529 } else if (event->type == GDK_BUTTON_PRESS) {
530 set_selected_track_as_side_effect (op);
535 case FadeInHandleItem:
537 case FadeOutHandleItem:
539 if (mouse_mode != MouseRange || _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
540 set_selected_regionview_from_click (press, op, true);
541 } else if (event->type == GDK_BUTTON_PRESS) {
542 set_selected_track_as_side_effect (op);
546 case ControlPointItem:
547 set_selected_track_as_side_effect (op, true);
548 if (mouse_mode != MouseRange || _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
549 set_selected_control_point_from_click (op, false);
554 /* for context click, select track */
555 if (event->button.button == 3) {
556 selection->clear_tracks ();
557 set_selected_track_as_side_effect (op, true);
561 case AutomationTrackItem:
562 set_selected_track_as_side_effect (op, true);
571 Editor::button_press_handler_1 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
573 /* single mouse clicks on any of these item types operate
574 independent of mouse mode, mostly because they are
575 not on the main track canvas or because we want
580 case PlayheadCursorItem:
581 _drags->set (new CursorDrag (this, item, true), event);
585 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
586 hide_marker (item, event);
588 _drags->set (new MarkerDrag (this, item), event);
592 case TempoMarkerItem:
594 TempoMarker* m = reinterpret_cast<TempoMarker*> (item->get_data ("marker"));
596 if (m->tempo().movable ()) {
598 new TempoMarkerDrag (
601 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
611 case MeterMarkerItem:
613 MeterMarker* m = reinterpret_cast<MeterMarker*> (item->get_data ("marker"));
615 if (m->meter().movable ()) {
617 new MeterMarkerDrag (
620 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
633 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
634 _drags->set (new CursorDrag (this, &playhead_cursor->canvas_item, false), event);
640 case RangeMarkerBarItem:
641 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
642 _drags->set (new CursorDrag (this, &playhead_cursor->canvas_item, false), event);
644 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateRangeMarker), event);
649 case CdMarkerBarItem:
650 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
651 _drags->set (new CursorDrag (this, &playhead_cursor->canvas_item, false), event);
653 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateCDMarker), event);
658 case TransportMarkerBarItem:
659 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
660 _drags->set (new CursorDrag (this, &playhead_cursor->canvas_item, false), event);
662 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateTransportMarker), event);
671 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
672 /* special case: allow trim of range selections in joined object mode;
673 in theory eff should equal MouseRange in this case, but it doesn't
674 because entering the range selection canvas item results in entered_regionview
675 being set to 0, so update_join_object_range_location acts as if we aren't
678 if (item_type == StartSelectionTrimItem) {
679 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
680 } else if (item_type == EndSelectionTrimItem) {
681 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
685 Editing::MouseMode eff = effective_mouse_mode ();
687 /* special case: allow drag of region fade in/out in object mode with join object/range enabled */
688 if (item_type == FadeInHandleItem || item_type == FadeOutHandleItem) {
695 case StartSelectionTrimItem:
696 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
699 case EndSelectionTrimItem:
700 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
704 if (Keyboard::modifier_state_contains
705 (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier))) {
706 // contains and not equals because I can't use alt as a modifier alone.
707 start_selection_grab (item, event);
708 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
709 /* grab selection for moving */
710 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionMove), event);
712 double const y = event->button.y + vertical_adjustment.get_value() - canvas_timebars_vsize;
713 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
715 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
716 if (join_object_range_button.get_active() && atv) {
717 /* smart "join" mode: drag automation */
718 _drags->set (new AutomationRangeDrag (this, atv->base_item(), selection->time), event, _cursors->up_down);
720 /* this was debated, but decided the more common action was to
721 make a new selection */
722 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
729 if (internal_editing()) {
730 /* trim notes if we're in internal edit mode and near the ends of the note */
731 ArdourCanvas::CanvasNote* cn = dynamic_cast<ArdourCanvas::CanvasNote*> (item);
732 if (cn && cn->big_enough_to_trim() && cn->mouse_near_ends()) {
733 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
735 _drags->set (new NoteDrag (this, item), event);
741 if (internal_editing()) {
742 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
743 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
747 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
752 case RegionViewNameHighlight:
753 if (!clicked_regionview->region()->locked()) {
754 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
755 _drags->set (new TrimDrag (this, item, clicked_regionview, s.by_layer()), event);
760 case LeftFrameHandle:
761 case RightFrameHandle:
762 if (!internal_editing() && !clicked_regionview->region()->locked()) {
763 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
764 _drags->set (new TrimDrag (this, item, clicked_regionview, s.by_layer()), event);
770 if (!internal_editing()) {
771 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
780 if (internal_editing()) {
781 ArdourCanvas::CanvasNoteEvent* cn = dynamic_cast<ArdourCanvas::CanvasNoteEvent*> (item);
782 if (cn->mouse_near_ends()) {
783 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
785 _drags->set (new NoteDrag (this, item), event);
795 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)) &&
796 event->type == GDK_BUTTON_PRESS) {
798 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
800 } else if (event->type == GDK_BUTTON_PRESS) {
803 case FadeInHandleItem:
805 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
806 _drags->set (new FadeInDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), s), event, _cursors->fade_in);
810 case FadeOutHandleItem:
812 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
813 _drags->set (new FadeOutDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), s), event, _cursors->fade_out);
817 case FeatureLineItem:
819 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier)) {
820 remove_transient(item);
824 _drags->set (new FeatureLineDrag (this, item), event);
830 if (dynamic_cast<AutomationRegionView*> (clicked_regionview)) {
831 /* click on an automation region view; do nothing here and let the ARV's signal handler
837 if (internal_editing ()) {
838 /* no region drags in internal edit mode */
842 /* click on a normal region view */
843 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
844 add_region_copy_drag (item, event, clicked_regionview);
845 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
846 add_region_brush_drag (item, event, clicked_regionview);
848 add_region_drag (item, event, clicked_regionview);
851 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT && !selection->regions.empty()) {
852 _drags->add (new SelectionDrag (this, clicked_axisview->get_selection_rect (clicked_selection)->rect, SelectionDrag::SelectionMove));
855 _drags->start_grab (event);
858 case RegionViewNameHighlight:
859 case LeftFrameHandle:
860 case RightFrameHandle:
861 if (!clicked_regionview->region()->locked()) {
862 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
863 _drags->set (new TrimDrag (this, item, clicked_regionview, s.by_layer()), event);
870 /* rename happens on edit clicks */
871 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
872 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, s.by_layer()), event);
877 case ControlPointItem:
878 _drags->set (new ControlPointDrag (this, item), event);
882 case AutomationLineItem:
883 _drags->set (new LineDrag (this, item), event);
888 if (internal_editing()) {
889 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
890 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
894 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
898 case AutomationTrackItem:
900 TimeAxisView* parent = clicked_axisview->get_parent ();
901 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (clicked_axisview);
903 if (parent && dynamic_cast<MidiTimeAxisView*> (parent) && atv->show_regions ()) {
904 /* create a MIDI region so that we have somewhere to put automation */
905 _drags->set (new RegionCreateDrag (this, item, parent), event);
907 /* rubberband drag to select automation points */
908 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
915 if (join_object_range_button.get_active()) {
916 /* we're in "smart" joined mode, and we've clicked on a Selection */
917 double const y = event->button.y + vertical_adjustment.get_value() - canvas_timebars_vsize;
918 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
920 /* if we're over an automation track, start a drag of its data */
921 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
923 _drags->set (new AutomationRangeDrag (this, atv->base_item(), selection->time), event, _cursors->up_down);
926 /* if we're over a track and a region, and in the `object' part of a region,
927 put a selection around the region and drag both
929 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
930 if (rtv && _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
931 boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track> (rtv->route ());
933 boost::shared_ptr<Playlist> pl = t->playlist ();
936 boost::shared_ptr<Region> r = pl->top_region_at (event_frame (event));
938 RegionView* rv = rtv->view()->find_view (r);
939 clicked_selection = select_range_around_region (rv);
940 _drags->add (new SelectionDrag (this, item, SelectionDrag::SelectionMove));
941 list<RegionView*> rvs;
943 _drags->add (new RegionMoveDrag (this, item, rv, rvs, false, false));
944 _drags->start_grab (event);
955 case ImageFrameHandleStartItem:
956 imageframe_start_handle_op(item, event) ;
959 case ImageFrameHandleEndItem:
960 imageframe_end_handle_op(item, event) ;
963 case MarkerViewHandleStartItem:
964 markerview_item_start_handle_op(item, event) ;
967 case MarkerViewHandleEndItem:
968 markerview_item_end_handle_op(item, event) ;
972 start_markerview_grab(item, event) ;
975 start_imageframe_grab(item, event) ;
993 /* start a grab so that if we finish after moving
994 we can tell what happened.
996 _drags->set (new RegionGainDrag (this, item), event, current_canvas_cursor);
1000 _drags->set (new LineDrag (this, item), event);
1003 case ControlPointItem:
1004 _drags->set (new ControlPointDrag (this, item), event);
1014 switch (item_type) {
1015 case ControlPointItem:
1016 _drags->set (new ControlPointDrag (this, item), event);
1019 case AutomationLineItem:
1020 _drags->set (new LineDrag (this, item), event);
1024 // XXX need automation mode to identify which
1026 // start_line_grab_from_regionview (item, event);
1036 if (event->type == GDK_BUTTON_PRESS) {
1037 _drags->set (new MouseZoomDrag (this, item), event);
1044 if (internal_editing() && item_type == NoteItem) {
1045 /* drag notes if we're in internal edit mode */
1046 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
1048 } else if ((!internal_editing() || dynamic_cast<AudioRegionView*> (clicked_regionview)) && clicked_regionview) {
1049 /* do time-FX if we're not in internal edit mode, or we are but we clicked on an audio region */
1050 _drags->set (new TimeFXDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1056 _drags->set (new ScrubDrag (this, item), event);
1057 scrub_reversals = 0;
1058 scrub_reverse_distance = 0;
1059 last_scrub_x = event->button.x;
1060 scrubbing_direction = 0;
1061 set_canvas_cursor (_cursors->transparent);
1073 Editor::button_press_handler_2 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1075 Editing::MouseMode const eff = effective_mouse_mode ();
1078 switch (item_type) {
1080 if (internal_editing ()) {
1081 /* no region drags in internal edit mode */
1085 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
1086 add_region_copy_drag (item, event, clicked_regionview);
1088 add_region_drag (item, event, clicked_regionview);
1090 _drags->start_grab (event);
1093 case ControlPointItem:
1094 _drags->set (new ControlPointDrag (this, item), event);
1102 switch (item_type) {
1103 case RegionViewNameHighlight:
1104 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1108 case LeftFrameHandle:
1109 case RightFrameHandle:
1110 if (!internal_editing ()) {
1111 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1116 case RegionViewName:
1117 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer()), event);
1128 /* relax till release */
1134 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1135 temporal_zoom_to_frame (false, event_frame (event));
1137 temporal_zoom_to_frame (true, event_frame(event));
1150 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1152 if (event->type != GDK_BUTTON_PRESS) {
1156 Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->track_canvas->get_window();
1158 if (canvas_window) {
1159 Glib::RefPtr<const Gdk::Window> pointer_window;
1162 Gdk::ModifierType mask;
1164 pointer_window = canvas_window->get_pointer (x, y, mask);
1166 if (pointer_window == track_canvas->get_bin_window()) {
1167 track_canvas->window_to_world (x, y, wx, wy);
1171 pre_press_cursor = current_canvas_cursor;
1173 track_canvas->grab_focus();
1175 if (_session && _session->actively_recording()) {
1179 button_selection (item, event, item_type);
1181 if (!_drags->active () &&
1182 (Keyboard::is_delete_event (&event->button) ||
1183 Keyboard::is_context_menu_event (&event->button) ||
1184 Keyboard::is_edit_event (&event->button))) {
1186 /* handled by button release */
1190 switch (event->button.button) {
1192 return button_press_handler_1 (item, event, item_type);
1196 return button_press_handler_2 (item, event, item_type);
1203 return button_press_dispatch (&event->button);
1212 Editor::button_press_dispatch (GdkEventButton* ev)
1214 /* this function is intended only for buttons 4 and above.
1217 Gtkmm2ext::MouseButton b (ev->state, ev->button);
1218 return button_bindings->activate (b, Gtkmm2ext::Bindings::Press);
1222 Editor::button_release_dispatch (GdkEventButton* ev)
1224 /* this function is intended only for buttons 4 and above.
1227 Gtkmm2ext::MouseButton b (ev->state, ev->button);
1228 return button_bindings->activate (b, Gtkmm2ext::Bindings::Release);
1232 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1234 framepos_t where = event_frame (event, 0, 0);
1235 AutomationTimeAxisView* atv = 0;
1237 if (pre_press_cursor) {
1238 set_canvas_cursor (pre_press_cursor);
1239 pre_press_cursor = 0;
1242 /* no action if we're recording */
1244 if (_session && _session->actively_recording()) {
1248 /* see if we're finishing a drag */
1250 bool were_dragging = false;
1251 if (_drags->active ()) {
1252 bool const r = _drags->end_grab (event);
1254 /* grab dragged, so do nothing else */
1258 were_dragging = true;
1261 update_region_layering_order_editor ();
1263 /* edit events get handled here */
1265 if (!_drags->active () && Keyboard::is_edit_event (&event->button)) {
1266 switch (item_type) {
1268 show_region_properties ();
1271 case TempoMarkerItem:
1272 edit_tempo_marker (item);
1275 case MeterMarkerItem:
1276 edit_meter_marker (item);
1279 case RegionViewName:
1280 if (clicked_regionview->name_active()) {
1281 return mouse_rename_region (item, event);
1285 case ControlPointItem:
1286 edit_control_point (item);
1299 /* context menu events get handled here */
1301 if (Keyboard::is_context_menu_event (&event->button)) {
1303 context_click_event = *event;
1305 if (!_drags->active ()) {
1307 /* no matter which button pops up the context menu, tell the menu
1308 widget to use button 1 to drive menu selection.
1311 switch (item_type) {
1313 case FadeInHandleItem:
1315 case FadeOutHandleItem:
1316 popup_fade_context_menu (1, event->button.time, item, item_type);
1320 popup_track_context_menu (1, event->button.time, item_type, false);
1324 case RegionViewNameHighlight:
1325 case LeftFrameHandle:
1326 case RightFrameHandle:
1327 case RegionViewName:
1328 popup_track_context_menu (1, event->button.time, item_type, false);
1332 popup_track_context_menu (1, event->button.time, item_type, true);
1335 case AutomationTrackItem:
1336 popup_track_context_menu (1, event->button.time, item_type, false);
1340 case RangeMarkerBarItem:
1341 case TransportMarkerBarItem:
1342 case CdMarkerBarItem:
1345 popup_ruler_menu (where, item_type);
1349 marker_context_menu (&event->button, item);
1352 case TempoMarkerItem:
1353 tempo_or_meter_marker_context_menu (&event->button, item);
1356 case MeterMarkerItem:
1357 tempo_or_meter_marker_context_menu (&event->button, item);
1360 case CrossfadeViewItem:
1361 popup_track_context_menu (1, event->button.time, item_type, false);
1364 case ControlPointItem:
1365 popup_control_point_context_menu (item, event);
1369 case ImageFrameItem:
1370 popup_imageframe_edit_menu(1, event->button.time, item, true) ;
1372 case ImageFrameTimeAxisItem:
1373 popup_imageframe_edit_menu(1, event->button.time, item, false) ;
1375 case MarkerViewItem:
1376 popup_marker_time_axis_edit_menu(1, event->button.time, item, true) ;
1378 case MarkerTimeAxisItem:
1379 popup_marker_time_axis_edit_menu(1, event->button.time, item, false) ;
1391 /* delete events get handled here */
1393 Editing::MouseMode const eff = effective_mouse_mode ();
1395 if (!_drags->active () && Keyboard::is_delete_event (&event->button)) {
1397 switch (item_type) {
1398 case TempoMarkerItem:
1399 remove_tempo_marker (item);
1402 case MeterMarkerItem:
1403 remove_meter_marker (item);
1407 remove_marker (*item, event);
1411 if (eff == MouseObject) {
1412 remove_clicked_region ();
1416 case ControlPointItem:
1417 remove_control_point (item);
1421 remove_midi_note (item, event);
1430 switch (event->button.button) {
1433 switch (item_type) {
1434 /* see comments in button_press_handler */
1435 case PlayheadCursorItem:
1438 case AutomationLineItem:
1439 case StartSelectionTrimItem:
1440 case EndSelectionTrimItem:
1444 if (!_dragging_playhead) {
1445 snap_to_with_modifier (where, event, 0, true);
1446 mouse_add_new_marker (where);
1450 case CdMarkerBarItem:
1451 if (!_dragging_playhead) {
1452 // if we get here then a dragged range wasn't done
1453 snap_to_with_modifier (where, event, 0, true);
1454 mouse_add_new_marker (where, true);
1459 if (!_dragging_playhead) {
1460 snap_to_with_modifier (where, event);
1461 mouse_add_new_tempo_event (where);
1466 if (!_dragging_playhead) {
1467 mouse_add_new_meter_event (pixel_to_frame (event->button.x));
1478 switch (item_type) {
1479 case AutomationTrackItem:
1480 atv = dynamic_cast<AutomationTimeAxisView*>(clicked_axisview);
1482 atv->add_automation_event (event, where, event->button.y);
1493 switch (item_type) {
1496 /* check that we didn't drag before releasing, since
1497 its really annoying to create new control
1498 points when doing this.
1500 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (clicked_regionview);
1501 if (were_dragging && arv) {
1502 arv->add_gain_point_event (item, event);
1508 case AutomationTrackItem:
1509 dynamic_cast<AutomationTimeAxisView*>(clicked_axisview)->
1510 add_automation_event (event, where, event->button.y);
1519 set_canvas_cursor (current_canvas_cursor);
1520 if (scrubbing_direction == 0) {
1521 /* no drag, just a click */
1522 switch (item_type) {
1524 play_selected_region ();
1530 /* make sure we stop */
1531 _session->request_transport_speed (0.0);
1540 /* do any (de)selection operations that should occur on button release */
1541 button_selection (item, event, item_type);
1550 switch (item_type) {
1552 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1554 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier|Keyboard::SecondaryModifier))) {
1557 // Button2 click is unused
1570 // x_style_paste (where, 1.0);
1591 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1598 switch (item_type) {
1599 case ControlPointItem:
1600 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1601 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1602 cp->set_visible (true);
1606 at_y = cp->get_y ();
1607 cp->i2w (at_x, at_y);
1611 fraction = 1.0 - (cp->get_y() / cp->line().height());
1613 if (is_drawable() && !_drags->active ()) {
1614 set_canvas_cursor (_cursors->fader);
1617 _verbose_cursor->set (cp->line().get_verbose_cursor_string (fraction), at_x, at_y);
1618 _verbose_cursor->show ();
1623 if (mouse_mode == MouseGain) {
1624 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1626 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredGainLine.get();
1627 if (is_drawable()) {
1628 set_canvas_cursor (_cursors->fader);
1633 case AutomationLineItem:
1634 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1636 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1638 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredAutomationLine.get();
1640 if (is_drawable()) {
1641 set_canvas_cursor (_cursors->fader);
1646 case RegionViewNameHighlight:
1647 if (is_drawable() && mouse_mode == MouseObject && entered_regionview) {
1648 set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1649 _over_region_trim_target = true;
1653 case LeftFrameHandle:
1654 case RightFrameHandle:
1655 if (is_drawable() && mouse_mode == MouseObject && !internal_editing() && entered_regionview) {
1656 set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1660 case StartSelectionTrimItem:
1662 case ImageFrameHandleStartItem:
1663 case MarkerViewHandleStartItem:
1665 if (is_drawable()) {
1666 set_canvas_cursor (_cursors->left_side_trim);
1669 case EndSelectionTrimItem:
1671 case ImageFrameHandleEndItem:
1672 case MarkerViewHandleEndItem:
1674 if (is_drawable()) {
1675 set_canvas_cursor (_cursors->right_side_trim);
1679 case PlayheadCursorItem:
1680 if (is_drawable()) {
1681 switch (_edit_point) {
1683 set_canvas_cursor (_cursors->grabber_edit_point);
1686 set_canvas_cursor (_cursors->grabber);
1692 case RegionViewName:
1694 /* when the name is not an active item, the entire name highlight is for trimming */
1696 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1697 if (mouse_mode == MouseObject && is_drawable()) {
1698 set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1699 _over_region_trim_target = true;
1705 case AutomationTrackItem:
1706 if (is_drawable()) {
1707 Gdk::Cursor *cursor;
1708 switch (mouse_mode) {
1710 cursor = _cursors->selector;
1713 cursor = _cursors->zoom_in;
1716 cursor = _cursors->cross_hair;
1720 set_canvas_cursor (cursor);
1722 AutomationTimeAxisView* atv;
1723 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1724 clear_entered_track = false;
1725 set_entered_track (atv);
1731 case RangeMarkerBarItem:
1732 case TransportMarkerBarItem:
1733 case CdMarkerBarItem:
1736 if (is_drawable()) {
1737 set_canvas_cursor (_cursors->timebar);
1742 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1745 entered_marker = marker;
1746 marker->set_color_rgba (ARDOUR_UI::config()->canvasvar_EnteredMarker.get());
1748 case MeterMarkerItem:
1749 case TempoMarkerItem:
1750 if (is_drawable()) {
1751 set_canvas_cursor (_cursors->timebar);
1755 case FadeInHandleItem:
1756 if (mouse_mode == MouseObject && !internal_editing()) {
1757 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1759 rect->property_fill_color_rgba() = 0xBBBBBBAA;
1761 set_canvas_cursor (_cursors->fade_in);
1765 case FadeOutHandleItem:
1766 if (mouse_mode == MouseObject && !internal_editing()) {
1767 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1769 rect->property_fill_color_rgba() = 0xBBBBBBAA;
1771 set_canvas_cursor (_cursors->fade_out);
1774 case FeatureLineItem:
1776 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1777 line->property_fill_color_rgba() = 0xFF0000FF;
1781 if (join_object_range_button.get_active()) {
1782 set_canvas_cursor ();
1790 /* second pass to handle entered track status in a comprehensible way.
1793 switch (item_type) {
1795 case AutomationLineItem:
1796 case ControlPointItem:
1797 /* these do not affect the current entered track state */
1798 clear_entered_track = false;
1801 case AutomationTrackItem:
1802 /* handled above already */
1806 set_entered_track (0);
1814 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent*, ItemType item_type)
1824 switch (item_type) {
1825 case ControlPointItem:
1826 cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
1827 if (cp->line().the_list()->interpolation() != AutomationList::Discrete) {
1828 if (cp->line().npoints() > 1 && !cp->get_selected()) {
1829 cp->set_visible (false);
1833 if (is_drawable()) {
1834 set_canvas_cursor (current_canvas_cursor);
1837 _verbose_cursor->hide ();
1840 case RegionViewNameHighlight:
1841 case LeftFrameHandle:
1842 case RightFrameHandle:
1843 case StartSelectionTrimItem:
1844 case EndSelectionTrimItem:
1845 case PlayheadCursorItem:
1848 case ImageFrameHandleStartItem:
1849 case ImageFrameHandleEndItem:
1850 case MarkerViewHandleStartItem:
1851 case MarkerViewHandleEndItem:
1854 _over_region_trim_target = false;
1856 if (is_drawable()) {
1857 set_canvas_cursor (current_canvas_cursor);
1862 case AutomationLineItem:
1863 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1865 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1867 line->property_fill_color_rgba() = al->get_line_color();
1869 if (is_drawable()) {
1870 set_canvas_cursor (current_canvas_cursor);
1874 case RegionViewName:
1875 /* see enter_handler() for notes */
1876 _over_region_trim_target = false;
1878 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1879 if (is_drawable() && mouse_mode == MouseObject) {
1880 set_canvas_cursor (current_canvas_cursor);
1885 case RangeMarkerBarItem:
1886 case TransportMarkerBarItem:
1887 case CdMarkerBarItem:
1891 if (is_drawable()) {
1892 set_canvas_cursor (current_canvas_cursor);
1897 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1901 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
1902 location_flags_changed (loc, this);
1905 case MeterMarkerItem:
1906 case TempoMarkerItem:
1908 if (is_drawable()) {
1909 set_canvas_cursor (_cursors->timebar);
1914 case FadeInHandleItem:
1915 case FadeOutHandleItem:
1916 rv = static_cast<RegionView*>(item->get_data ("regionview"));
1918 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1920 rect->property_fill_color_rgba() = rv->get_fill_color();
1921 rect->property_outline_pixels() = 0;
1924 set_canvas_cursor (current_canvas_cursor);
1927 case AutomationTrackItem:
1928 if (is_drawable()) {
1929 set_canvas_cursor (current_canvas_cursor);
1930 clear_entered_track = true;
1931 Glib::signal_idle().connect (sigc::mem_fun(*this, &Editor::left_automation_track));
1934 case FeatureLineItem:
1936 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1937 line->property_fill_color_rgba() = (guint) ARDOUR_UI::config()->canvasvar_ZeroLine.get();;
1949 Editor::left_automation_track ()
1951 if (clear_entered_track) {
1952 set_entered_track (0);
1953 clear_entered_track = false;
1959 Editor::scrub (framepos_t frame, double current_x)
1963 if (scrubbing_direction == 0) {
1965 _session->request_locate (frame, false);
1966 _session->request_transport_speed (0.1);
1967 scrubbing_direction = 1;
1971 if (last_scrub_x > current_x) {
1973 /* pointer moved to the left */
1975 if (scrubbing_direction > 0) {
1977 /* we reversed direction to go backwards */
1980 scrub_reverse_distance += (int) (last_scrub_x - current_x);
1984 /* still moving to the left (backwards) */
1986 scrub_reversals = 0;
1987 scrub_reverse_distance = 0;
1989 delta = 0.01 * (last_scrub_x - current_x);
1990 _session->request_transport_speed_nonzero (_session->transport_speed() - delta);
1994 /* pointer moved to the right */
1996 if (scrubbing_direction < 0) {
1997 /* we reversed direction to go forward */
2000 scrub_reverse_distance += (int) (current_x - last_scrub_x);
2003 /* still moving to the right */
2005 scrub_reversals = 0;
2006 scrub_reverse_distance = 0;
2008 delta = 0.01 * (current_x - last_scrub_x);
2009 _session->request_transport_speed_nonzero (_session->transport_speed() + delta);
2013 /* if there have been more than 2 opposite motion moves detected, or one that moves
2014 back more than 10 pixels, reverse direction
2017 if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
2019 if (scrubbing_direction > 0) {
2020 /* was forwards, go backwards */
2021 _session->request_transport_speed (-0.1);
2022 scrubbing_direction = -1;
2024 /* was backwards, go forwards */
2025 _session->request_transport_speed (0.1);
2026 scrubbing_direction = 1;
2029 scrub_reverse_distance = 0;
2030 scrub_reversals = 0;
2034 last_scrub_x = current_x;
2038 Editor::motion_handler (ArdourCanvas::Item* /*item*/, GdkEvent* event, bool from_autoscroll)
2040 _last_motion_y = event->motion.y;
2042 if (event->motion.is_hint) {
2045 /* We call this so that MOTION_NOTIFY events continue to be
2046 delivered to the canvas. We need to do this because we set
2047 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
2048 the density of the events, at the expense of a round-trip
2049 to the server. Given that this will mostly occur on cases
2050 where DISPLAY = :0.0, and given the cost of what the motion
2051 event might do, its a good tradeoff.
2054 track_canvas->get_pointer (x, y);
2057 if (current_stepping_trackview) {
2058 /* don't keep the persistent stepped trackview if the mouse moves */
2059 current_stepping_trackview = 0;
2060 step_timeout.disconnect ();
2063 if (_session && _session->actively_recording()) {
2064 /* Sorry. no dragging stuff around while we record */
2068 JoinObjectRangeState const old = _join_object_range_state;
2069 update_join_object_range_location (event->motion.x, event->motion.y);
2070 if (_join_object_range_state != old) {
2071 set_canvas_cursor ();
2074 if (_over_region_trim_target) {
2075 set_canvas_cursor_for_region_view (event->motion.x, entered_regionview);
2078 bool handled = false;
2079 if (_drags->active ()) {
2080 handled = _drags->motion_handler (event, from_autoscroll);
2087 track_canvas_motion (event);
2092 Editor::can_remove_control_point (ArdourCanvas::Item* item)
2094 ControlPoint* control_point;
2096 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2097 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2101 AutomationLine& line = control_point->line ();
2102 if (dynamic_cast<AudioRegionGainLine*> (&line)) {
2103 /* we shouldn't remove the first or last gain point in region gain lines */
2104 if (line.is_last_point(*control_point) || line.is_first_point(*control_point)) {
2113 Editor::remove_control_point (ArdourCanvas::Item* item)
2115 if (!can_remove_control_point (item)) {
2119 ControlPoint* control_point;
2121 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2122 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2126 control_point->line().remove_point (*control_point);
2130 Editor::edit_control_point (ArdourCanvas::Item* item)
2132 ControlPoint* p = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"));
2135 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2139 ControlPointDialog d (p);
2140 d.set_position (Gtk::WIN_POS_MOUSE);
2143 if (d.run () != RESPONSE_ACCEPT) {
2147 p->line().modify_point_y (*p, d.get_y_fraction ());
2151 Editor::edit_note (ArdourCanvas::Item* item)
2153 ArdourCanvas::CanvasNoteEvent* e = dynamic_cast<ArdourCanvas::CanvasNoteEvent*> (item);
2156 EditNoteDialog d (&e->region_view(), e);
2157 d.set_position (Gtk::WIN_POS_MOUSE);
2165 Editor::visible_order_range (int* low, int* high) const
2167 *low = TimeAxisView::max_order ();
2170 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
2172 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
2174 if (!rtv->hidden()) {
2176 if (*high < rtv->order()) {
2177 *high = rtv->order ();
2180 if (*low > rtv->order()) {
2181 *low = rtv->order ();
2188 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
2190 /* Either add to or set the set the region selection, unless
2191 this is an alignment click (control used)
2194 if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
2195 TimeAxisView* tv = &rv.get_time_axis_view();
2196 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
2198 if (rtv && rtv->is_track()) {
2199 speed = rtv->track()->speed();
2202 framepos_t where = get_preferred_edit_position();
2206 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
2208 align_region (rv.region(), SyncPoint, (framepos_t) (where * speed));
2210 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
2212 align_region (rv.region(), End, (framepos_t) (where * speed));
2216 align_region (rv.region(), Start, (framepos_t) (where * speed));
2223 Editor::collect_new_region_view (RegionView* rv)
2225 latest_regionviews.push_back (rv);
2229 Editor::collect_and_select_new_region_view (RegionView* rv)
2232 latest_regionviews.push_back (rv);
2236 Editor::cancel_selection ()
2238 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2239 (*i)->hide_selection ();
2242 selection->clear ();
2243 clicked_selection = 0;
2248 Editor::point_trim (GdkEvent* event, framepos_t new_bound)
2250 RegionView* rv = clicked_regionview;
2252 /* Choose action dependant on which button was pressed */
2253 switch (event->button.button) {
2255 begin_reversible_command (_("start point trim"));
2257 if (selection->selected (rv)) {
2258 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
2259 i != selection->regions.by_layer().end(); ++i)
2262 cerr << "region view contains null region" << endl;
2265 if (!(*i)->region()->locked()) {
2266 (*i)->region()->clear_changes ();
2267 (*i)->region()->trim_front (new_bound);
2268 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2273 if (!rv->region()->locked()) {
2274 rv->region()->clear_changes ();
2275 rv->region()->trim_front (new_bound);
2276 _session->add_command(new StatefulDiffCommand (rv->region()));
2280 commit_reversible_command();
2284 begin_reversible_command (_("End point trim"));
2286 if (selection->selected (rv)) {
2288 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
2290 if (!(*i)->region()->locked()) {
2291 (*i)->region()->clear_changes();
2292 (*i)->region()->trim_end (new_bound);
2293 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2299 if (!rv->region()->locked()) {
2300 rv->region()->clear_changes ();
2301 rv->region()->trim_end (new_bound);
2302 _session->add_command (new StatefulDiffCommand (rv->region()));
2306 commit_reversible_command();
2315 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* /*event*/)
2320 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2321 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2325 Location* location = find_location_from_marker (marker, is_start);
2326 location->set_hidden (true, this);
2331 Editor::reposition_zoom_rect (framepos_t start, framepos_t end)
2333 double x1 = frame_to_pixel (start);
2334 double x2 = frame_to_pixel (end);
2335 double y2 = full_canvas_height - 1.0;
2337 zoom_rect->property_x1() = x1;
2338 zoom_rect->property_y1() = 1.0;
2339 zoom_rect->property_x2() = x2;
2340 zoom_rect->property_y2() = y2;
2345 Editor::mouse_rename_region (ArdourCanvas::Item* /*item*/, GdkEvent* /*event*/)
2347 using namespace Gtkmm2ext;
2349 ArdourPrompter prompter (false);
2351 prompter.set_prompt (_("Name for region:"));
2352 prompter.set_initial_text (clicked_regionview->region()->name());
2353 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
2354 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
2355 prompter.show_all ();
2356 switch (prompter.run ()) {
2357 case Gtk::RESPONSE_ACCEPT:
2359 prompter.get_result(str);
2361 clicked_regionview->region()->set_name (str);
2370 Editor::mouse_brush_insert_region (RegionView* rv, framepos_t pos)
2372 /* no brushing without a useful snap setting */
2374 switch (_snap_mode) {
2376 return; /* can't work because it allows region to be placed anywhere */
2381 switch (_snap_type) {
2389 /* don't brush a copy over the original */
2391 if (pos == rv->region()->position()) {
2395 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
2397 if (rtv == 0 || !rtv->is_track()) {
2401 boost::shared_ptr<Playlist> playlist = rtv->playlist();
2402 double speed = rtv->track()->speed();
2404 playlist->clear_changes ();
2405 boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region(), true));
2406 playlist->add_region (new_region, (framepos_t) (pos * speed));
2407 _session->add_command (new StatefulDiffCommand (playlist));
2409 // playlist is frozen, so we have to update manually XXX this is disgusting
2411 playlist->RegionAdded (new_region); /* EMIT SIGNAL */
2415 Editor::track_height_step_timeout ()
2417 if (get_microseconds() - last_track_height_step_timestamp < 250000) {
2418 current_stepping_trackview = 0;
2425 Editor::add_region_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2427 assert (region_view);
2429 if (!region_view->region()->playlist()) {
2433 _region_motion_group->raise_to_top ();
2435 if (Config->get_edit_mode() == Splice) {
2436 _drags->add (new RegionSpliceDrag (this, item, region_view, selection->regions.by_layer()));
2438 RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id);
2439 _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), false, false));
2442 /* sync the canvas to what we think is its current state */
2443 update_canvas_now();
2447 Editor::add_region_copy_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2449 assert (region_view);
2451 if (!region_view->region()->playlist()) {
2455 _region_motion_group->raise_to_top ();
2457 RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id);
2458 _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), false, true));
2462 Editor::add_region_brush_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2464 assert (region_view);
2466 if (!region_view->region()->playlist()) {
2470 if (Config->get_edit_mode() == Splice) {
2474 RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id);
2475 _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), true, false));
2477 begin_reversible_command (Operations::drag_region_brush);
2480 /** Start a grab where a time range is selected, track(s) are selected, and the
2481 * user clicks and drags a region with a modifier in order to create a new region containing
2482 * the section of the clicked region that lies within the time range.
2485 Editor::start_selection_grab (ArdourCanvas::Item* /*item*/, GdkEvent* event)
2487 if (clicked_regionview == 0) {
2491 /* lets try to create new Region for the selection */
2493 vector<boost::shared_ptr<Region> > new_regions;
2494 create_region_from_selection (new_regions);
2496 if (new_regions.empty()) {
2500 /* XXX fix me one day to use all new regions */
2502 boost::shared_ptr<Region> region (new_regions.front());
2504 /* add it to the current stream/playlist.
2506 tricky: the streamview for the track will add a new regionview. we will
2507 catch the signal it sends when it creates the regionview to
2508 set the regionview we want to then drag.
2511 latest_regionviews.clear();
2512 sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
2514 /* A selection grab currently creates two undo/redo operations, one for
2515 creating the new region and another for moving it.
2518 begin_reversible_command (Operations::selection_grab);
2520 boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
2522 playlist->clear_changes ();
2523 clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
2524 _session->add_command(new StatefulDiffCommand (playlist));
2526 commit_reversible_command ();
2530 if (latest_regionviews.empty()) {
2531 /* something went wrong */
2535 /* we need to deselect all other regionviews, and select this one
2536 i'm ignoring undo stuff, because the region creation will take care of it
2538 selection->set (latest_regionviews);
2540 _drags->set (new RegionMoveDrag (this, latest_regionviews.front()->get_canvas_group(), latest_regionviews.front(), latest_regionviews, false, false), event);
2546 if (_drags->active ()) {
2549 selection->clear ();
2554 Editor::set_internal_edit (bool yn)
2556 _internal_editing = yn;
2559 mouse_select_button.set_image (::get_icon("midi_tool_pencil"));
2560 ARDOUR_UI::instance()->set_tip (mouse_select_button, _("Draw/Edit MIDI Notes"));
2561 mouse_mode_toggled (mouse_mode);
2563 pre_internal_mouse_mode = mouse_mode;
2565 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2566 (*i)->enter_internal_edit_mode ();
2571 mouse_select_button.set_image (::get_icon("tool_range"));
2572 ARDOUR_UI::instance()->set_tip (mouse_select_button, _("Select/Move Ranges"));
2573 mouse_mode_toggled (mouse_mode); // sets cursor
2575 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2576 (*i)->leave_internal_edit_mode ();
2579 if (mouse_mode == MouseRange && pre_internal_mouse_mode != MouseRange) {
2580 /* we were drawing .. flip back to something sensible */
2581 set_mouse_mode (pre_internal_mouse_mode);
2586 /** Update _join_object_range_state which indicate whether we are over the top or bottom half of a region view,
2587 * used by the `join object/range' tool mode.
2590 Editor::update_join_object_range_location (double /*x*/, double y)
2592 /* XXX: actually, this decides based on whether the mouse is in the top or bottom half of a RouteTimeAxisView;
2593 entered_{track,regionview} is not always setup (e.g. if the mouse is over a TimeSelection), and to get a Region
2594 that we're over requires searching the playlist.
2597 if (join_object_range_button.get_active() == false || (mouse_mode != MouseRange && mouse_mode != MouseObject)) {
2598 _join_object_range_state = JOIN_OBJECT_RANGE_NONE;
2602 if (mouse_mode == MouseObject) {
2603 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2604 } else if (mouse_mode == MouseRange) {
2605 _join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
2608 /* XXX: maybe we should make entered_track work in all cases, rather than resorting to this */
2609 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y + vertical_adjustment.get_value() - canvas_timebars_vsize);
2613 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
2618 rtv->canvas_display()->w2i (cx, cy);
2620 double const c = cy / rtv->view()->child_height();
2622 double const f = modf (c, &d);
2624 _join_object_range_state = f < 0.5 ? JOIN_OBJECT_RANGE_RANGE : JOIN_OBJECT_RANGE_OBJECT;
2630 Editor::effective_mouse_mode () const
2632 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
2634 } else if (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE) {
2642 Editor::remove_midi_note (ArdourCanvas::Item* item, GdkEvent *)
2644 ArdourCanvas::CanvasNoteEvent* e = dynamic_cast<ArdourCanvas::CanvasNoteEvent*> (item);
2647 e->region_view().delete_note (e->note ());
2651 Editor::set_canvas_cursor_for_region_view (double x, RegionView* rv)
2655 ArdourCanvas::Group* g = rv->get_canvas_group ();
2656 ArdourCanvas::Group* p = g->get_parent_group ();
2658 /* Compute x in region view parent coordinates */
2662 double x1, x2, y1, y2;
2663 g->get_bounds (x1, y1, x2, y2);
2665 /* Halfway across the region */
2666 double const h = (x1 + x2) / 2;
2668 Trimmable::CanTrim ct = rv->region()->can_trim ();
2670 if (ct & Trimmable::FrontTrimEarlier) {
2671 set_canvas_cursor (_cursors->left_side_trim);
2673 set_canvas_cursor (_cursors->left_side_trim_right_only);
2676 if (ct & Trimmable::EndTrimLater) {
2677 set_canvas_cursor (_cursors->right_side_trim);
2679 set_canvas_cursor (_cursors->right_side_trim_left_only);
2684 /** Obtain the pointer position in world coordinates */
2686 Editor::get_pointer_position (double& x, double& y) const
2689 track_canvas->get_pointer (px, py);
2690 track_canvas->window_to_world (px, py, x, y);