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/audioregion.h"
66 #include "ardour/operations.h"
67 #include "ardour/playlist.h"
68 #include "ardour/profile.h"
69 #include "ardour/region_factory.h"
70 #include "ardour/route.h"
71 #include "ardour/session.h"
72 #include "ardour/types.h"
79 using namespace ARDOUR;
82 using namespace Editing;
83 using Gtkmm2ext::Keyboard;
86 Editor::mouse_frame (framepos_t& where, bool& in_track_canvas) const
88 /* gdk_window_get_pointer() has X11's XQueryPointer semantics in that it only
89 pays attentions to subwindows. this means that menu windows are ignored, and
90 if the pointer is in a menu, the return window from the call will be the
91 the regular subwindow *under* the menu.
93 this matters quite a lot if the pointer is moving around in a menu that overlaps
94 the track canvas because we will believe that we are within the track canvas
95 when we are not. therefore, we track enter/leave events for the track canvas
96 and allow that to override the result of gdk_window_get_pointer().
99 if (!within_track_canvas) {
105 Gdk::ModifierType mask;
106 Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->track_canvas->get_window();
107 Glib::RefPtr<const Gdk::Window> pointer_window;
109 if (!canvas_window) {
113 pointer_window = canvas_window->get_pointer (x, y, mask);
115 if (pointer_window == track_canvas->get_bin_window()) {
118 in_track_canvas = true;
121 in_track_canvas = false;
126 event.type = GDK_BUTTON_RELEASE;
130 where = event_frame (&event, 0, 0);
135 Editor::event_frame (GdkEvent const * event, double* pcx, double* pcy) const
149 switch (event->type) {
150 case GDK_BUTTON_RELEASE:
151 case GDK_BUTTON_PRESS:
152 case GDK_2BUTTON_PRESS:
153 case GDK_3BUTTON_PRESS:
154 *pcx = event->button.x;
155 *pcy = event->button.y;
156 _trackview_group->w2i(*pcx, *pcy);
158 case GDK_MOTION_NOTIFY:
159 *pcx = event->motion.x;
160 *pcy = event->motion.y;
161 _trackview_group->w2i(*pcx, *pcy);
163 case GDK_ENTER_NOTIFY:
164 case GDK_LEAVE_NOTIFY:
165 track_canvas->w2c(event->crossing.x, event->crossing.y, *pcx, *pcy);
168 case GDK_KEY_RELEASE:
169 // track_canvas->w2c(event->key.x, event->key.y, *pcx, *pcy);
172 warning << string_compose (_("Editor::event_frame() used on unhandled event type %1"), event->type) << endmsg;
176 /* note that pixel_to_frame() never returns less than zero, so even if the pixel
177 position is negative (as can be the case with motion events in particular),
178 the frame location is always positive.
181 return pixel_to_frame (*pcx);
185 Editor::which_grabber_cursor ()
187 Gdk::Cursor* c = _cursors->grabber;
189 if (_internal_editing) {
190 switch (mouse_mode) {
192 c = _cursors->midi_pencil;
196 c = _cursors->grabber_note;
200 c = _cursors->midi_resize;
209 switch (_edit_point) {
211 c = _cursors->grabber_edit_point;
214 boost::shared_ptr<Movable> m = _movable.lock();
215 if (m && m->locked()) {
216 c = _cursors->speaker;
226 Editor::set_current_trimmable (boost::shared_ptr<Trimmable> t)
228 boost::shared_ptr<Trimmable> st = _trimmable.lock();
230 if (!st || st == t) {
232 set_canvas_cursor ();
237 Editor::set_current_movable (boost::shared_ptr<Movable> m)
239 boost::shared_ptr<Movable> sm = _movable.lock();
241 if (!sm || sm != m) {
243 set_canvas_cursor ();
248 Editor::set_canvas_cursor ()
250 switch (mouse_mode) {
252 current_canvas_cursor = _cursors->selector;
256 current_canvas_cursor = which_grabber_cursor();
260 current_canvas_cursor = _cursors->midi_pencil;
264 current_canvas_cursor = _cursors->cross_hair;
268 if (Keyboard::the_keyboard().key_is_down (GDK_Control_L)) {
269 current_canvas_cursor = _cursors->zoom_out;
271 current_canvas_cursor = _cursors->zoom_in;
276 current_canvas_cursor = _cursors->time_fx; // just use playhead
280 current_canvas_cursor = _cursors->speaker;
284 switch (_join_object_range_state) {
285 case JOIN_OBJECT_RANGE_NONE:
287 case JOIN_OBJECT_RANGE_OBJECT:
288 current_canvas_cursor = which_grabber_cursor ();
290 case JOIN_OBJECT_RANGE_RANGE:
291 current_canvas_cursor = _cursors->selector;
295 /* up-down cursor as a cue that automation can be dragged up and down when in join object/range mode */
296 if (smart_mode_action->get_active()) {
298 get_pointer_position (x, y);
299 ArdourCanvas::Item* i = track_canvas->get_item_at (x, y);
300 if (i && i->property_parent() && (*i->property_parent()).get_data (X_("timeselection"))) {
301 pair<TimeAxisView*, int> tvp = trackview_by_y_position (_last_motion_y + vertical_adjustment.get_value() - canvas_timebars_vsize);
302 if (dynamic_cast<AutomationTimeAxisView*> (tvp.first)) {
303 current_canvas_cursor = _cursors->up_down;
308 set_canvas_cursor (current_canvas_cursor, true);
312 Editor::set_mouse_mode (MouseMode m, bool force)
314 if (_drags->active ()) {
318 if (!force && m == mouse_mode) {
322 Glib::RefPtr<Action> act;
326 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-range"));
330 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-object"));
334 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-draw"));
338 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-gain"));
342 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-zoom"));
346 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-timefx"));
350 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-audition"));
356 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
359 /* go there and back to ensure that the toggled handler is called to set up mouse_mode */
360 tact->set_active (false);
361 tact->set_active (true);
363 MouseModeChanged (); /* EMIT SIGNAL */
367 Editor::mouse_mode_toggled (MouseMode m)
369 Glib::RefPtr<Action> act;
370 Glib::RefPtr<ToggleAction> tact;
374 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-range"));
378 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-object"));
382 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-draw"));
386 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-gain"));
390 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-zoom"));
394 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-timefx"));
398 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-audition"));
404 tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
407 if (!tact->get_active()) {
408 /* this was just the notification that the old mode has been
409 * left. we'll get called again with the new mode active in a
417 act = ActionManager::get_action (X_("MouseMode"), X_("toggle-internal-edit"));
418 tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
419 tact->set_active (true);
429 if (!internal_editing()) {
430 if (mouse_mode != MouseRange && mouse_mode != MouseGain && _join_object_range_state == JOIN_OBJECT_RANGE_NONE) {
432 /* in all modes except range, gain and joined object/range, hide the range selection,
433 show the object (region) selection.
436 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
437 (*i)->hide_selection ();
443 in range or object/range mode, show the range selection.
446 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
447 (*i)->show_selection (selection->time);
452 set_canvas_cursor ();
453 set_gain_envelope_visibility ();
455 MouseModeChanged (); /* EMIT SIGNAL */
459 Editor::step_mouse_mode (bool next)
461 switch (current_mouse_mode()) {
464 if (Profile->get_sae()) {
465 set_mouse_mode (MouseZoom);
467 set_mouse_mode (MouseRange);
470 set_mouse_mode (MouseTimeFX);
475 if (next) set_mouse_mode (MouseDraw);
476 else set_mouse_mode (MouseObject);
480 if (next) set_mouse_mode (MouseZoom);
481 else set_mouse_mode (MouseRange);
486 if (Profile->get_sae()) {
487 set_mouse_mode (MouseTimeFX);
489 set_mouse_mode (MouseGain);
492 if (Profile->get_sae()) {
493 set_mouse_mode (MouseObject);
495 set_mouse_mode (MouseDraw);
501 if (next) set_mouse_mode (MouseTimeFX);
502 else set_mouse_mode (MouseZoom);
507 set_mouse_mode (MouseAudition);
509 if (Profile->get_sae()) {
510 set_mouse_mode (MouseZoom);
512 set_mouse_mode (MouseGain);
518 if (next) set_mouse_mode (MouseObject);
519 else set_mouse_mode (MouseTimeFX);
525 Editor::toggle_internal_editing_from_double_click (GdkEvent* event)
527 if (_drags->active()) {
528 _drags->end_grab (event);
531 ActionManager::do_action ("MouseMode", "toggle-internal-edit");
533 /* prevent reversion of edit cursor on button release */
535 pre_press_cursor = 0;
541 Editor::button_selection (ArdourCanvas::Item* /*item*/, GdkEvent* event, ItemType item_type)
543 /* in object/audition/timefx/gain-automation mode,
544 any button press sets the selection if the object
545 can be selected. this is a bit of hack, because
546 we want to avoid this if the mouse operation is a
549 note: not dbl-click or triple-click
551 Also note that there is no region selection in internal edit mode, otherwise
552 for operations operating on the selection (e.g. cut) it is not obvious whether
553 to cut notes or regions.
556 if (((mouse_mode != MouseObject) &&
557 (_join_object_range_state != JOIN_OBJECT_RANGE_OBJECT) &&
558 (mouse_mode != MouseAudition || item_type != RegionItem) &&
559 (mouse_mode != MouseTimeFX || item_type != RegionItem) &&
560 (mouse_mode != MouseGain) &&
561 (mouse_mode != MouseRange) &&
562 (mouse_mode != MouseDraw)) ||
563 ((event->type != GDK_BUTTON_PRESS && event->type != GDK_BUTTON_RELEASE) || event->button.button > 3) ||
564 (internal_editing() && mouse_mode != MouseTimeFX)) {
569 if (event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) {
571 if ((event->button.state & Keyboard::RelevantModifierKeyMask) && event->button.button != 1) {
573 /* almost no selection action on modified button-2 or button-3 events */
575 if (item_type != RegionItem && event->button.button != 2) {
581 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
582 bool press = (event->type == GDK_BUTTON_PRESS);
586 if (!doing_range_stuff()) {
587 set_selected_regionview_from_click (press, op);
591 if (doing_range_stuff()) {
592 /* don't change the selection unless the
593 clicked track is not currently selected. if
594 so, "collapse" the selection to just this
597 if (!selection->selected (clicked_axisview)) {
598 set_selected_track_as_side_effect (Selection::Set);
604 case RegionViewNameHighlight:
606 case LeftFrameHandle:
607 case RightFrameHandle:
608 if (doing_object_stuff() || (mouse_mode != MouseRange && mouse_mode != MouseObject)) {
609 set_selected_regionview_from_click (press, op);
610 } else if (event->type == GDK_BUTTON_PRESS) {
611 set_selected_track_as_side_effect (op);
615 case FadeInHandleItem:
617 case FadeOutHandleItem:
619 case StartCrossFadeItem:
620 case EndCrossFadeItem:
621 if (doing_object_stuff() || (mouse_mode != MouseRange && mouse_mode != MouseObject)) {
622 set_selected_regionview_from_click (press, op);
623 } else if (event->type == GDK_BUTTON_PRESS) {
624 set_selected_track_as_side_effect (op);
628 case ControlPointItem:
629 set_selected_track_as_side_effect (op);
630 if (doing_object_stuff() || (mouse_mode != MouseRange && mouse_mode != MouseObject)) {
631 set_selected_control_point_from_click (press, op);
636 /* for context click, select track */
637 if (event->button.button == 3) {
638 selection->clear_tracks ();
639 set_selected_track_as_side_effect (op);
643 case AutomationTrackItem:
644 set_selected_track_as_side_effect (op);
653 Editor::button_press_handler_1 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
655 /* single mouse clicks on any of these item types operate
656 independent of mouse mode, mostly because they are
657 not on the main track canvas or because we want
662 case PlayheadCursorItem:
663 _drags->set (new CursorDrag (this, item, true), event);
667 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
668 hide_marker (item, event);
670 _drags->set (new MarkerDrag (this, item), event);
674 case TempoMarkerItem:
676 TempoMarker* m = reinterpret_cast<TempoMarker*> (item->get_data ("marker"));
678 if (m->tempo().movable ()) {
680 new TempoMarkerDrag (
683 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
693 case MeterMarkerItem:
695 MeterMarker* m = reinterpret_cast<MeterMarker*> (item->get_data ("marker"));
697 if (m->meter().movable ()) {
699 new MeterMarkerDrag (
702 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
715 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
716 _drags->set (new CursorDrag (this, &playhead_cursor->canvas_item, false), event);
722 case RangeMarkerBarItem:
723 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
724 _drags->set (new CursorDrag (this, &playhead_cursor->canvas_item, false), event);
726 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateRangeMarker), event);
731 case CdMarkerBarItem:
732 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
733 _drags->set (new CursorDrag (this, &playhead_cursor->canvas_item, false), event);
735 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateCDMarker), event);
740 case TransportMarkerBarItem:
741 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
742 _drags->set (new CursorDrag (this, &playhead_cursor->canvas_item, false), event);
744 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateTransportMarker), event);
753 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
754 /* special case: allow trim of range selections in joined object mode;
755 in theory eff should equal MouseRange in this case, but it doesn't
756 because entering the range selection canvas item results in entered_regionview
757 being set to 0, so update_join_object_range_location acts as if we aren't
760 if (item_type == StartSelectionTrimItem) {
761 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
762 } else if (item_type == EndSelectionTrimItem) {
763 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
767 Editing::MouseMode eff = effective_mouse_mode ();
769 /* special case: allow drag of region fade in/out in object mode with join object/range enabled */
770 if (item_type == FadeInHandleItem || item_type == FadeOutHandleItem) {
777 case StartSelectionTrimItem:
778 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
781 case EndSelectionTrimItem:
782 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
786 if (Keyboard::modifier_state_contains
787 (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier))) {
788 // contains and not equals because I can't use alt as a modifier alone.
789 start_selection_grab (item, event);
790 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
791 /* grab selection for moving */
792 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionMove), event);
794 double const y = event->button.y + vertical_adjustment.get_value() - canvas_timebars_vsize;
795 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
797 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
798 if (smart_mode_action->get_active() && atv) {
799 /* smart "join" mode: drag automation */
800 _drags->set (new AutomationRangeDrag (this, atv, selection->time), event, _cursors->up_down);
802 /* this was debated, but decided the more common action was to
803 make a new selection */
804 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
811 if (internal_editing()) {
812 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
813 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
817 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
822 case RegionViewNameHighlight:
823 if (!clicked_regionview->region()->locked()) {
824 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
825 _drags->set (new TrimDrag (this, item, clicked_regionview, s.by_layer()), event);
830 case LeftFrameHandle:
831 case RightFrameHandle:
832 if (!internal_editing() && doing_object_stuff() && !clicked_regionview->region()->locked()) {
833 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
834 _drags->set (new TrimDrag (this, item, clicked_regionview, s.by_layer()), event);
840 if (!internal_editing()) {
841 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
850 if (internal_editing()) {
851 /* trim notes if we're in internal edit mode and near the ends of the note */
852 ArdourCanvas::CanvasNote* cn = dynamic_cast<ArdourCanvas::CanvasNote*> (item);
853 if (cn && cn->big_enough_to_trim() && cn->mouse_near_ends()) {
854 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
856 _drags->set (new NoteDrag (this, item), event);
870 if (internal_editing()) {
871 ArdourCanvas::CanvasNoteEvent* cn = dynamic_cast<ArdourCanvas::CanvasNoteEvent*> (item);
872 if (cn->mouse_near_ends()) {
873 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
875 _drags->set (new NoteDrag (this, item), event);
885 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)) &&
886 event->type == GDK_BUTTON_PRESS) {
888 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
890 } else if (event->type == GDK_BUTTON_PRESS) {
893 case FadeInHandleItem:
895 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
896 _drags->set (new FadeInDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), s), event, _cursors->fade_in);
900 case FadeOutHandleItem:
902 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
903 _drags->set (new FadeOutDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), s), event, _cursors->fade_out);
907 case StartCrossFadeItem:
908 _drags->set (new CrossfadeEdgeDrag (this, reinterpret_cast<AudioRegionView*>(item->get_data("regionview")), item, true), event, 0);
911 case EndCrossFadeItem:
912 _drags->set (new CrossfadeEdgeDrag (this, reinterpret_cast<AudioRegionView*>(item->get_data("regionview")), item, false), event, 0);
915 case FeatureLineItem:
917 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier)) {
918 remove_transient(item);
922 _drags->set (new FeatureLineDrag (this, item), event);
928 if (dynamic_cast<AutomationRegionView*> (clicked_regionview)) {
929 /* click on an automation region view; do nothing here and let the ARV's signal handler
935 if (internal_editing ()) {
936 if (event->type == GDK_2BUTTON_PRESS && event->button.button == 1) {
937 Glib::RefPtr<Action> act = ActionManager::get_action (X_("MouseMode"), X_("toggle-internal-edit"));
943 /* click on a normal region view */
944 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
945 add_region_copy_drag (item, event, clicked_regionview);
946 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
947 add_region_brush_drag (item, event, clicked_regionview);
949 add_region_drag (item, event, clicked_regionview);
953 if (!internal_editing() && (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE && !selection->regions.empty())) {
954 _drags->add (new SelectionDrag (this, clicked_axisview->get_selection_rect (clicked_selection)->rect, SelectionDrag::SelectionMove));
957 _drags->start_grab (event);
960 case RegionViewNameHighlight:
961 case LeftFrameHandle:
962 case RightFrameHandle:
963 if (!clicked_regionview->region()->locked()) {
964 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
965 _drags->set (new TrimDrag (this, item, clicked_regionview, s.by_layer()), event);
972 /* rename happens on edit clicks */
973 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
974 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, s.by_layer()), event);
979 case ControlPointItem:
980 _drags->set (new ControlPointDrag (this, item), event);
984 case AutomationLineItem:
985 _drags->set (new LineDrag (this, item), event);
990 if (internal_editing()) {
991 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
992 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
996 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
1000 case AutomationTrackItem:
1002 TimeAxisView* parent = clicked_axisview->get_parent ();
1003 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (clicked_axisview);
1005 if (parent && dynamic_cast<MidiTimeAxisView*> (parent) && atv->show_regions ()) {
1007 RouteTimeAxisView* p = dynamic_cast<RouteTimeAxisView*> (parent);
1009 boost::shared_ptr<Playlist> pl = p->track()->playlist ();
1010 if (pl->n_regions() == 0) {
1011 /* Parent has no regions; create one so that we have somewhere to put automation */
1012 _drags->set (new RegionCreateDrag (this, item, parent), event);
1014 /* See if there's a region before the click that we can extend, and extend it if so */
1015 framepos_t const t = event_frame (event);
1016 boost::shared_ptr<Region> prev = pl->find_next_region (t, End, -1);
1018 _drags->set (new RegionCreateDrag (this, item, parent), event);
1020 prev->set_length (t - prev->position ());
1024 /* rubberband drag to select automation points */
1025 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
1032 if (smart_mode_action->get_active()) {
1033 /* we're in "smart" joined mode, and we've clicked on a Selection */
1034 double const y = event->button.y + vertical_adjustment.get_value() - canvas_timebars_vsize;
1035 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
1037 /* if we're over an automation track, start a drag of its data */
1038 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
1040 _drags->set (new AutomationRangeDrag (this, atv, selection->time), event, _cursors->up_down);
1043 /* if we're over a track and a region, and in the `object' part of a region,
1044 put a selection around the region and drag both
1046 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1047 if (rtv && _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
1048 boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track> (rtv->route ());
1050 boost::shared_ptr<Playlist> pl = t->playlist ();
1053 boost::shared_ptr<Region> r = pl->top_region_at (event_frame (event));
1055 RegionView* rv = rtv->view()->find_view (r);
1056 clicked_selection = select_range (rv->region()->position(),
1057 rv->region()->last_frame()+1);
1058 _drags->add (new SelectionDrag (this, item, SelectionDrag::SelectionMove));
1059 list<RegionView*> rvs;
1061 _drags->add (new RegionMoveDrag (this, item, rv, rvs, false, false));
1062 _drags->start_grab (event);
1073 case ImageFrameHandleStartItem:
1074 imageframe_start_handle_op(item, event) ;
1077 case ImageFrameHandleEndItem:
1078 imageframe_end_handle_op(item, event) ;
1081 case MarkerViewHandleStartItem:
1082 markerview_item_start_handle_op(item, event) ;
1085 case MarkerViewHandleEndItem:
1086 markerview_item_end_handle_op(item, event) ;
1089 case MarkerViewItem:
1090 start_markerview_grab(item, event) ;
1092 case ImageFrameItem:
1093 start_imageframe_grab(item, event) ;
1109 switch (item_type) {
1111 _drags->set (new LineDrag (this, item), event);
1114 case ControlPointItem:
1115 _drags->set (new ControlPointDrag (this, item), event);
1121 AudioRegionView* arv = dynamic_cast<AudioRegionView *> (clicked_regionview);
1123 _drags->set (new AutomationRangeDrag (this, arv, selection->time), event, _cursors->up_down);
1124 _drags->start_grab (event);
1130 case AutomationLineItem:
1131 _drags->set (new LineDrag (this, item), event);
1141 if (event->type == GDK_BUTTON_PRESS) {
1142 _drags->set (new MouseZoomDrag (this, item), event);
1149 if (internal_editing() && item_type == NoteItem) {
1150 /* drag notes if we're in internal edit mode */
1151 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
1153 } else if (clicked_regionview) {
1155 _drags->set (new TimeFXDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1161 _drags->set (new ScrubDrag (this, item), event);
1162 scrub_reversals = 0;
1163 scrub_reverse_distance = 0;
1164 last_scrub_x = event->button.x;
1165 scrubbing_direction = 0;
1166 set_canvas_cursor (_cursors->transparent);
1178 Editor::button_press_handler_2 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1180 Editing::MouseMode const eff = effective_mouse_mode ();
1183 switch (item_type) {
1185 if (internal_editing ()) {
1186 /* no region drags in internal edit mode */
1190 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
1191 add_region_copy_drag (item, event, clicked_regionview);
1193 add_region_drag (item, event, clicked_regionview);
1195 _drags->start_grab (event);
1198 case ControlPointItem:
1199 _drags->set (new ControlPointDrag (this, item), event);
1207 switch (item_type) {
1208 case RegionViewNameHighlight:
1209 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1213 case LeftFrameHandle:
1214 case RightFrameHandle:
1215 if (!internal_editing ()) {
1216 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1221 case RegionViewName:
1222 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer()), event);
1236 /* relax till release */
1242 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1243 temporal_zoom_to_frame (false, event_frame (event));
1245 temporal_zoom_to_frame (true, event_frame(event));
1258 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1260 if (event->type != GDK_BUTTON_PRESS) {
1264 Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->track_canvas->get_window();
1266 if (canvas_window) {
1267 Glib::RefPtr<const Gdk::Window> pointer_window;
1270 Gdk::ModifierType mask;
1272 pointer_window = canvas_window->get_pointer (x, y, mask);
1274 if (pointer_window == track_canvas->get_bin_window()) {
1275 track_canvas->window_to_world (x, y, wx, wy);
1279 pre_press_cursor = current_canvas_cursor;
1281 track_canvas->grab_focus();
1283 if (_session && _session->actively_recording()) {
1289 if (internal_editing()) {
1290 bool leave_internal_edit_mode = false;
1292 switch (item_type) {
1297 if (!dynamic_cast<MidiRegionView*> (clicked_regionview) && !dynamic_cast<AutomationRegionView*> (clicked_regionview)) {
1298 leave_internal_edit_mode = true;
1302 case PlayheadCursorItem:
1304 case TempoMarkerItem:
1305 case MeterMarkerItem:
1309 case RangeMarkerBarItem:
1310 case CdMarkerBarItem:
1311 case TransportMarkerBarItem:
1312 /* button press on these events never does anything to
1313 change the editing mode.
1318 leave_internal_edit_mode = true;
1325 if (leave_internal_edit_mode) {
1326 ActionManager::do_action ("MouseMode", "toggle-internal-edit");
1330 button_selection (item, event, item_type);
1332 if (!_drags->active () &&
1333 (Keyboard::is_delete_event (&event->button) ||
1334 Keyboard::is_context_menu_event (&event->button) ||
1335 Keyboard::is_edit_event (&event->button))) {
1337 /* handled by button release */
1341 switch (event->button.button) {
1343 return button_press_handler_1 (item, event, item_type);
1347 return button_press_handler_2 (item, event, item_type);
1354 return button_press_dispatch (&event->button);
1363 Editor::button_press_dispatch (GdkEventButton* ev)
1365 /* this function is intended only for buttons 4 and above.
1368 Gtkmm2ext::MouseButton b (ev->state, ev->button);
1369 return button_bindings->activate (b, Gtkmm2ext::Bindings::Press);
1373 Editor::button_release_dispatch (GdkEventButton* ev)
1375 /* this function is intended only for buttons 4 and above.
1378 Gtkmm2ext::MouseButton b (ev->state, ev->button);
1379 return button_bindings->activate (b, Gtkmm2ext::Bindings::Release);
1383 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1385 framepos_t where = event_frame (event, 0, 0);
1386 AutomationTimeAxisView* atv = 0;
1388 if (pre_press_cursor) {
1389 set_canvas_cursor (pre_press_cursor);
1390 pre_press_cursor = 0;
1393 /* no action if we're recording */
1395 if (_session && _session->actively_recording()) {
1399 /* see if we're finishing a drag */
1401 bool were_dragging = false;
1402 if (_drags->active ()) {
1403 bool const r = _drags->end_grab (event);
1405 /* grab dragged, so do nothing else */
1409 were_dragging = true;
1412 update_region_layering_order_editor ();
1414 /* edit events get handled here */
1416 if (!_drags->active () && Keyboard::is_edit_event (&event->button)) {
1417 switch (item_type) {
1419 show_region_properties ();
1422 case TempoMarkerItem:
1423 edit_tempo_marker (item);
1426 case MeterMarkerItem:
1427 edit_meter_marker (item);
1430 case RegionViewName:
1431 if (clicked_regionview->name_active()) {
1432 return mouse_rename_region (item, event);
1436 case ControlPointItem:
1437 edit_control_point (item);
1442 ArdourCanvas::CanvasNoteEvent* e = dynamic_cast<ArdourCanvas::CanvasNoteEvent*> (item);
1444 edit_notes (e->region_view().selection ());
1454 /* context menu events get handled here */
1456 if (Keyboard::is_context_menu_event (&event->button)) {
1458 context_click_event = *event;
1460 if (!_drags->active ()) {
1462 /* no matter which button pops up the context menu, tell the menu
1463 widget to use button 1 to drive menu selection.
1466 switch (item_type) {
1468 case FadeInHandleItem:
1470 case FadeOutHandleItem:
1471 popup_fade_context_menu (1, event->button.time, item, item_type);
1474 case StartCrossFadeItem:
1475 popup_xfade_in_context_menu (1, event->button.time, item, item_type);
1478 case EndCrossFadeItem:
1479 popup_xfade_out_context_menu (1, event->button.time, item, item_type);
1483 popup_track_context_menu (1, event->button.time, item_type, false);
1487 case RegionViewNameHighlight:
1488 case LeftFrameHandle:
1489 case RightFrameHandle:
1490 case RegionViewName:
1491 popup_track_context_menu (1, event->button.time, item_type, false);
1495 popup_track_context_menu (1, event->button.time, item_type, true);
1498 case AutomationTrackItem:
1499 popup_track_context_menu (1, event->button.time, item_type, false);
1503 case RangeMarkerBarItem:
1504 case TransportMarkerBarItem:
1505 case CdMarkerBarItem:
1508 popup_ruler_menu (where, item_type);
1512 marker_context_menu (&event->button, item);
1515 case TempoMarkerItem:
1516 tempo_or_meter_marker_context_menu (&event->button, item);
1519 case MeterMarkerItem:
1520 tempo_or_meter_marker_context_menu (&event->button, item);
1523 case CrossfadeViewItem:
1524 popup_track_context_menu (1, event->button.time, item_type, false);
1527 case ControlPointItem:
1528 popup_control_point_context_menu (item, event);
1532 case ImageFrameItem:
1533 popup_imageframe_edit_menu(1, event->button.time, item, true) ;
1535 case ImageFrameTimeAxisItem:
1536 popup_imageframe_edit_menu(1, event->button.time, item, false) ;
1538 case MarkerViewItem:
1539 popup_marker_time_axis_edit_menu(1, event->button.time, item, true) ;
1541 case MarkerTimeAxisItem:
1542 popup_marker_time_axis_edit_menu(1, event->button.time, item, false) ;
1554 /* delete events get handled here */
1556 Editing::MouseMode const eff = effective_mouse_mode ();
1558 if (!_drags->active () && Keyboard::is_delete_event (&event->button)) {
1560 switch (item_type) {
1561 case TempoMarkerItem:
1562 remove_tempo_marker (item);
1565 case MeterMarkerItem:
1566 remove_meter_marker (item);
1570 remove_marker (*item, event);
1574 if (eff == MouseObject) {
1575 remove_clicked_region ();
1579 case ControlPointItem:
1580 remove_control_point (item);
1584 remove_midi_note (item, event);
1593 switch (event->button.button) {
1596 switch (item_type) {
1597 /* see comments in button_press_handler */
1598 case PlayheadCursorItem:
1601 case AutomationLineItem:
1602 case StartSelectionTrimItem:
1603 case EndSelectionTrimItem:
1607 if (!_dragging_playhead) {
1608 snap_to_with_modifier (where, event, 0, true);
1609 mouse_add_new_marker (where);
1613 case CdMarkerBarItem:
1614 if (!_dragging_playhead) {
1615 // if we get here then a dragged range wasn't done
1616 snap_to_with_modifier (where, event, 0, true);
1617 mouse_add_new_marker (where, true);
1622 if (!_dragging_playhead) {
1623 snap_to_with_modifier (where, event);
1624 mouse_add_new_tempo_event (where);
1629 if (!_dragging_playhead) {
1630 mouse_add_new_meter_event (pixel_to_frame (event->button.x));
1641 switch (item_type) {
1642 case AutomationTrackItem:
1643 atv = dynamic_cast<AutomationTimeAxisView*>(clicked_axisview);
1645 atv->add_automation_event (event, where, event->button.y);
1655 switch (item_type) {
1658 /* check that we didn't drag before releasing, since
1659 its really annoying to create new control
1660 points when doing this.
1662 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (clicked_regionview);
1663 if (!were_dragging && arv) {
1664 arv->add_gain_point_event (item, event);
1670 case AutomationTrackItem:
1671 dynamic_cast<AutomationTimeAxisView*>(clicked_axisview)->
1672 add_automation_event (event, where, event->button.y);
1681 set_canvas_cursor (current_canvas_cursor);
1682 if (scrubbing_direction == 0) {
1683 /* no drag, just a click */
1684 switch (item_type) {
1686 play_selected_region ();
1692 /* make sure we stop */
1693 _session->request_transport_speed (0.0);
1702 /* do any (de)selection operations that should occur on button release */
1703 button_selection (item, event, item_type);
1712 switch (item_type) {
1714 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1716 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier|Keyboard::SecondaryModifier))) {
1719 // Button2 click is unused
1734 // x_style_paste (where, 1.0);
1755 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1762 switch (item_type) {
1763 case ControlPointItem:
1764 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1765 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1766 cp->set_visible (true);
1770 at_y = cp->get_y ();
1771 cp->i2w (at_x, at_y);
1775 fraction = 1.0 - (cp->get_y() / cp->line().height());
1777 if (is_drawable() && !_drags->active ()) {
1778 set_canvas_cursor (_cursors->fader);
1781 _verbose_cursor->set (cp->line().get_verbose_cursor_string (fraction), at_x, at_y);
1782 _verbose_cursor->show ();
1787 if (mouse_mode == MouseGain) {
1788 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1790 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredGainLine.get();
1791 if (is_drawable()) {
1792 set_canvas_cursor (_cursors->fader);
1797 case AutomationLineItem:
1798 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1799 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1801 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredAutomationLine.get();
1803 if (is_drawable()) {
1804 set_canvas_cursor (_cursors->fader);
1809 case RegionViewNameHighlight:
1810 if (is_drawable() && doing_object_stuff() && entered_regionview) {
1811 set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1812 _over_region_trim_target = true;
1816 case LeftFrameHandle:
1817 case RightFrameHandle:
1818 if (is_drawable() && doing_object_stuff() && !internal_editing() && entered_regionview) {
1819 set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1823 case StartSelectionTrimItem:
1825 case ImageFrameHandleStartItem:
1826 case MarkerViewHandleStartItem:
1828 if (is_drawable()) {
1829 set_canvas_cursor (_cursors->left_side_trim);
1832 case EndSelectionTrimItem:
1834 case ImageFrameHandleEndItem:
1835 case MarkerViewHandleEndItem:
1837 if (is_drawable()) {
1838 set_canvas_cursor (_cursors->right_side_trim);
1842 case PlayheadCursorItem:
1843 if (is_drawable()) {
1844 switch (_edit_point) {
1846 set_canvas_cursor (_cursors->grabber_edit_point);
1849 set_canvas_cursor (_cursors->grabber);
1855 case RegionViewName:
1857 /* when the name is not an active item, the entire name highlight is for trimming */
1859 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1860 if (mouse_mode == MouseObject && is_drawable()) {
1861 set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1862 _over_region_trim_target = true;
1868 case AutomationTrackItem:
1869 if (is_drawable()) {
1870 Gdk::Cursor *cursor;
1871 switch (mouse_mode) {
1873 cursor = _cursors->selector;
1876 cursor = _cursors->zoom_in;
1879 cursor = _cursors->cross_hair;
1883 set_canvas_cursor (cursor);
1885 AutomationTimeAxisView* atv;
1886 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1887 clear_entered_track = false;
1888 set_entered_track (atv);
1894 case RangeMarkerBarItem:
1895 case TransportMarkerBarItem:
1896 case CdMarkerBarItem:
1899 if (is_drawable()) {
1900 set_canvas_cursor (_cursors->timebar);
1905 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1908 entered_marker = marker;
1909 marker->set_color_rgba (ARDOUR_UI::config()->canvasvar_EnteredMarker.get());
1911 case MeterMarkerItem:
1912 case TempoMarkerItem:
1913 if (is_drawable()) {
1914 set_canvas_cursor (_cursors->timebar);
1918 case FadeInHandleItem:
1919 if (mouse_mode == MouseObject && !internal_editing()) {
1920 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1922 rect->property_fill_color_rgba() = 0xBBBBBBAA;
1924 set_canvas_cursor (_cursors->fade_in);
1928 case FadeOutHandleItem:
1929 if (mouse_mode == MouseObject && !internal_editing()) {
1930 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1932 rect->property_fill_color_rgba() = 0xBBBBBBAA;
1934 set_canvas_cursor (_cursors->fade_out);
1937 case FeatureLineItem:
1939 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1940 line->property_fill_color_rgba() = 0xFF0000FF;
1944 if (smart_mode_action->get_active()) {
1945 set_canvas_cursor ();
1953 /* second pass to handle entered track status in a comprehensible way.
1956 switch (item_type) {
1958 case AutomationLineItem:
1959 case ControlPointItem:
1960 /* these do not affect the current entered track state */
1961 clear_entered_track = false;
1964 case AutomationTrackItem:
1965 /* handled above already */
1969 set_entered_track (0);
1977 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent*, ItemType item_type)
1987 switch (item_type) {
1988 case ControlPointItem:
1989 cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
1990 if (cp->line().the_list()->interpolation() != AutomationList::Discrete) {
1991 if (cp->line().npoints() > 1 && !cp->get_selected()) {
1992 cp->set_visible (false);
1996 if (is_drawable()) {
1997 set_canvas_cursor (current_canvas_cursor);
2000 _verbose_cursor->hide ();
2003 case RegionViewNameHighlight:
2004 case LeftFrameHandle:
2005 case RightFrameHandle:
2006 case StartSelectionTrimItem:
2007 case EndSelectionTrimItem:
2008 case PlayheadCursorItem:
2011 case ImageFrameHandleStartItem:
2012 case ImageFrameHandleEndItem:
2013 case MarkerViewHandleStartItem:
2014 case MarkerViewHandleEndItem:
2017 _over_region_trim_target = false;
2019 if (is_drawable()) {
2020 set_canvas_cursor (current_canvas_cursor);
2025 case AutomationLineItem:
2026 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
2028 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
2030 line->property_fill_color_rgba() = al->get_line_color();
2032 if (is_drawable()) {
2033 set_canvas_cursor (current_canvas_cursor);
2037 case RegionViewName:
2038 /* see enter_handler() for notes */
2039 _over_region_trim_target = false;
2041 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
2042 if (is_drawable() && mouse_mode == MouseObject) {
2043 set_canvas_cursor (current_canvas_cursor);
2048 case RangeMarkerBarItem:
2049 case TransportMarkerBarItem:
2050 case CdMarkerBarItem:
2054 if (is_drawable()) {
2055 set_canvas_cursor (current_canvas_cursor);
2060 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2064 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
2065 location_flags_changed (loc, this);
2068 case MeterMarkerItem:
2069 case TempoMarkerItem:
2071 if (is_drawable()) {
2072 set_canvas_cursor (current_canvas_cursor);
2077 case FadeInHandleItem:
2078 case FadeOutHandleItem:
2079 rv = static_cast<RegionView*>(item->get_data ("regionview"));
2081 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
2083 rect->property_fill_color_rgba() = rv->get_fill_color();
2084 rect->property_outline_pixels() = 0;
2087 set_canvas_cursor (current_canvas_cursor);
2090 case AutomationTrackItem:
2091 if (is_drawable()) {
2092 set_canvas_cursor (current_canvas_cursor);
2093 clear_entered_track = true;
2094 Glib::signal_idle().connect (sigc::mem_fun(*this, &Editor::left_automation_track));
2097 case FeatureLineItem:
2099 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
2100 line->property_fill_color_rgba() = (guint) ARDOUR_UI::config()->canvasvar_ZeroLine.get();;
2112 Editor::left_automation_track ()
2114 if (clear_entered_track) {
2115 set_entered_track (0);
2116 clear_entered_track = false;
2122 Editor::scrub (framepos_t frame, double current_x)
2126 if (scrubbing_direction == 0) {
2128 _session->request_locate (frame, false);
2129 _session->request_transport_speed (0.1);
2130 scrubbing_direction = 1;
2134 if (last_scrub_x > current_x) {
2136 /* pointer moved to the left */
2138 if (scrubbing_direction > 0) {
2140 /* we reversed direction to go backwards */
2143 scrub_reverse_distance += (int) (last_scrub_x - current_x);
2147 /* still moving to the left (backwards) */
2149 scrub_reversals = 0;
2150 scrub_reverse_distance = 0;
2152 delta = 0.01 * (last_scrub_x - current_x);
2153 _session->request_transport_speed_nonzero (_session->transport_speed() - delta);
2157 /* pointer moved to the right */
2159 if (scrubbing_direction < 0) {
2160 /* we reversed direction to go forward */
2163 scrub_reverse_distance += (int) (current_x - last_scrub_x);
2166 /* still moving to the right */
2168 scrub_reversals = 0;
2169 scrub_reverse_distance = 0;
2171 delta = 0.01 * (current_x - last_scrub_x);
2172 _session->request_transport_speed_nonzero (_session->transport_speed() + delta);
2176 /* if there have been more than 2 opposite motion moves detected, or one that moves
2177 back more than 10 pixels, reverse direction
2180 if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
2182 if (scrubbing_direction > 0) {
2183 /* was forwards, go backwards */
2184 _session->request_transport_speed (-0.1);
2185 scrubbing_direction = -1;
2187 /* was backwards, go forwards */
2188 _session->request_transport_speed (0.1);
2189 scrubbing_direction = 1;
2192 scrub_reverse_distance = 0;
2193 scrub_reversals = 0;
2197 last_scrub_x = current_x;
2201 Editor::motion_handler (ArdourCanvas::Item* /*item*/, GdkEvent* event, bool from_autoscroll)
2203 _last_motion_y = event->motion.y;
2205 if (event->motion.is_hint) {
2208 /* We call this so that MOTION_NOTIFY events continue to be
2209 delivered to the canvas. We need to do this because we set
2210 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
2211 the density of the events, at the expense of a round-trip
2212 to the server. Given that this will mostly occur on cases
2213 where DISPLAY = :0.0, and given the cost of what the motion
2214 event might do, its a good tradeoff.
2217 track_canvas->get_pointer (x, y);
2220 if (current_stepping_trackview) {
2221 /* don't keep the persistent stepped trackview if the mouse moves */
2222 current_stepping_trackview = 0;
2223 step_timeout.disconnect ();
2226 if (_session && _session->actively_recording()) {
2227 /* Sorry. no dragging stuff around while we record */
2231 JoinObjectRangeState const old = _join_object_range_state;
2232 update_join_object_range_location (event->motion.x, event->motion.y);
2233 if (_join_object_range_state != old) {
2234 set_canvas_cursor ();
2237 if (_over_region_trim_target) {
2238 set_canvas_cursor_for_region_view (event->motion.x, entered_regionview);
2241 bool handled = false;
2242 if (_drags->active ()) {
2243 handled = _drags->motion_handler (event, from_autoscroll);
2250 track_canvas_motion (event);
2255 Editor::can_remove_control_point (ArdourCanvas::Item* item)
2257 ControlPoint* control_point;
2259 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2260 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2264 AutomationLine& line = control_point->line ();
2265 if (dynamic_cast<AudioRegionGainLine*> (&line)) {
2266 /* we shouldn't remove the first or last gain point in region gain lines */
2267 if (line.is_last_point(*control_point) || line.is_first_point(*control_point)) {
2276 Editor::remove_control_point (ArdourCanvas::Item* item)
2278 if (!can_remove_control_point (item)) {
2282 ControlPoint* control_point;
2284 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2285 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2289 control_point->line().remove_point (*control_point);
2293 Editor::edit_control_point (ArdourCanvas::Item* item)
2295 ControlPoint* p = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"));
2298 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2302 ControlPointDialog d (p);
2303 d.set_position (Gtk::WIN_POS_MOUSE);
2306 if (d.run () != RESPONSE_ACCEPT) {
2310 p->line().modify_point_y (*p, d.get_y_fraction ());
2314 Editor::edit_notes (MidiRegionView::Selection const & s)
2320 EditNoteDialog d (&(*s.begin())->region_view(), s);
2321 d.set_position (Gtk::WIN_POS_MOUSE);
2329 Editor::visible_order_range (int* low, int* high) const
2331 *low = TimeAxisView::max_order ();
2334 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
2336 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
2338 if (!rtv->hidden()) {
2340 if (*high < rtv->order()) {
2341 *high = rtv->order ();
2344 if (*low > rtv->order()) {
2345 *low = rtv->order ();
2352 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
2354 /* Either add to or set the set the region selection, unless
2355 this is an alignment click (control used)
2358 if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
2359 TimeAxisView* tv = &rv.get_time_axis_view();
2360 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
2362 if (rtv && rtv->is_track()) {
2363 speed = rtv->track()->speed();
2366 framepos_t where = get_preferred_edit_position();
2370 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
2372 align_region (rv.region(), SyncPoint, (framepos_t) (where * speed));
2374 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
2376 align_region (rv.region(), End, (framepos_t) (where * speed));
2380 align_region (rv.region(), Start, (framepos_t) (where * speed));
2387 Editor::collect_new_region_view (RegionView* rv)
2389 latest_regionviews.push_back (rv);
2393 Editor::collect_and_select_new_region_view (RegionView* rv)
2396 latest_regionviews.push_back (rv);
2400 Editor::cancel_selection ()
2402 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2403 (*i)->hide_selection ();
2406 selection->clear ();
2407 clicked_selection = 0;
2412 Editor::point_trim (GdkEvent* event, framepos_t new_bound)
2414 RegionView* rv = clicked_regionview;
2416 /* Choose action dependant on which button was pressed */
2417 switch (event->button.button) {
2419 begin_reversible_command (_("start point trim"));
2421 if (selection->selected (rv)) {
2422 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
2423 i != selection->regions.by_layer().end(); ++i)
2425 if (!(*i)->region()->locked()) {
2426 (*i)->region()->clear_changes ();
2427 (*i)->region()->trim_front (new_bound);
2428 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2433 if (!rv->region()->locked()) {
2434 rv->region()->clear_changes ();
2435 rv->region()->trim_front (new_bound);
2436 _session->add_command(new StatefulDiffCommand (rv->region()));
2440 commit_reversible_command();
2444 begin_reversible_command (_("End point trim"));
2446 if (selection->selected (rv)) {
2448 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
2450 if (!(*i)->region()->locked()) {
2451 (*i)->region()->clear_changes();
2452 (*i)->region()->trim_end (new_bound);
2453 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2459 if (!rv->region()->locked()) {
2460 rv->region()->clear_changes ();
2461 rv->region()->trim_end (new_bound);
2462 _session->add_command (new StatefulDiffCommand (rv->region()));
2466 commit_reversible_command();
2475 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* /*event*/)
2480 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2481 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2485 Location* location = find_location_from_marker (marker, is_start);
2486 location->set_hidden (true, this);
2491 Editor::reposition_zoom_rect (framepos_t start, framepos_t end)
2493 double x1 = frame_to_pixel (start);
2494 double x2 = frame_to_pixel (end);
2495 double y2 = full_canvas_height - 1.0;
2497 zoom_rect->property_x1() = x1;
2498 zoom_rect->property_y1() = 1.0;
2499 zoom_rect->property_x2() = x2;
2500 zoom_rect->property_y2() = y2;
2505 Editor::mouse_rename_region (ArdourCanvas::Item* /*item*/, GdkEvent* /*event*/)
2507 using namespace Gtkmm2ext;
2509 ArdourPrompter prompter (false);
2511 prompter.set_prompt (_("Name for region:"));
2512 prompter.set_initial_text (clicked_regionview->region()->name());
2513 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
2514 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
2515 prompter.show_all ();
2516 switch (prompter.run ()) {
2517 case Gtk::RESPONSE_ACCEPT:
2519 prompter.get_result(str);
2521 clicked_regionview->region()->set_name (str);
2530 Editor::mouse_brush_insert_region (RegionView* rv, framepos_t pos)
2532 /* no brushing without a useful snap setting */
2534 switch (_snap_mode) {
2536 return; /* can't work because it allows region to be placed anywhere */
2541 switch (_snap_type) {
2549 /* don't brush a copy over the original */
2551 if (pos == rv->region()->position()) {
2555 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
2557 if (rtv == 0 || !rtv->is_track()) {
2561 boost::shared_ptr<Playlist> playlist = rtv->playlist();
2562 double speed = rtv->track()->speed();
2564 playlist->clear_changes ();
2565 boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region(), true));
2566 playlist->add_region (new_region, (framepos_t) (pos * speed));
2567 _session->add_command (new StatefulDiffCommand (playlist));
2569 // playlist is frozen, so we have to update manually XXX this is disgusting
2571 playlist->RegionAdded (new_region); /* EMIT SIGNAL */
2575 Editor::track_height_step_timeout ()
2577 if (get_microseconds() - last_track_height_step_timestamp < 250000) {
2578 current_stepping_trackview = 0;
2585 Editor::add_region_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2587 assert (region_view);
2589 if (!region_view->region()->playlist()) {
2593 _region_motion_group->raise_to_top ();
2595 if (Config->get_edit_mode() == Splice) {
2596 _drags->add (new RegionSpliceDrag (this, item, region_view, selection->regions.by_layer()));
2598 RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id);
2599 _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), false, false));
2602 /* sync the canvas to what we think is its current state */
2603 update_canvas_now();
2607 Editor::add_region_copy_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2609 assert (region_view);
2611 if (!region_view->region()->playlist()) {
2615 _region_motion_group->raise_to_top ();
2617 RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id);
2618 _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), false, true));
2622 Editor::add_region_brush_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2624 assert (region_view);
2626 if (!region_view->region()->playlist()) {
2630 if (Config->get_edit_mode() == Splice) {
2634 RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id);
2635 _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), true, false));
2637 begin_reversible_command (Operations::drag_region_brush);
2640 /** Start a grab where a time range is selected, track(s) are selected, and the
2641 * user clicks and drags a region with a modifier in order to create a new region containing
2642 * the section of the clicked region that lies within the time range.
2645 Editor::start_selection_grab (ArdourCanvas::Item* /*item*/, GdkEvent* event)
2647 if (clicked_regionview == 0) {
2651 /* lets try to create new Region for the selection */
2653 vector<boost::shared_ptr<Region> > new_regions;
2654 create_region_from_selection (new_regions);
2656 if (new_regions.empty()) {
2660 /* XXX fix me one day to use all new regions */
2662 boost::shared_ptr<Region> region (new_regions.front());
2664 /* add it to the current stream/playlist.
2666 tricky: the streamview for the track will add a new regionview. we will
2667 catch the signal it sends when it creates the regionview to
2668 set the regionview we want to then drag.
2671 latest_regionviews.clear();
2672 sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
2674 /* A selection grab currently creates two undo/redo operations, one for
2675 creating the new region and another for moving it.
2678 begin_reversible_command (Operations::selection_grab);
2680 boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
2682 playlist->clear_changes ();
2683 clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
2684 _session->add_command(new StatefulDiffCommand (playlist));
2686 commit_reversible_command ();
2690 if (latest_regionviews.empty()) {
2691 /* something went wrong */
2695 /* we need to deselect all other regionviews, and select this one
2696 i'm ignoring undo stuff, because the region creation will take care of it
2698 selection->set (latest_regionviews);
2700 _drags->set (new RegionMoveDrag (this, latest_regionviews.front()->get_canvas_group(), latest_regionviews.front(), latest_regionviews, false, false), event);
2706 if (_drags->active ()) {
2709 selection->clear ();
2714 Editor::set_internal_edit (bool yn)
2716 if (_internal_editing == yn) {
2720 _internal_editing = yn;
2723 pre_internal_mouse_mode = mouse_mode;
2724 pre_internal_snap_type = _snap_type;
2725 pre_internal_snap_mode = _snap_mode;
2727 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2728 (*i)->enter_internal_edit_mode ();
2731 set_snap_to (internal_snap_type);
2732 set_snap_mode (internal_snap_mode);
2736 internal_snap_mode = _snap_mode;
2737 internal_snap_type = _snap_type;
2739 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2740 (*i)->leave_internal_edit_mode ();
2743 if (mouse_mode == MouseDraw && pre_internal_mouse_mode != MouseDraw) {
2744 /* we were drawing .. flip back to something sensible */
2745 set_mouse_mode (pre_internal_mouse_mode);
2748 set_snap_to (pre_internal_snap_type);
2749 set_snap_mode (pre_internal_snap_mode);
2752 set_canvas_cursor ();
2755 /** Update _join_object_range_state which indicate whether we are over the top or bottom half of a region view,
2756 * used by the `join object/range' tool mode.
2759 Editor::update_join_object_range_location (double /*x*/, double y)
2761 /* XXX: actually, this decides based on whether the mouse is in the top
2762 or bottom half of a the waveform part RouteTimeAxisView;
2764 Note that entered_{track,regionview} is not always setup (e.g. if
2765 the mouse is over a TimeSelection), and to get a Region
2766 that we're over requires searching the playlist.
2769 if (!smart_mode_action->get_active() || (mouse_mode != MouseRange && mouse_mode != MouseObject)) {
2770 _join_object_range_state = JOIN_OBJECT_RANGE_NONE;
2774 if (mouse_mode == MouseObject) {
2775 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2776 } else if (mouse_mode == MouseRange) {
2777 _join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
2780 /* XXX: maybe we should make entered_track work in all cases, rather than resorting to this */
2781 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y + vertical_adjustment.get_value() - canvas_timebars_vsize);
2785 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
2790 rtv->canvas_display()->w2i (cx, cy);
2792 double const c = cy / (rtv->view()->child_height() - TimeAxisViewItem::NAME_HIGHLIGHT_SIZE);
2794 double const f = modf (c, &d);
2796 _join_object_range_state = f < 0.5 ? JOIN_OBJECT_RANGE_RANGE : JOIN_OBJECT_RANGE_OBJECT;
2802 Editor::effective_mouse_mode () const
2804 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
2806 } else if (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE) {
2814 Editor::remove_midi_note (ArdourCanvas::Item* item, GdkEvent *)
2816 ArdourCanvas::CanvasNoteEvent* e = dynamic_cast<ArdourCanvas::CanvasNoteEvent*> (item);
2819 e->region_view().delete_note (e->note ());
2823 Editor::set_canvas_cursor_for_region_view (double x, RegionView* rv)
2827 ArdourCanvas::Group* g = rv->get_canvas_group ();
2828 ArdourCanvas::Group* p = g->get_parent_group ();
2830 /* Compute x in region view parent coordinates */
2834 double x1, x2, y1, y2;
2835 g->get_bounds (x1, y1, x2, y2);
2837 /* Halfway across the region */
2838 double const h = (x1 + x2) / 2;
2840 Trimmable::CanTrim ct = rv->region()->can_trim ();
2842 if (ct & Trimmable::FrontTrimEarlier) {
2843 set_canvas_cursor (_cursors->left_side_trim);
2845 set_canvas_cursor (_cursors->left_side_trim_right_only);
2848 if (ct & Trimmable::EndTrimLater) {
2849 set_canvas_cursor (_cursors->right_side_trim);
2851 set_canvas_cursor (_cursors->right_side_trim_left_only);
2856 /** Obtain the pointer position in world coordinates */
2858 Editor::get_pointer_position (double& x, double& y) const
2861 track_canvas->get_pointer (px, py);
2862 track_canvas->window_to_world (px, py, x, y);