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 ()) {
1006 /* create a MIDI region so that we have somewhere to put automation */
1007 _drags->set (new RegionCreateDrag (this, item, parent), event);
1009 /* rubberband drag to select automation points */
1010 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
1017 if (smart_mode_action->get_active()) {
1018 /* we're in "smart" joined mode, and we've clicked on a Selection */
1019 double const y = event->button.y + vertical_adjustment.get_value() - canvas_timebars_vsize;
1020 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
1022 /* if we're over an automation track, start a drag of its data */
1023 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
1025 _drags->set (new AutomationRangeDrag (this, atv, selection->time), event, _cursors->up_down);
1028 /* if we're over a track and a region, and in the `object' part of a region,
1029 put a selection around the region and drag both
1031 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1032 if (rtv && _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
1033 boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track> (rtv->route ());
1035 boost::shared_ptr<Playlist> pl = t->playlist ();
1038 boost::shared_ptr<Region> r = pl->top_region_at (event_frame (event));
1040 RegionView* rv = rtv->view()->find_view (r);
1041 clicked_selection = select_range (rv->region()->position(),
1042 rv->region()->last_frame()+1);
1043 _drags->add (new SelectionDrag (this, item, SelectionDrag::SelectionMove));
1044 list<RegionView*> rvs;
1046 _drags->add (new RegionMoveDrag (this, item, rv, rvs, false, false));
1047 _drags->start_grab (event);
1058 case ImageFrameHandleStartItem:
1059 imageframe_start_handle_op(item, event) ;
1062 case ImageFrameHandleEndItem:
1063 imageframe_end_handle_op(item, event) ;
1066 case MarkerViewHandleStartItem:
1067 markerview_item_start_handle_op(item, event) ;
1070 case MarkerViewHandleEndItem:
1071 markerview_item_end_handle_op(item, event) ;
1074 case MarkerViewItem:
1075 start_markerview_grab(item, event) ;
1077 case ImageFrameItem:
1078 start_imageframe_grab(item, event) ;
1094 switch (item_type) {
1096 _drags->set (new LineDrag (this, item), event);
1099 case ControlPointItem:
1100 _drags->set (new ControlPointDrag (this, item), event);
1106 AudioRegionView* arv = dynamic_cast<AudioRegionView *> (clicked_regionview);
1108 _drags->set (new AutomationRangeDrag (this, arv, selection->time), event, _cursors->up_down);
1109 _drags->start_grab (event);
1115 case AutomationLineItem:
1116 _drags->set (new LineDrag (this, item), event);
1126 if (event->type == GDK_BUTTON_PRESS) {
1127 _drags->set (new MouseZoomDrag (this, item), event);
1134 if (internal_editing() && item_type == NoteItem) {
1135 /* drag notes if we're in internal edit mode */
1136 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
1138 } else if (clicked_regionview) {
1140 _drags->set (new TimeFXDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1146 _drags->set (new ScrubDrag (this, item), event);
1147 scrub_reversals = 0;
1148 scrub_reverse_distance = 0;
1149 last_scrub_x = event->button.x;
1150 scrubbing_direction = 0;
1151 set_canvas_cursor (_cursors->transparent);
1163 Editor::button_press_handler_2 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1165 Editing::MouseMode const eff = effective_mouse_mode ();
1168 switch (item_type) {
1170 if (internal_editing ()) {
1171 /* no region drags in internal edit mode */
1175 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
1176 add_region_copy_drag (item, event, clicked_regionview);
1178 add_region_drag (item, event, clicked_regionview);
1180 _drags->start_grab (event);
1183 case ControlPointItem:
1184 _drags->set (new ControlPointDrag (this, item), event);
1192 switch (item_type) {
1193 case RegionViewNameHighlight:
1194 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1198 case LeftFrameHandle:
1199 case RightFrameHandle:
1200 if (!internal_editing ()) {
1201 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1206 case RegionViewName:
1207 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer()), event);
1221 /* relax till release */
1227 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1228 temporal_zoom_to_frame (false, event_frame (event));
1230 temporal_zoom_to_frame (true, event_frame(event));
1243 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1245 if (event->type != GDK_BUTTON_PRESS) {
1249 Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->track_canvas->get_window();
1251 if (canvas_window) {
1252 Glib::RefPtr<const Gdk::Window> pointer_window;
1255 Gdk::ModifierType mask;
1257 pointer_window = canvas_window->get_pointer (x, y, mask);
1259 if (pointer_window == track_canvas->get_bin_window()) {
1260 track_canvas->window_to_world (x, y, wx, wy);
1264 pre_press_cursor = current_canvas_cursor;
1266 track_canvas->grab_focus();
1268 if (_session && _session->actively_recording()) {
1274 if (internal_editing()) {
1275 bool leave_internal_edit_mode = false;
1277 switch (item_type) {
1282 if (!dynamic_cast<MidiRegionView*> (clicked_regionview) && !dynamic_cast<AutomationRegionView*> (clicked_regionview)) {
1283 leave_internal_edit_mode = true;
1287 case PlayheadCursorItem:
1289 case TempoMarkerItem:
1290 case MeterMarkerItem:
1294 case RangeMarkerBarItem:
1295 case CdMarkerBarItem:
1296 case TransportMarkerBarItem:
1297 /* button press on these events never does anything to
1298 change the editing mode.
1303 leave_internal_edit_mode = true;
1310 if (leave_internal_edit_mode) {
1311 ActionManager::do_action ("MouseMode", "toggle-internal-edit");
1315 button_selection (item, event, item_type);
1317 if (!_drags->active () &&
1318 (Keyboard::is_delete_event (&event->button) ||
1319 Keyboard::is_context_menu_event (&event->button) ||
1320 Keyboard::is_edit_event (&event->button))) {
1322 /* handled by button release */
1326 switch (event->button.button) {
1328 return button_press_handler_1 (item, event, item_type);
1332 return button_press_handler_2 (item, event, item_type);
1339 return button_press_dispatch (&event->button);
1348 Editor::button_press_dispatch (GdkEventButton* ev)
1350 /* this function is intended only for buttons 4 and above.
1353 Gtkmm2ext::MouseButton b (ev->state, ev->button);
1354 return button_bindings->activate (b, Gtkmm2ext::Bindings::Press);
1358 Editor::button_release_dispatch (GdkEventButton* ev)
1360 /* this function is intended only for buttons 4 and above.
1363 Gtkmm2ext::MouseButton b (ev->state, ev->button);
1364 return button_bindings->activate (b, Gtkmm2ext::Bindings::Release);
1368 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1370 framepos_t where = event_frame (event, 0, 0);
1371 AutomationTimeAxisView* atv = 0;
1373 if (pre_press_cursor) {
1374 set_canvas_cursor (pre_press_cursor);
1375 pre_press_cursor = 0;
1378 /* no action if we're recording */
1380 if (_session && _session->actively_recording()) {
1384 /* see if we're finishing a drag */
1386 bool were_dragging = false;
1387 if (_drags->active ()) {
1388 bool const r = _drags->end_grab (event);
1390 /* grab dragged, so do nothing else */
1394 were_dragging = true;
1397 update_region_layering_order_editor ();
1399 /* edit events get handled here */
1401 if (!_drags->active () && Keyboard::is_edit_event (&event->button)) {
1402 switch (item_type) {
1404 show_region_properties ();
1407 case TempoMarkerItem:
1408 edit_tempo_marker (item);
1411 case MeterMarkerItem:
1412 edit_meter_marker (item);
1415 case RegionViewName:
1416 if (clicked_regionview->name_active()) {
1417 return mouse_rename_region (item, event);
1421 case ControlPointItem:
1422 edit_control_point (item);
1435 /* context menu events get handled here */
1437 if (Keyboard::is_context_menu_event (&event->button)) {
1439 context_click_event = *event;
1441 if (!_drags->active ()) {
1443 /* no matter which button pops up the context menu, tell the menu
1444 widget to use button 1 to drive menu selection.
1447 switch (item_type) {
1449 case FadeInHandleItem:
1451 case FadeOutHandleItem:
1452 popup_fade_context_menu (1, event->button.time, item, item_type);
1455 case StartCrossFadeItem:
1456 popup_xfade_in_context_menu (1, event->button.time, item, item_type);
1459 case EndCrossFadeItem:
1460 popup_xfade_out_context_menu (1, event->button.time, item, item_type);
1464 popup_track_context_menu (1, event->button.time, item_type, false);
1468 case RegionViewNameHighlight:
1469 case LeftFrameHandle:
1470 case RightFrameHandle:
1471 case RegionViewName:
1472 popup_track_context_menu (1, event->button.time, item_type, false);
1476 popup_track_context_menu (1, event->button.time, item_type, true);
1479 case AutomationTrackItem:
1480 popup_track_context_menu (1, event->button.time, item_type, false);
1484 case RangeMarkerBarItem:
1485 case TransportMarkerBarItem:
1486 case CdMarkerBarItem:
1489 popup_ruler_menu (where, item_type);
1493 marker_context_menu (&event->button, item);
1496 case TempoMarkerItem:
1497 tempo_or_meter_marker_context_menu (&event->button, item);
1500 case MeterMarkerItem:
1501 tempo_or_meter_marker_context_menu (&event->button, item);
1504 case CrossfadeViewItem:
1505 popup_track_context_menu (1, event->button.time, item_type, false);
1508 case ControlPointItem:
1509 popup_control_point_context_menu (item, event);
1513 case ImageFrameItem:
1514 popup_imageframe_edit_menu(1, event->button.time, item, true) ;
1516 case ImageFrameTimeAxisItem:
1517 popup_imageframe_edit_menu(1, event->button.time, item, false) ;
1519 case MarkerViewItem:
1520 popup_marker_time_axis_edit_menu(1, event->button.time, item, true) ;
1522 case MarkerTimeAxisItem:
1523 popup_marker_time_axis_edit_menu(1, event->button.time, item, false) ;
1535 /* delete events get handled here */
1537 Editing::MouseMode const eff = effective_mouse_mode ();
1539 if (!_drags->active () && Keyboard::is_delete_event (&event->button)) {
1541 switch (item_type) {
1542 case TempoMarkerItem:
1543 remove_tempo_marker (item);
1546 case MeterMarkerItem:
1547 remove_meter_marker (item);
1551 remove_marker (*item, event);
1555 if (eff == MouseObject) {
1556 remove_clicked_region ();
1560 case ControlPointItem:
1561 remove_control_point (item);
1565 remove_midi_note (item, event);
1574 switch (event->button.button) {
1577 switch (item_type) {
1578 /* see comments in button_press_handler */
1579 case PlayheadCursorItem:
1582 case AutomationLineItem:
1583 case StartSelectionTrimItem:
1584 case EndSelectionTrimItem:
1588 if (!_dragging_playhead) {
1589 snap_to_with_modifier (where, event, 0, true);
1590 mouse_add_new_marker (where);
1594 case CdMarkerBarItem:
1595 if (!_dragging_playhead) {
1596 // if we get here then a dragged range wasn't done
1597 snap_to_with_modifier (where, event, 0, true);
1598 mouse_add_new_marker (where, true);
1603 if (!_dragging_playhead) {
1604 snap_to_with_modifier (where, event);
1605 mouse_add_new_tempo_event (where);
1610 if (!_dragging_playhead) {
1611 mouse_add_new_meter_event (pixel_to_frame (event->button.x));
1622 switch (item_type) {
1623 case AutomationTrackItem:
1624 atv = dynamic_cast<AutomationTimeAxisView*>(clicked_axisview);
1626 atv->add_automation_event (event, where, event->button.y);
1636 switch (item_type) {
1639 /* check that we didn't drag before releasing, since
1640 its really annoying to create new control
1641 points when doing this.
1643 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (clicked_regionview);
1644 if (!were_dragging && arv) {
1645 arv->add_gain_point_event (item, event);
1651 case AutomationTrackItem:
1652 dynamic_cast<AutomationTimeAxisView*>(clicked_axisview)->
1653 add_automation_event (event, where, event->button.y);
1662 set_canvas_cursor (current_canvas_cursor);
1663 if (scrubbing_direction == 0) {
1664 /* no drag, just a click */
1665 switch (item_type) {
1667 play_selected_region ();
1673 /* make sure we stop */
1674 _session->request_transport_speed (0.0);
1683 /* do any (de)selection operations that should occur on button release */
1684 button_selection (item, event, item_type);
1693 switch (item_type) {
1695 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1697 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier|Keyboard::SecondaryModifier))) {
1700 // Button2 click is unused
1715 // x_style_paste (where, 1.0);
1736 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1743 switch (item_type) {
1744 case ControlPointItem:
1745 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1746 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1747 cp->set_visible (true);
1751 at_y = cp->get_y ();
1752 cp->i2w (at_x, at_y);
1756 fraction = 1.0 - (cp->get_y() / cp->line().height());
1758 if (is_drawable() && !_drags->active ()) {
1759 set_canvas_cursor (_cursors->fader);
1762 _verbose_cursor->set (cp->line().get_verbose_cursor_string (fraction), at_x, at_y);
1763 _verbose_cursor->show ();
1768 if (mouse_mode == MouseGain) {
1769 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1771 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredGainLine.get();
1772 if (is_drawable()) {
1773 set_canvas_cursor (_cursors->fader);
1778 case AutomationLineItem:
1779 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1780 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1782 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredAutomationLine.get();
1784 if (is_drawable()) {
1785 set_canvas_cursor (_cursors->fader);
1790 case RegionViewNameHighlight:
1791 if (is_drawable() && doing_object_stuff() && entered_regionview) {
1792 set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1793 _over_region_trim_target = true;
1797 case LeftFrameHandle:
1798 case RightFrameHandle:
1799 if (is_drawable() && doing_object_stuff() && !internal_editing() && entered_regionview) {
1800 set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1804 case StartSelectionTrimItem:
1806 case ImageFrameHandleStartItem:
1807 case MarkerViewHandleStartItem:
1809 if (is_drawable()) {
1810 set_canvas_cursor (_cursors->left_side_trim);
1813 case EndSelectionTrimItem:
1815 case ImageFrameHandleEndItem:
1816 case MarkerViewHandleEndItem:
1818 if (is_drawable()) {
1819 set_canvas_cursor (_cursors->right_side_trim);
1823 case PlayheadCursorItem:
1824 if (is_drawable()) {
1825 switch (_edit_point) {
1827 set_canvas_cursor (_cursors->grabber_edit_point);
1830 set_canvas_cursor (_cursors->grabber);
1836 case RegionViewName:
1838 /* when the name is not an active item, the entire name highlight is for trimming */
1840 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1841 if (mouse_mode == MouseObject && is_drawable()) {
1842 set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1843 _over_region_trim_target = true;
1849 case AutomationTrackItem:
1850 if (is_drawable()) {
1851 Gdk::Cursor *cursor;
1852 switch (mouse_mode) {
1854 cursor = _cursors->selector;
1857 cursor = _cursors->zoom_in;
1860 cursor = _cursors->cross_hair;
1864 set_canvas_cursor (cursor);
1866 AutomationTimeAxisView* atv;
1867 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1868 clear_entered_track = false;
1869 set_entered_track (atv);
1875 case RangeMarkerBarItem:
1876 case TransportMarkerBarItem:
1877 case CdMarkerBarItem:
1880 if (is_drawable()) {
1881 set_canvas_cursor (_cursors->timebar);
1886 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1889 entered_marker = marker;
1890 marker->set_color_rgba (ARDOUR_UI::config()->canvasvar_EnteredMarker.get());
1892 case MeterMarkerItem:
1893 case TempoMarkerItem:
1894 if (is_drawable()) {
1895 set_canvas_cursor (_cursors->timebar);
1899 case FadeInHandleItem:
1900 if (mouse_mode == MouseObject && !internal_editing()) {
1901 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1903 rect->property_fill_color_rgba() = 0xBBBBBBAA;
1905 set_canvas_cursor (_cursors->fade_in);
1909 case FadeOutHandleItem:
1910 if (mouse_mode == MouseObject && !internal_editing()) {
1911 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1913 rect->property_fill_color_rgba() = 0xBBBBBBAA;
1915 set_canvas_cursor (_cursors->fade_out);
1918 case FeatureLineItem:
1920 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1921 line->property_fill_color_rgba() = 0xFF0000FF;
1925 if (smart_mode_action->get_active()) {
1926 set_canvas_cursor ();
1934 /* second pass to handle entered track status in a comprehensible way.
1937 switch (item_type) {
1939 case AutomationLineItem:
1940 case ControlPointItem:
1941 /* these do not affect the current entered track state */
1942 clear_entered_track = false;
1945 case AutomationTrackItem:
1946 /* handled above already */
1950 set_entered_track (0);
1958 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent*, ItemType item_type)
1968 switch (item_type) {
1969 case ControlPointItem:
1970 cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
1971 if (cp->line().the_list()->interpolation() != AutomationList::Discrete) {
1972 if (cp->line().npoints() > 1 && !cp->get_selected()) {
1973 cp->set_visible (false);
1977 if (is_drawable()) {
1978 set_canvas_cursor (current_canvas_cursor);
1981 _verbose_cursor->hide ();
1984 case RegionViewNameHighlight:
1985 case LeftFrameHandle:
1986 case RightFrameHandle:
1987 case StartSelectionTrimItem:
1988 case EndSelectionTrimItem:
1989 case PlayheadCursorItem:
1992 case ImageFrameHandleStartItem:
1993 case ImageFrameHandleEndItem:
1994 case MarkerViewHandleStartItem:
1995 case MarkerViewHandleEndItem:
1998 _over_region_trim_target = false;
2000 if (is_drawable()) {
2001 set_canvas_cursor (current_canvas_cursor);
2006 case AutomationLineItem:
2007 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
2009 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
2011 line->property_fill_color_rgba() = al->get_line_color();
2013 if (is_drawable()) {
2014 set_canvas_cursor (current_canvas_cursor);
2018 case RegionViewName:
2019 /* see enter_handler() for notes */
2020 _over_region_trim_target = false;
2022 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
2023 if (is_drawable() && mouse_mode == MouseObject) {
2024 set_canvas_cursor (current_canvas_cursor);
2029 case RangeMarkerBarItem:
2030 case TransportMarkerBarItem:
2031 case CdMarkerBarItem:
2035 if (is_drawable()) {
2036 set_canvas_cursor (current_canvas_cursor);
2041 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2045 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
2046 location_flags_changed (loc, this);
2049 case MeterMarkerItem:
2050 case TempoMarkerItem:
2052 if (is_drawable()) {
2053 set_canvas_cursor (current_canvas_cursor);
2058 case FadeInHandleItem:
2059 case FadeOutHandleItem:
2060 rv = static_cast<RegionView*>(item->get_data ("regionview"));
2062 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
2064 rect->property_fill_color_rgba() = rv->get_fill_color();
2065 rect->property_outline_pixels() = 0;
2068 set_canvas_cursor (current_canvas_cursor);
2071 case AutomationTrackItem:
2072 if (is_drawable()) {
2073 set_canvas_cursor (current_canvas_cursor);
2074 clear_entered_track = true;
2075 Glib::signal_idle().connect (sigc::mem_fun(*this, &Editor::left_automation_track));
2078 case FeatureLineItem:
2080 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
2081 line->property_fill_color_rgba() = (guint) ARDOUR_UI::config()->canvasvar_ZeroLine.get();;
2093 Editor::left_automation_track ()
2095 if (clear_entered_track) {
2096 set_entered_track (0);
2097 clear_entered_track = false;
2103 Editor::scrub (framepos_t frame, double current_x)
2107 if (scrubbing_direction == 0) {
2109 _session->request_locate (frame, false);
2110 _session->request_transport_speed (0.1);
2111 scrubbing_direction = 1;
2115 if (last_scrub_x > current_x) {
2117 /* pointer moved to the left */
2119 if (scrubbing_direction > 0) {
2121 /* we reversed direction to go backwards */
2124 scrub_reverse_distance += (int) (last_scrub_x - current_x);
2128 /* still moving to the left (backwards) */
2130 scrub_reversals = 0;
2131 scrub_reverse_distance = 0;
2133 delta = 0.01 * (last_scrub_x - current_x);
2134 _session->request_transport_speed_nonzero (_session->transport_speed() - delta);
2138 /* pointer moved to the right */
2140 if (scrubbing_direction < 0) {
2141 /* we reversed direction to go forward */
2144 scrub_reverse_distance += (int) (current_x - last_scrub_x);
2147 /* still moving to the right */
2149 scrub_reversals = 0;
2150 scrub_reverse_distance = 0;
2152 delta = 0.01 * (current_x - last_scrub_x);
2153 _session->request_transport_speed_nonzero (_session->transport_speed() + delta);
2157 /* if there have been more than 2 opposite motion moves detected, or one that moves
2158 back more than 10 pixels, reverse direction
2161 if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
2163 if (scrubbing_direction > 0) {
2164 /* was forwards, go backwards */
2165 _session->request_transport_speed (-0.1);
2166 scrubbing_direction = -1;
2168 /* was backwards, go forwards */
2169 _session->request_transport_speed (0.1);
2170 scrubbing_direction = 1;
2173 scrub_reverse_distance = 0;
2174 scrub_reversals = 0;
2178 last_scrub_x = current_x;
2182 Editor::motion_handler (ArdourCanvas::Item* /*item*/, GdkEvent* event, bool from_autoscroll)
2184 _last_motion_y = event->motion.y;
2186 if (event->motion.is_hint) {
2189 /* We call this so that MOTION_NOTIFY events continue to be
2190 delivered to the canvas. We need to do this because we set
2191 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
2192 the density of the events, at the expense of a round-trip
2193 to the server. Given that this will mostly occur on cases
2194 where DISPLAY = :0.0, and given the cost of what the motion
2195 event might do, its a good tradeoff.
2198 track_canvas->get_pointer (x, y);
2201 if (current_stepping_trackview) {
2202 /* don't keep the persistent stepped trackview if the mouse moves */
2203 current_stepping_trackview = 0;
2204 step_timeout.disconnect ();
2207 if (_session && _session->actively_recording()) {
2208 /* Sorry. no dragging stuff around while we record */
2212 JoinObjectRangeState const old = _join_object_range_state;
2213 update_join_object_range_location (event->motion.x, event->motion.y);
2214 if (_join_object_range_state != old) {
2215 set_canvas_cursor ();
2218 if (_over_region_trim_target) {
2219 set_canvas_cursor_for_region_view (event->motion.x, entered_regionview);
2222 bool handled = false;
2223 if (_drags->active ()) {
2224 handled = _drags->motion_handler (event, from_autoscroll);
2231 track_canvas_motion (event);
2236 Editor::can_remove_control_point (ArdourCanvas::Item* item)
2238 ControlPoint* control_point;
2240 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2241 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2245 AutomationLine& line = control_point->line ();
2246 if (dynamic_cast<AudioRegionGainLine*> (&line)) {
2247 /* we shouldn't remove the first or last gain point in region gain lines */
2248 if (line.is_last_point(*control_point) || line.is_first_point(*control_point)) {
2257 Editor::remove_control_point (ArdourCanvas::Item* item)
2259 if (!can_remove_control_point (item)) {
2263 ControlPoint* control_point;
2265 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2266 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2270 control_point->line().remove_point (*control_point);
2274 Editor::edit_control_point (ArdourCanvas::Item* item)
2276 ControlPoint* p = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"));
2279 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2283 ControlPointDialog d (p);
2284 d.set_position (Gtk::WIN_POS_MOUSE);
2287 if (d.run () != RESPONSE_ACCEPT) {
2291 p->line().modify_point_y (*p, d.get_y_fraction ());
2295 Editor::edit_note (ArdourCanvas::Item* item)
2297 ArdourCanvas::CanvasNoteEvent* e = dynamic_cast<ArdourCanvas::CanvasNoteEvent*> (item);
2300 EditNoteDialog d (&e->region_view(), e);
2301 d.set_position (Gtk::WIN_POS_MOUSE);
2309 Editor::visible_order_range (int* low, int* high) const
2311 *low = TimeAxisView::max_order ();
2314 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
2316 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
2318 if (!rtv->hidden()) {
2320 if (*high < rtv->order()) {
2321 *high = rtv->order ();
2324 if (*low > rtv->order()) {
2325 *low = rtv->order ();
2332 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
2334 /* Either add to or set the set the region selection, unless
2335 this is an alignment click (control used)
2338 if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
2339 TimeAxisView* tv = &rv.get_time_axis_view();
2340 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
2342 if (rtv && rtv->is_track()) {
2343 speed = rtv->track()->speed();
2346 framepos_t where = get_preferred_edit_position();
2350 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
2352 align_region (rv.region(), SyncPoint, (framepos_t) (where * speed));
2354 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
2356 align_region (rv.region(), End, (framepos_t) (where * speed));
2360 align_region (rv.region(), Start, (framepos_t) (where * speed));
2367 Editor::collect_new_region_view (RegionView* rv)
2369 latest_regionviews.push_back (rv);
2373 Editor::collect_and_select_new_region_view (RegionView* rv)
2376 latest_regionviews.push_back (rv);
2380 Editor::cancel_selection ()
2382 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2383 (*i)->hide_selection ();
2386 selection->clear ();
2387 clicked_selection = 0;
2392 Editor::point_trim (GdkEvent* event, framepos_t new_bound)
2394 RegionView* rv = clicked_regionview;
2396 /* Choose action dependant on which button was pressed */
2397 switch (event->button.button) {
2399 begin_reversible_command (_("start point trim"));
2401 if (selection->selected (rv)) {
2402 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
2403 i != selection->regions.by_layer().end(); ++i)
2405 if (!(*i)->region()->locked()) {
2406 (*i)->region()->clear_changes ();
2407 (*i)->region()->trim_front (new_bound);
2408 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2413 if (!rv->region()->locked()) {
2414 rv->region()->clear_changes ();
2415 rv->region()->trim_front (new_bound);
2416 _session->add_command(new StatefulDiffCommand (rv->region()));
2420 commit_reversible_command();
2424 begin_reversible_command (_("End point trim"));
2426 if (selection->selected (rv)) {
2428 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
2430 if (!(*i)->region()->locked()) {
2431 (*i)->region()->clear_changes();
2432 (*i)->region()->trim_end (new_bound);
2433 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2439 if (!rv->region()->locked()) {
2440 rv->region()->clear_changes ();
2441 rv->region()->trim_end (new_bound);
2442 _session->add_command (new StatefulDiffCommand (rv->region()));
2446 commit_reversible_command();
2455 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* /*event*/)
2460 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2461 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2465 Location* location = find_location_from_marker (marker, is_start);
2466 location->set_hidden (true, this);
2471 Editor::reposition_zoom_rect (framepos_t start, framepos_t end)
2473 double x1 = frame_to_pixel (start);
2474 double x2 = frame_to_pixel (end);
2475 double y2 = full_canvas_height - 1.0;
2477 zoom_rect->property_x1() = x1;
2478 zoom_rect->property_y1() = 1.0;
2479 zoom_rect->property_x2() = x2;
2480 zoom_rect->property_y2() = y2;
2485 Editor::mouse_rename_region (ArdourCanvas::Item* /*item*/, GdkEvent* /*event*/)
2487 using namespace Gtkmm2ext;
2489 ArdourPrompter prompter (false);
2491 prompter.set_prompt (_("Name for region:"));
2492 prompter.set_initial_text (clicked_regionview->region()->name());
2493 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
2494 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
2495 prompter.show_all ();
2496 switch (prompter.run ()) {
2497 case Gtk::RESPONSE_ACCEPT:
2499 prompter.get_result(str);
2501 clicked_regionview->region()->set_name (str);
2510 Editor::mouse_brush_insert_region (RegionView* rv, framepos_t pos)
2512 /* no brushing without a useful snap setting */
2514 switch (_snap_mode) {
2516 return; /* can't work because it allows region to be placed anywhere */
2521 switch (_snap_type) {
2529 /* don't brush a copy over the original */
2531 if (pos == rv->region()->position()) {
2535 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
2537 if (rtv == 0 || !rtv->is_track()) {
2541 boost::shared_ptr<Playlist> playlist = rtv->playlist();
2542 double speed = rtv->track()->speed();
2544 playlist->clear_changes ();
2545 boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region(), true));
2546 playlist->add_region (new_region, (framepos_t) (pos * speed));
2547 _session->add_command (new StatefulDiffCommand (playlist));
2549 // playlist is frozen, so we have to update manually XXX this is disgusting
2551 playlist->RegionAdded (new_region); /* EMIT SIGNAL */
2555 Editor::track_height_step_timeout ()
2557 if (get_microseconds() - last_track_height_step_timestamp < 250000) {
2558 current_stepping_trackview = 0;
2565 Editor::add_region_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2567 assert (region_view);
2569 if (!region_view->region()->playlist()) {
2573 _region_motion_group->raise_to_top ();
2575 if (Config->get_edit_mode() == Splice) {
2576 _drags->add (new RegionSpliceDrag (this, item, region_view, selection->regions.by_layer()));
2578 RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id);
2579 _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), false, false));
2582 /* sync the canvas to what we think is its current state */
2583 update_canvas_now();
2587 Editor::add_region_copy_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2589 assert (region_view);
2591 if (!region_view->region()->playlist()) {
2595 _region_motion_group->raise_to_top ();
2597 RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id);
2598 _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), false, true));
2602 Editor::add_region_brush_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2604 assert (region_view);
2606 if (!region_view->region()->playlist()) {
2610 if (Config->get_edit_mode() == Splice) {
2614 RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id);
2615 _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), true, false));
2617 begin_reversible_command (Operations::drag_region_brush);
2620 /** Start a grab where a time range is selected, track(s) are selected, and the
2621 * user clicks and drags a region with a modifier in order to create a new region containing
2622 * the section of the clicked region that lies within the time range.
2625 Editor::start_selection_grab (ArdourCanvas::Item* /*item*/, GdkEvent* event)
2627 if (clicked_regionview == 0) {
2631 /* lets try to create new Region for the selection */
2633 vector<boost::shared_ptr<Region> > new_regions;
2634 create_region_from_selection (new_regions);
2636 if (new_regions.empty()) {
2640 /* XXX fix me one day to use all new regions */
2642 boost::shared_ptr<Region> region (new_regions.front());
2644 /* add it to the current stream/playlist.
2646 tricky: the streamview for the track will add a new regionview. we will
2647 catch the signal it sends when it creates the regionview to
2648 set the regionview we want to then drag.
2651 latest_regionviews.clear();
2652 sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
2654 /* A selection grab currently creates two undo/redo operations, one for
2655 creating the new region and another for moving it.
2658 begin_reversible_command (Operations::selection_grab);
2660 boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
2662 playlist->clear_changes ();
2663 clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
2664 _session->add_command(new StatefulDiffCommand (playlist));
2666 commit_reversible_command ();
2670 if (latest_regionviews.empty()) {
2671 /* something went wrong */
2675 /* we need to deselect all other regionviews, and select this one
2676 i'm ignoring undo stuff, because the region creation will take care of it
2678 selection->set (latest_regionviews);
2680 _drags->set (new RegionMoveDrag (this, latest_regionviews.front()->get_canvas_group(), latest_regionviews.front(), latest_regionviews, false, false), event);
2686 if (_drags->active ()) {
2689 selection->clear ();
2694 Editor::set_internal_edit (bool yn)
2696 if (_internal_editing == yn) {
2700 _internal_editing = yn;
2703 pre_internal_mouse_mode = mouse_mode;
2704 pre_internal_snap_type = _snap_type;
2705 pre_internal_snap_mode = _snap_mode;
2707 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2708 (*i)->enter_internal_edit_mode ();
2711 set_snap_to (internal_snap_type);
2712 set_snap_mode (internal_snap_mode);
2716 internal_snap_mode = _snap_mode;
2717 internal_snap_type = _snap_type;
2719 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2720 (*i)->leave_internal_edit_mode ();
2723 if (mouse_mode == MouseDraw && pre_internal_mouse_mode != MouseDraw) {
2724 /* we were drawing .. flip back to something sensible */
2725 set_mouse_mode (pre_internal_mouse_mode);
2728 set_snap_to (pre_internal_snap_type);
2729 set_snap_mode (pre_internal_snap_mode);
2732 set_canvas_cursor ();
2735 /** Update _join_object_range_state which indicate whether we are over the top or bottom half of a region view,
2736 * used by the `join object/range' tool mode.
2739 Editor::update_join_object_range_location (double /*x*/, double y)
2741 /* XXX: actually, this decides based on whether the mouse is in the top
2742 or bottom half of a the waveform part RouteTimeAxisView;
2744 Note that entered_{track,regionview} is not always setup (e.g. if
2745 the mouse is over a TimeSelection), and to get a Region
2746 that we're over requires searching the playlist.
2749 if (!smart_mode_action->get_active() || (mouse_mode != MouseRange && mouse_mode != MouseObject)) {
2750 _join_object_range_state = JOIN_OBJECT_RANGE_NONE;
2754 if (mouse_mode == MouseObject) {
2755 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2756 } else if (mouse_mode == MouseRange) {
2757 _join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
2760 /* XXX: maybe we should make entered_track work in all cases, rather than resorting to this */
2761 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y + vertical_adjustment.get_value() - canvas_timebars_vsize);
2765 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
2770 rtv->canvas_display()->w2i (cx, cy);
2772 double const c = cy / (rtv->view()->child_height() - TimeAxisViewItem::NAME_HIGHLIGHT_SIZE);
2774 double const f = modf (c, &d);
2776 _join_object_range_state = f < 0.5 ? JOIN_OBJECT_RANGE_RANGE : JOIN_OBJECT_RANGE_OBJECT;
2782 Editor::effective_mouse_mode () const
2784 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
2786 } else if (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE) {
2794 Editor::remove_midi_note (ArdourCanvas::Item* item, GdkEvent *)
2796 ArdourCanvas::CanvasNoteEvent* e = dynamic_cast<ArdourCanvas::CanvasNoteEvent*> (item);
2799 e->region_view().delete_note (e->note ());
2803 Editor::set_canvas_cursor_for_region_view (double x, RegionView* rv)
2807 ArdourCanvas::Group* g = rv->get_canvas_group ();
2808 ArdourCanvas::Group* p = g->get_parent_group ();
2810 /* Compute x in region view parent coordinates */
2814 double x1, x2, y1, y2;
2815 g->get_bounds (x1, y1, x2, y2);
2817 /* Halfway across the region */
2818 double const h = (x1 + x2) / 2;
2820 Trimmable::CanTrim ct = rv->region()->can_trim ();
2822 if (ct & Trimmable::FrontTrimEarlier) {
2823 set_canvas_cursor (_cursors->left_side_trim);
2825 set_canvas_cursor (_cursors->left_side_trim_right_only);
2828 if (ct & Trimmable::EndTrimLater) {
2829 set_canvas_cursor (_cursors->right_side_trim);
2831 set_canvas_cursor (_cursors->right_side_trim_left_only);
2836 /** Obtain the pointer position in world coordinates */
2838 Editor::get_pointer_position (double& x, double& y) const
2841 track_canvas->get_pointer (px, py);
2842 track_canvas->window_to_world (px, py, x, y);