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 if (_internal_editing) {
252 switch (mouse_mode) {
254 current_canvas_cursor = _cursors->midi_pencil;
258 current_canvas_cursor = which_grabber_cursor();
262 current_canvas_cursor = _cursors->midi_resize;
271 switch (mouse_mode) {
273 current_canvas_cursor = _cursors->selector;
277 current_canvas_cursor = which_grabber_cursor();
281 /* shouldn't be possible, but just cover it anyway ... */
282 current_canvas_cursor = _cursors->midi_pencil;
286 current_canvas_cursor = _cursors->cross_hair;
290 if (Keyboard::the_keyboard().key_is_down (GDK_Control_L)) {
291 current_canvas_cursor = _cursors->zoom_out;
293 current_canvas_cursor = _cursors->zoom_in;
298 current_canvas_cursor = _cursors->time_fx; // just use playhead
302 current_canvas_cursor = _cursors->speaker;
307 switch (_join_object_range_state) {
308 case JOIN_OBJECT_RANGE_NONE:
310 case JOIN_OBJECT_RANGE_OBJECT:
311 current_canvas_cursor = which_grabber_cursor ();
313 case JOIN_OBJECT_RANGE_RANGE:
314 current_canvas_cursor = _cursors->selector;
318 /* up-down cursor as a cue that automation can be dragged up and down when in join object/range mode */
319 if (smart_mode_action->get_active()) {
321 get_pointer_position (x, y);
322 ArdourCanvas::Item* i = track_canvas->get_item_at (x, y);
323 if (i && i->property_parent() && (*i->property_parent()).get_data (X_("timeselection"))) {
324 pair<TimeAxisView*, int> tvp = trackview_by_y_position (_last_motion_y + vertical_adjustment.get_value() - canvas_timebars_vsize);
325 if (dynamic_cast<AutomationTimeAxisView*> (tvp.first)) {
326 current_canvas_cursor = _cursors->up_down;
331 set_canvas_cursor (current_canvas_cursor, true);
335 Editor::set_mouse_mode (MouseMode m, bool force)
337 if (_drags->active ()) {
341 if (!force && m == mouse_mode) {
345 Glib::RefPtr<Action> act;
349 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-range"));
353 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-object"));
357 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-draw"));
361 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-gain"));
365 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-zoom"));
369 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-timefx"));
373 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-audition"));
379 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
382 /* go there and back to ensure that the toggled handler is called to set up mouse_mode */
383 tact->set_active (false);
384 tact->set_active (true);
386 MouseModeChanged (); /* EMIT SIGNAL */
390 Editor::mouse_mode_toggled (MouseMode m)
392 Glib::RefPtr<Action> act;
393 Glib::RefPtr<ToggleAction> tact;
397 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-range"));
401 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-object"));
405 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-draw"));
409 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-gain"));
413 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-zoom"));
417 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-timefx"));
421 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-audition"));
427 tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
430 if (!tact->get_active()) {
431 /* this was just the notification that the old mode has been
432 * left. we'll get called again with the new mode active in a
440 act = ActionManager::get_action (X_("MouseMode"), X_("toggle-internal-edit"));
441 tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
442 tact->set_active (true);
452 if (!internal_editing()) {
453 if (mouse_mode != MouseRange && mouse_mode != MouseGain && _join_object_range_state == JOIN_OBJECT_RANGE_NONE) {
455 /* in all modes except range, gain and joined object/range, hide the range selection,
456 show the object (region) selection.
459 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
460 (*i)->hide_selection ();
466 in range or object/range mode, show the range selection.
469 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
470 (*i)->show_selection (selection->time);
475 set_canvas_cursor ();
477 MouseModeChanged (); /* EMIT SIGNAL */
481 Editor::step_mouse_mode (bool next)
483 switch (current_mouse_mode()) {
486 if (Profile->get_sae()) {
487 set_mouse_mode (MouseZoom);
489 set_mouse_mode (MouseRange);
492 set_mouse_mode (MouseTimeFX);
497 if (next) set_mouse_mode (MouseDraw);
498 else set_mouse_mode (MouseObject);
502 if (next) set_mouse_mode (MouseZoom);
503 else set_mouse_mode (MouseRange);
508 if (Profile->get_sae()) {
509 set_mouse_mode (MouseTimeFX);
511 set_mouse_mode (MouseGain);
514 if (Profile->get_sae()) {
515 set_mouse_mode (MouseObject);
517 set_mouse_mode (MouseDraw);
523 if (next) set_mouse_mode (MouseTimeFX);
524 else set_mouse_mode (MouseZoom);
529 set_mouse_mode (MouseAudition);
531 if (Profile->get_sae()) {
532 set_mouse_mode (MouseZoom);
534 set_mouse_mode (MouseGain);
540 if (next) set_mouse_mode (MouseObject);
541 else set_mouse_mode (MouseTimeFX);
547 Editor::toggle_internal_editing_from_double_click (GdkEvent* event)
549 if (_drags->active()) {
550 _drags->end_grab (event);
553 ActionManager::do_action ("MouseMode", "toggle-internal-edit");
555 /* prevent reversion of edit cursor on button release */
557 pre_press_cursor = 0;
563 Editor::button_selection (ArdourCanvas::Item* /*item*/, GdkEvent* event, ItemType item_type)
565 /* in object/audition/timefx/gain-automation mode,
566 any button press sets the selection if the object
567 can be selected. this is a bit of hack, because
568 we want to avoid this if the mouse operation is a
571 note: not dbl-click or triple-click
573 Also note that there is no region selection in internal edit mode, otherwise
574 for operations operating on the selection (e.g. cut) it is not obvious whether
575 to cut notes or regions.
578 if (((mouse_mode != MouseObject) &&
579 (_join_object_range_state != JOIN_OBJECT_RANGE_OBJECT) &&
580 (mouse_mode != MouseAudition || item_type != RegionItem) &&
581 (mouse_mode != MouseTimeFX || item_type != RegionItem) &&
582 (mouse_mode != MouseGain) &&
583 (mouse_mode != MouseRange) &&
584 (mouse_mode != MouseDraw)) ||
585 ((event->type != GDK_BUTTON_PRESS && event->type != GDK_BUTTON_RELEASE) || event->button.button > 3) ||
586 internal_editing()) {
591 if (event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) {
593 if ((event->button.state & Keyboard::RelevantModifierKeyMask) && event->button.button != 1) {
595 /* almost no selection action on modified button-2 or button-3 events */
597 if (item_type != RegionItem && event->button.button != 2) {
603 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
604 bool press = (event->type == GDK_BUTTON_PRESS);
608 if (!doing_range_stuff()) {
609 set_selected_regionview_from_click (press, op);
613 if (doing_range_stuff()) {
614 /* don't change the selection unless the
615 clicked track is not currently selected. if
616 so, "collapse" the selection to just this
619 if (!selection->selected (clicked_axisview)) {
620 set_selected_track_as_side_effect (Selection::Set);
626 case RegionViewNameHighlight:
628 case LeftFrameHandle:
629 case RightFrameHandle:
630 if (doing_object_stuff() || (mouse_mode != MouseRange && mouse_mode != MouseObject)) {
631 set_selected_regionview_from_click (press, op);
632 } else if (event->type == GDK_BUTTON_PRESS) {
633 set_selected_track_as_side_effect (op);
637 case FadeInHandleItem:
639 case FadeOutHandleItem:
641 case StartCrossFadeItem:
642 case EndCrossFadeItem:
643 if (doing_object_stuff() || (mouse_mode != MouseRange && mouse_mode != MouseObject)) {
644 set_selected_regionview_from_click (press, op);
645 } else if (event->type == GDK_BUTTON_PRESS) {
646 set_selected_track_as_side_effect (op);
650 case ControlPointItem:
651 set_selected_track_as_side_effect (op);
652 if (doing_object_stuff() || (mouse_mode != MouseRange && mouse_mode != MouseObject)) {
653 set_selected_control_point_from_click (press, op);
658 /* for context click, select track */
659 if (event->button.button == 3) {
660 selection->clear_tracks ();
661 set_selected_track_as_side_effect (op);
665 case AutomationTrackItem:
666 set_selected_track_as_side_effect (op);
675 Editor::button_press_handler_1 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
677 /* single mouse clicks on any of these item types operate
678 independent of mouse mode, mostly because they are
679 not on the main track canvas or because we want
684 case PlayheadCursorItem:
685 _drags->set (new CursorDrag (this, item, true), event);
689 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
690 hide_marker (item, event);
692 _drags->set (new MarkerDrag (this, item), event);
696 case TempoMarkerItem:
698 TempoMarker* m = reinterpret_cast<TempoMarker*> (item->get_data ("marker"));
700 if (m->tempo().movable ()) {
702 new TempoMarkerDrag (
705 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
715 case MeterMarkerItem:
717 MeterMarker* m = reinterpret_cast<MeterMarker*> (item->get_data ("marker"));
719 if (m->meter().movable ()) {
721 new MeterMarkerDrag (
724 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
737 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
738 _drags->set (new CursorDrag (this, &playhead_cursor->canvas_item, false), event);
744 case RangeMarkerBarItem:
745 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
746 _drags->set (new CursorDrag (this, &playhead_cursor->canvas_item, false), event);
748 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateRangeMarker), event);
753 case CdMarkerBarItem:
754 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
755 _drags->set (new CursorDrag (this, &playhead_cursor->canvas_item, false), event);
757 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateCDMarker), event);
762 case TransportMarkerBarItem:
763 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
764 _drags->set (new CursorDrag (this, &playhead_cursor->canvas_item, false), event);
766 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateTransportMarker), event);
775 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
776 /* special case: allow trim of range selections in joined object mode;
777 in theory eff should equal MouseRange in this case, but it doesn't
778 because entering the range selection canvas item results in entered_regionview
779 being set to 0, so update_join_object_range_location acts as if we aren't
782 if (item_type == StartSelectionTrimItem) {
783 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
784 } else if (item_type == EndSelectionTrimItem) {
785 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
789 Editing::MouseMode eff = effective_mouse_mode ();
791 /* special case: allow drag of region fade in/out in object mode with join object/range enabled */
792 if (item_type == FadeInHandleItem || item_type == FadeOutHandleItem) {
799 case StartSelectionTrimItem:
800 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
803 case EndSelectionTrimItem:
804 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
808 if (Keyboard::modifier_state_contains
809 (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier))) {
810 // contains and not equals because I can't use alt as a modifier alone.
811 start_selection_grab (item, event);
812 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
813 /* grab selection for moving */
814 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionMove), event);
816 double const y = event->button.y + vertical_adjustment.get_value() - canvas_timebars_vsize;
817 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
819 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
820 if (smart_mode_action->get_active() && atv) {
821 /* smart "join" mode: drag automation */
822 _drags->set (new AutomationRangeDrag (this, atv, selection->time), event, _cursors->up_down);
824 /* this was debated, but decided the more common action was to
825 make a new selection */
826 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
833 if (internal_editing()) {
834 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
835 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
839 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
844 case RegionViewNameHighlight:
845 if (!clicked_regionview->region()->locked()) {
846 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
847 _drags->set (new TrimDrag (this, item, clicked_regionview, s.by_layer()), event);
852 case LeftFrameHandle:
853 case RightFrameHandle:
854 if (!internal_editing() && doing_object_stuff() && !clicked_regionview->region()->locked()) {
855 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
856 _drags->set (new TrimDrag (this, item, clicked_regionview, s.by_layer()), event);
862 if (!internal_editing()) {
863 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
872 if (internal_editing()) {
873 /* trim notes if we're in internal edit mode and near the ends of the note */
874 ArdourCanvas::CanvasNote* cn = dynamic_cast<ArdourCanvas::CanvasNote*> (item);
875 if (cn && cn->big_enough_to_trim() && cn->mouse_near_ends()) {
876 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
878 _drags->set (new NoteDrag (this, item), event);
892 if (internal_editing()) {
893 ArdourCanvas::CanvasNoteEvent* cn = dynamic_cast<ArdourCanvas::CanvasNoteEvent*> (item);
894 if (cn->mouse_near_ends()) {
895 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
897 _drags->set (new NoteDrag (this, item), event);
907 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)) &&
908 event->type == GDK_BUTTON_PRESS) {
910 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
912 } else if (event->type == GDK_BUTTON_PRESS) {
915 case FadeInHandleItem:
917 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
918 _drags->set (new FadeInDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), s), event, _cursors->fade_in);
922 case FadeOutHandleItem:
924 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
925 _drags->set (new FadeOutDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), s), event, _cursors->fade_out);
929 case StartCrossFadeItem:
930 _drags->set (new CrossfadeEdgeDrag (this, reinterpret_cast<AudioRegionView*>(item->get_data("regionview")), item, true), event, 0);
933 case EndCrossFadeItem:
934 _drags->set (new CrossfadeEdgeDrag (this, reinterpret_cast<AudioRegionView*>(item->get_data("regionview")), item, false), event, 0);
937 case FeatureLineItem:
939 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier)) {
940 remove_transient(item);
944 _drags->set (new FeatureLineDrag (this, item), event);
950 if (dynamic_cast<AutomationRegionView*> (clicked_regionview)) {
951 /* click on an automation region view; do nothing here and let the ARV's signal handler
957 if (internal_editing ()) {
958 if (event->type == GDK_2BUTTON_PRESS && event->button.button == 1) {
959 Glib::RefPtr<Action> act = ActionManager::get_action (X_("MouseMode"), X_("toggle-internal-edit"));
965 /* click on a normal region view */
966 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
967 add_region_copy_drag (item, event, clicked_regionview);
968 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
969 add_region_brush_drag (item, event, clicked_regionview);
971 add_region_drag (item, event, clicked_regionview);
975 if (!internal_editing() && (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE && !selection->regions.empty())) {
976 _drags->add (new SelectionDrag (this, clicked_axisview->get_selection_rect (clicked_selection)->rect, SelectionDrag::SelectionMove));
979 _drags->start_grab (event);
982 case RegionViewNameHighlight:
983 case LeftFrameHandle:
984 case RightFrameHandle:
985 if (!clicked_regionview->region()->locked()) {
986 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
987 _drags->set (new TrimDrag (this, item, clicked_regionview, s.by_layer()), event);
994 /* rename happens on edit clicks */
995 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
996 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, s.by_layer()), event);
1001 case ControlPointItem:
1002 _drags->set (new ControlPointDrag (this, item), event);
1006 case AutomationLineItem:
1007 _drags->set (new LineDrag (this, item), event);
1012 if (internal_editing()) {
1013 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
1014 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
1018 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
1022 case AutomationTrackItem:
1024 TimeAxisView* parent = clicked_axisview->get_parent ();
1025 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (clicked_axisview);
1027 if (parent && dynamic_cast<MidiTimeAxisView*> (parent) && atv->show_regions ()) {
1028 /* create a MIDI region so that we have somewhere to put automation */
1029 _drags->set (new RegionCreateDrag (this, item, parent), event);
1031 /* rubberband drag to select automation points */
1032 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
1039 if (smart_mode_action->get_active()) {
1040 /* we're in "smart" joined mode, and we've clicked on a Selection */
1041 double const y = event->button.y + vertical_adjustment.get_value() - canvas_timebars_vsize;
1042 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
1044 /* if we're over an automation track, start a drag of its data */
1045 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
1047 _drags->set (new AutomationRangeDrag (this, atv, selection->time), event, _cursors->up_down);
1050 /* if we're over a track and a region, and in the `object' part of a region,
1051 put a selection around the region and drag both
1053 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1054 if (rtv && _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
1055 boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track> (rtv->route ());
1057 boost::shared_ptr<Playlist> pl = t->playlist ();
1060 boost::shared_ptr<Region> r = pl->top_region_at (event_frame (event));
1062 RegionView* rv = rtv->view()->find_view (r);
1063 clicked_selection = select_range (rv->region()->position(),
1064 rv->region()->last_frame()+1);
1065 _drags->add (new SelectionDrag (this, item, SelectionDrag::SelectionMove));
1066 list<RegionView*> rvs;
1068 _drags->add (new RegionMoveDrag (this, item, rv, rvs, false, false));
1069 _drags->start_grab (event);
1080 case ImageFrameHandleStartItem:
1081 imageframe_start_handle_op(item, event) ;
1084 case ImageFrameHandleEndItem:
1085 imageframe_end_handle_op(item, event) ;
1088 case MarkerViewHandleStartItem:
1089 markerview_item_start_handle_op(item, event) ;
1092 case MarkerViewHandleEndItem:
1093 markerview_item_end_handle_op(item, event) ;
1096 case MarkerViewItem:
1097 start_markerview_grab(item, event) ;
1099 case ImageFrameItem:
1100 start_imageframe_grab(item, event) ;
1116 switch (item_type) {
1118 _drags->set (new LineDrag (this, item), event);
1121 case ControlPointItem:
1122 _drags->set (new ControlPointDrag (this, item), event);
1128 AudioRegionView* arv = dynamic_cast<AudioRegionView *> (clicked_regionview);
1130 _drags->set (new AutomationRangeDrag (this, arv, selection->time), event, _cursors->up_down);
1131 _drags->start_grab (event);
1137 case AutomationLineItem:
1138 _drags->set (new LineDrag (this, item), event);
1148 if (event->type == GDK_BUTTON_PRESS) {
1149 _drags->set (new MouseZoomDrag (this, item), event);
1156 if (internal_editing() && item_type == NoteItem) {
1157 /* drag notes if we're in internal edit mode */
1158 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
1160 } else if ((!internal_editing() || dynamic_cast<AudioRegionView*> (clicked_regionview)) && clicked_regionview) {
1161 /* do time-FX if we're not in internal edit mode, or we are but we clicked on an audio region */
1162 _drags->set (new TimeFXDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1168 _drags->set (new ScrubDrag (this, item), event);
1169 scrub_reversals = 0;
1170 scrub_reverse_distance = 0;
1171 last_scrub_x = event->button.x;
1172 scrubbing_direction = 0;
1173 set_canvas_cursor (_cursors->transparent);
1185 Editor::button_press_handler_2 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1187 Editing::MouseMode const eff = effective_mouse_mode ();
1190 switch (item_type) {
1192 if (internal_editing ()) {
1193 /* no region drags in internal edit mode */
1197 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
1198 add_region_copy_drag (item, event, clicked_regionview);
1200 add_region_drag (item, event, clicked_regionview);
1202 _drags->start_grab (event);
1205 case ControlPointItem:
1206 _drags->set (new ControlPointDrag (this, item), event);
1214 switch (item_type) {
1215 case RegionViewNameHighlight:
1216 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1220 case LeftFrameHandle:
1221 case RightFrameHandle:
1222 if (!internal_editing ()) {
1223 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1228 case RegionViewName:
1229 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer()), event);
1243 /* relax till release */
1249 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1250 temporal_zoom_to_frame (false, event_frame (event));
1252 temporal_zoom_to_frame (true, event_frame(event));
1265 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1267 if (event->type != GDK_BUTTON_PRESS) {
1271 Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->track_canvas->get_window();
1273 if (canvas_window) {
1274 Glib::RefPtr<const Gdk::Window> pointer_window;
1277 Gdk::ModifierType mask;
1279 pointer_window = canvas_window->get_pointer (x, y, mask);
1281 if (pointer_window == track_canvas->get_bin_window()) {
1282 track_canvas->window_to_world (x, y, wx, wy);
1286 pre_press_cursor = current_canvas_cursor;
1288 track_canvas->grab_focus();
1290 if (_session && _session->actively_recording()) {
1296 if (internal_editing()) {
1297 bool leave_internal_edit_mode = false;
1299 switch (item_type) {
1304 if (!dynamic_cast<MidiRegionView*> (clicked_regionview) && !dynamic_cast<AutomationRegionView*> (clicked_regionview)) {
1305 leave_internal_edit_mode = true;
1309 case PlayheadCursorItem:
1311 case TempoMarkerItem:
1312 case MeterMarkerItem:
1316 case RangeMarkerBarItem:
1317 case CdMarkerBarItem:
1318 case TransportMarkerBarItem:
1319 /* button press on these events never does anything to
1320 change the editing mode.
1325 leave_internal_edit_mode = true;
1332 if (leave_internal_edit_mode) {
1333 ActionManager::do_action ("MouseMode", "toggle-internal-edit");
1337 button_selection (item, event, item_type);
1339 if (!_drags->active () &&
1340 (Keyboard::is_delete_event (&event->button) ||
1341 Keyboard::is_context_menu_event (&event->button) ||
1342 Keyboard::is_edit_event (&event->button))) {
1344 /* handled by button release */
1348 switch (event->button.button) {
1350 return button_press_handler_1 (item, event, item_type);
1354 return button_press_handler_2 (item, event, item_type);
1361 return button_press_dispatch (&event->button);
1370 Editor::button_press_dispatch (GdkEventButton* ev)
1372 /* this function is intended only for buttons 4 and above.
1375 Gtkmm2ext::MouseButton b (ev->state, ev->button);
1376 return button_bindings->activate (b, Gtkmm2ext::Bindings::Press);
1380 Editor::button_release_dispatch (GdkEventButton* ev)
1382 /* this function is intended only for buttons 4 and above.
1385 Gtkmm2ext::MouseButton b (ev->state, ev->button);
1386 return button_bindings->activate (b, Gtkmm2ext::Bindings::Release);
1390 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1392 framepos_t where = event_frame (event, 0, 0);
1393 AutomationTimeAxisView* atv = 0;
1395 if (pre_press_cursor) {
1396 set_canvas_cursor (pre_press_cursor);
1397 pre_press_cursor = 0;
1400 /* no action if we're recording */
1402 if (_session && _session->actively_recording()) {
1406 /* see if we're finishing a drag */
1408 bool were_dragging = false;
1409 if (_drags->active ()) {
1410 bool const r = _drags->end_grab (event);
1412 /* grab dragged, so do nothing else */
1416 were_dragging = true;
1419 update_region_layering_order_editor ();
1421 /* edit events get handled here */
1423 if (!_drags->active () && Keyboard::is_edit_event (&event->button)) {
1424 switch (item_type) {
1426 show_region_properties ();
1429 case TempoMarkerItem:
1430 edit_tempo_marker (item);
1433 case MeterMarkerItem:
1434 edit_meter_marker (item);
1437 case RegionViewName:
1438 if (clicked_regionview->name_active()) {
1439 return mouse_rename_region (item, event);
1443 case ControlPointItem:
1444 edit_control_point (item);
1457 /* context menu events get handled here */
1459 if (Keyboard::is_context_menu_event (&event->button)) {
1461 context_click_event = *event;
1463 if (!_drags->active ()) {
1465 /* no matter which button pops up the context menu, tell the menu
1466 widget to use button 1 to drive menu selection.
1469 switch (item_type) {
1471 case FadeInHandleItem:
1473 case FadeOutHandleItem:
1474 popup_fade_context_menu (1, event->button.time, item, item_type);
1477 case StartCrossFadeItem:
1478 popup_xfade_in_context_menu (1, event->button.time, item, item_type);
1481 case EndCrossFadeItem:
1482 popup_xfade_out_context_menu (1, event->button.time, item, item_type);
1486 popup_track_context_menu (1, event->button.time, item_type, false);
1490 case RegionViewNameHighlight:
1491 case LeftFrameHandle:
1492 case RightFrameHandle:
1493 case RegionViewName:
1494 popup_track_context_menu (1, event->button.time, item_type, false);
1498 popup_track_context_menu (1, event->button.time, item_type, true);
1501 case AutomationTrackItem:
1502 popup_track_context_menu (1, event->button.time, item_type, false);
1506 case RangeMarkerBarItem:
1507 case TransportMarkerBarItem:
1508 case CdMarkerBarItem:
1511 popup_ruler_menu (where, item_type);
1515 marker_context_menu (&event->button, item);
1518 case TempoMarkerItem:
1519 tempo_or_meter_marker_context_menu (&event->button, item);
1522 case MeterMarkerItem:
1523 tempo_or_meter_marker_context_menu (&event->button, item);
1526 case CrossfadeViewItem:
1527 popup_track_context_menu (1, event->button.time, item_type, false);
1530 case ControlPointItem:
1531 popup_control_point_context_menu (item, event);
1535 case ImageFrameItem:
1536 popup_imageframe_edit_menu(1, event->button.time, item, true) ;
1538 case ImageFrameTimeAxisItem:
1539 popup_imageframe_edit_menu(1, event->button.time, item, false) ;
1541 case MarkerViewItem:
1542 popup_marker_time_axis_edit_menu(1, event->button.time, item, true) ;
1544 case MarkerTimeAxisItem:
1545 popup_marker_time_axis_edit_menu(1, event->button.time, item, false) ;
1557 /* delete events get handled here */
1559 Editing::MouseMode const eff = effective_mouse_mode ();
1561 if (!_drags->active () && Keyboard::is_delete_event (&event->button)) {
1563 switch (item_type) {
1564 case TempoMarkerItem:
1565 remove_tempo_marker (item);
1568 case MeterMarkerItem:
1569 remove_meter_marker (item);
1573 remove_marker (*item, event);
1577 if (eff == MouseObject) {
1578 remove_clicked_region ();
1582 case ControlPointItem:
1583 remove_control_point (item);
1587 remove_midi_note (item, event);
1596 switch (event->button.button) {
1599 switch (item_type) {
1600 /* see comments in button_press_handler */
1601 case PlayheadCursorItem:
1604 case AutomationLineItem:
1605 case StartSelectionTrimItem:
1606 case EndSelectionTrimItem:
1610 if (!_dragging_playhead) {
1611 snap_to_with_modifier (where, event, 0, true);
1612 mouse_add_new_marker (where);
1616 case CdMarkerBarItem:
1617 if (!_dragging_playhead) {
1618 // if we get here then a dragged range wasn't done
1619 snap_to_with_modifier (where, event, 0, true);
1620 mouse_add_new_marker (where, true);
1625 if (!_dragging_playhead) {
1626 snap_to_with_modifier (where, event);
1627 mouse_add_new_tempo_event (where);
1632 if (!_dragging_playhead) {
1633 mouse_add_new_meter_event (pixel_to_frame (event->button.x));
1644 switch (item_type) {
1645 case AutomationTrackItem:
1646 atv = dynamic_cast<AutomationTimeAxisView*>(clicked_axisview);
1648 atv->add_automation_event (event, where, event->button.y);
1658 switch (item_type) {
1661 /* check that we didn't drag before releasing, since
1662 its really annoying to create new control
1663 points when doing this.
1665 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (clicked_regionview);
1666 if (!were_dragging && arv) {
1667 arv->add_gain_point_event (item, event);
1673 case AutomationTrackItem:
1674 dynamic_cast<AutomationTimeAxisView*>(clicked_axisview)->
1675 add_automation_event (event, where, event->button.y);
1684 set_canvas_cursor (current_canvas_cursor);
1685 if (scrubbing_direction == 0) {
1686 /* no drag, just a click */
1687 switch (item_type) {
1689 play_selected_region ();
1695 /* make sure we stop */
1696 _session->request_transport_speed (0.0);
1705 /* do any (de)selection operations that should occur on button release */
1706 button_selection (item, event, item_type);
1715 switch (item_type) {
1717 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1719 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier|Keyboard::SecondaryModifier))) {
1722 // Button2 click is unused
1737 // x_style_paste (where, 1.0);
1758 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1765 switch (item_type) {
1766 case ControlPointItem:
1767 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1768 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1769 cp->set_visible (true);
1773 at_y = cp->get_y ();
1774 cp->i2w (at_x, at_y);
1778 fraction = 1.0 - (cp->get_y() / cp->line().height());
1780 if (is_drawable() && !_drags->active ()) {
1781 set_canvas_cursor (_cursors->fader);
1784 _verbose_cursor->set (cp->line().get_verbose_cursor_string (fraction), at_x, at_y);
1785 _verbose_cursor->show ();
1790 if (mouse_mode == MouseGain) {
1791 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1793 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredGainLine.get();
1794 if (is_drawable()) {
1795 set_canvas_cursor (_cursors->fader);
1800 case AutomationLineItem:
1801 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1802 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1804 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredAutomationLine.get();
1806 if (is_drawable()) {
1807 set_canvas_cursor (_cursors->fader);
1812 case RegionViewNameHighlight:
1813 if (is_drawable() && doing_object_stuff() && entered_regionview) {
1814 set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1815 _over_region_trim_target = true;
1819 case LeftFrameHandle:
1820 case RightFrameHandle:
1821 if (is_drawable() && doing_object_stuff() && !internal_editing() && entered_regionview) {
1822 set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1826 case StartSelectionTrimItem:
1828 case ImageFrameHandleStartItem:
1829 case MarkerViewHandleStartItem:
1831 if (is_drawable()) {
1832 set_canvas_cursor (_cursors->left_side_trim);
1835 case EndSelectionTrimItem:
1837 case ImageFrameHandleEndItem:
1838 case MarkerViewHandleEndItem:
1840 if (is_drawable()) {
1841 set_canvas_cursor (_cursors->right_side_trim);
1845 case PlayheadCursorItem:
1846 if (is_drawable()) {
1847 switch (_edit_point) {
1849 set_canvas_cursor (_cursors->grabber_edit_point);
1852 set_canvas_cursor (_cursors->grabber);
1858 case RegionViewName:
1860 /* when the name is not an active item, the entire name highlight is for trimming */
1862 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1863 if (mouse_mode == MouseObject && is_drawable()) {
1864 set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1865 _over_region_trim_target = true;
1871 case AutomationTrackItem:
1872 if (is_drawable()) {
1873 Gdk::Cursor *cursor;
1874 switch (mouse_mode) {
1876 cursor = _cursors->selector;
1879 cursor = _cursors->zoom_in;
1882 cursor = _cursors->cross_hair;
1886 set_canvas_cursor (cursor);
1888 AutomationTimeAxisView* atv;
1889 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1890 clear_entered_track = false;
1891 set_entered_track (atv);
1897 case RangeMarkerBarItem:
1898 case TransportMarkerBarItem:
1899 case CdMarkerBarItem:
1902 if (is_drawable()) {
1903 set_canvas_cursor (_cursors->timebar);
1908 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1911 entered_marker = marker;
1912 marker->set_color_rgba (ARDOUR_UI::config()->canvasvar_EnteredMarker.get());
1914 case MeterMarkerItem:
1915 case TempoMarkerItem:
1916 if (is_drawable()) {
1917 set_canvas_cursor (_cursors->timebar);
1921 case FadeInHandleItem:
1922 if (mouse_mode == MouseObject && !internal_editing()) {
1923 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1925 rect->property_fill_color_rgba() = 0xBBBBBBAA;
1927 set_canvas_cursor (_cursors->fade_in);
1931 case FadeOutHandleItem:
1932 if (mouse_mode == MouseObject && !internal_editing()) {
1933 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1935 rect->property_fill_color_rgba() = 0xBBBBBBAA;
1937 set_canvas_cursor (_cursors->fade_out);
1940 case FeatureLineItem:
1942 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1943 line->property_fill_color_rgba() = 0xFF0000FF;
1947 if (smart_mode_action->get_active()) {
1948 set_canvas_cursor ();
1956 /* second pass to handle entered track status in a comprehensible way.
1959 switch (item_type) {
1961 case AutomationLineItem:
1962 case ControlPointItem:
1963 /* these do not affect the current entered track state */
1964 clear_entered_track = false;
1967 case AutomationTrackItem:
1968 /* handled above already */
1972 set_entered_track (0);
1980 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent*, ItemType item_type)
1990 switch (item_type) {
1991 case ControlPointItem:
1992 cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
1993 if (cp->line().the_list()->interpolation() != AutomationList::Discrete) {
1994 if (cp->line().npoints() > 1 && !cp->get_selected()) {
1995 cp->set_visible (false);
1999 if (is_drawable()) {
2000 set_canvas_cursor (current_canvas_cursor);
2003 _verbose_cursor->hide ();
2006 case RegionViewNameHighlight:
2007 case LeftFrameHandle:
2008 case RightFrameHandle:
2009 case StartSelectionTrimItem:
2010 case EndSelectionTrimItem:
2011 case PlayheadCursorItem:
2014 case ImageFrameHandleStartItem:
2015 case ImageFrameHandleEndItem:
2016 case MarkerViewHandleStartItem:
2017 case MarkerViewHandleEndItem:
2020 _over_region_trim_target = false;
2022 if (is_drawable()) {
2023 set_canvas_cursor (current_canvas_cursor);
2028 case AutomationLineItem:
2029 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
2031 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
2033 line->property_fill_color_rgba() = al->get_line_color();
2035 if (is_drawable()) {
2036 set_canvas_cursor (current_canvas_cursor);
2040 case RegionViewName:
2041 /* see enter_handler() for notes */
2042 _over_region_trim_target = false;
2044 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
2045 if (is_drawable() && mouse_mode == MouseObject) {
2046 set_canvas_cursor (current_canvas_cursor);
2051 case RangeMarkerBarItem:
2052 case TransportMarkerBarItem:
2053 case CdMarkerBarItem:
2057 if (is_drawable()) {
2058 set_canvas_cursor (current_canvas_cursor);
2063 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2067 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
2068 location_flags_changed (loc, this);
2071 case MeterMarkerItem:
2072 case TempoMarkerItem:
2074 if (is_drawable()) {
2075 set_canvas_cursor (current_canvas_cursor);
2080 case FadeInHandleItem:
2081 case FadeOutHandleItem:
2082 rv = static_cast<RegionView*>(item->get_data ("regionview"));
2084 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
2086 rect->property_fill_color_rgba() = rv->get_fill_color();
2087 rect->property_outline_pixels() = 0;
2090 set_canvas_cursor (current_canvas_cursor);
2093 case AutomationTrackItem:
2094 if (is_drawable()) {
2095 set_canvas_cursor (current_canvas_cursor);
2096 clear_entered_track = true;
2097 Glib::signal_idle().connect (sigc::mem_fun(*this, &Editor::left_automation_track));
2100 case FeatureLineItem:
2102 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
2103 line->property_fill_color_rgba() = (guint) ARDOUR_UI::config()->canvasvar_ZeroLine.get();;
2115 Editor::left_automation_track ()
2117 if (clear_entered_track) {
2118 set_entered_track (0);
2119 clear_entered_track = false;
2125 Editor::scrub (framepos_t frame, double current_x)
2129 if (scrubbing_direction == 0) {
2131 _session->request_locate (frame, false);
2132 _session->request_transport_speed (0.1);
2133 scrubbing_direction = 1;
2137 if (last_scrub_x > current_x) {
2139 /* pointer moved to the left */
2141 if (scrubbing_direction > 0) {
2143 /* we reversed direction to go backwards */
2146 scrub_reverse_distance += (int) (last_scrub_x - current_x);
2150 /* still moving to the left (backwards) */
2152 scrub_reversals = 0;
2153 scrub_reverse_distance = 0;
2155 delta = 0.01 * (last_scrub_x - current_x);
2156 _session->request_transport_speed_nonzero (_session->transport_speed() - delta);
2160 /* pointer moved to the right */
2162 if (scrubbing_direction < 0) {
2163 /* we reversed direction to go forward */
2166 scrub_reverse_distance += (int) (current_x - last_scrub_x);
2169 /* still moving to the right */
2171 scrub_reversals = 0;
2172 scrub_reverse_distance = 0;
2174 delta = 0.01 * (current_x - last_scrub_x);
2175 _session->request_transport_speed_nonzero (_session->transport_speed() + delta);
2179 /* if there have been more than 2 opposite motion moves detected, or one that moves
2180 back more than 10 pixels, reverse direction
2183 if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
2185 if (scrubbing_direction > 0) {
2186 /* was forwards, go backwards */
2187 _session->request_transport_speed (-0.1);
2188 scrubbing_direction = -1;
2190 /* was backwards, go forwards */
2191 _session->request_transport_speed (0.1);
2192 scrubbing_direction = 1;
2195 scrub_reverse_distance = 0;
2196 scrub_reversals = 0;
2200 last_scrub_x = current_x;
2204 Editor::motion_handler (ArdourCanvas::Item* /*item*/, GdkEvent* event, bool from_autoscroll)
2206 _last_motion_y = event->motion.y;
2208 if (event->motion.is_hint) {
2211 /* We call this so that MOTION_NOTIFY events continue to be
2212 delivered to the canvas. We need to do this because we set
2213 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
2214 the density of the events, at the expense of a round-trip
2215 to the server. Given that this will mostly occur on cases
2216 where DISPLAY = :0.0, and given the cost of what the motion
2217 event might do, its a good tradeoff.
2220 track_canvas->get_pointer (x, y);
2223 if (current_stepping_trackview) {
2224 /* don't keep the persistent stepped trackview if the mouse moves */
2225 current_stepping_trackview = 0;
2226 step_timeout.disconnect ();
2229 if (_session && _session->actively_recording()) {
2230 /* Sorry. no dragging stuff around while we record */
2234 JoinObjectRangeState const old = _join_object_range_state;
2235 update_join_object_range_location (event->motion.x, event->motion.y);
2236 if (_join_object_range_state != old) {
2237 set_canvas_cursor ();
2240 if (_over_region_trim_target) {
2241 set_canvas_cursor_for_region_view (event->motion.x, entered_regionview);
2244 bool handled = false;
2245 if (_drags->active ()) {
2246 handled = _drags->motion_handler (event, from_autoscroll);
2253 track_canvas_motion (event);
2258 Editor::can_remove_control_point (ArdourCanvas::Item* item)
2260 ControlPoint* control_point;
2262 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2263 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2267 AutomationLine& line = control_point->line ();
2268 if (dynamic_cast<AudioRegionGainLine*> (&line)) {
2269 /* we shouldn't remove the first or last gain point in region gain lines */
2270 if (line.is_last_point(*control_point) || line.is_first_point(*control_point)) {
2279 Editor::remove_control_point (ArdourCanvas::Item* item)
2281 if (!can_remove_control_point (item)) {
2285 ControlPoint* control_point;
2287 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2288 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2292 control_point->line().remove_point (*control_point);
2296 Editor::edit_control_point (ArdourCanvas::Item* item)
2298 ControlPoint* p = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"));
2301 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2305 ControlPointDialog d (p);
2306 d.set_position (Gtk::WIN_POS_MOUSE);
2309 if (d.run () != RESPONSE_ACCEPT) {
2313 p->line().modify_point_y (*p, d.get_y_fraction ());
2317 Editor::edit_note (ArdourCanvas::Item* item)
2319 ArdourCanvas::CanvasNoteEvent* e = dynamic_cast<ArdourCanvas::CanvasNoteEvent*> (item);
2322 EditNoteDialog d (&e->region_view(), e);
2323 d.set_position (Gtk::WIN_POS_MOUSE);
2331 Editor::visible_order_range (int* low, int* high) const
2333 *low = TimeAxisView::max_order ();
2336 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
2338 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
2340 if (!rtv->hidden()) {
2342 if (*high < rtv->order()) {
2343 *high = rtv->order ();
2346 if (*low > rtv->order()) {
2347 *low = rtv->order ();
2354 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
2356 /* Either add to or set the set the region selection, unless
2357 this is an alignment click (control used)
2360 if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
2361 TimeAxisView* tv = &rv.get_time_axis_view();
2362 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
2364 if (rtv && rtv->is_track()) {
2365 speed = rtv->track()->speed();
2368 framepos_t where = get_preferred_edit_position();
2372 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
2374 align_region (rv.region(), SyncPoint, (framepos_t) (where * speed));
2376 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
2378 align_region (rv.region(), End, (framepos_t) (where * speed));
2382 align_region (rv.region(), Start, (framepos_t) (where * speed));
2389 Editor::collect_new_region_view (RegionView* rv)
2391 latest_regionviews.push_back (rv);
2395 Editor::collect_and_select_new_region_view (RegionView* rv)
2398 latest_regionviews.push_back (rv);
2402 Editor::cancel_selection ()
2404 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2405 (*i)->hide_selection ();
2408 selection->clear ();
2409 clicked_selection = 0;
2414 Editor::point_trim (GdkEvent* event, framepos_t new_bound)
2416 RegionView* rv = clicked_regionview;
2418 /* Choose action dependant on which button was pressed */
2419 switch (event->button.button) {
2421 begin_reversible_command (_("start point trim"));
2423 if (selection->selected (rv)) {
2424 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
2425 i != selection->regions.by_layer().end(); ++i)
2427 if (!(*i)->region()->locked()) {
2428 (*i)->region()->clear_changes ();
2429 (*i)->region()->trim_front (new_bound);
2430 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2435 if (!rv->region()->locked()) {
2436 rv->region()->clear_changes ();
2437 rv->region()->trim_front (new_bound);
2438 _session->add_command(new StatefulDiffCommand (rv->region()));
2442 commit_reversible_command();
2446 begin_reversible_command (_("End point trim"));
2448 if (selection->selected (rv)) {
2450 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
2452 if (!(*i)->region()->locked()) {
2453 (*i)->region()->clear_changes();
2454 (*i)->region()->trim_end (new_bound);
2455 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2461 if (!rv->region()->locked()) {
2462 rv->region()->clear_changes ();
2463 rv->region()->trim_end (new_bound);
2464 _session->add_command (new StatefulDiffCommand (rv->region()));
2468 commit_reversible_command();
2477 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* /*event*/)
2482 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2483 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2487 Location* location = find_location_from_marker (marker, is_start);
2488 location->set_hidden (true, this);
2493 Editor::reposition_zoom_rect (framepos_t start, framepos_t end)
2495 double x1 = frame_to_pixel (start);
2496 double x2 = frame_to_pixel (end);
2497 double y2 = full_canvas_height - 1.0;
2499 zoom_rect->property_x1() = x1;
2500 zoom_rect->property_y1() = 1.0;
2501 zoom_rect->property_x2() = x2;
2502 zoom_rect->property_y2() = y2;
2507 Editor::mouse_rename_region (ArdourCanvas::Item* /*item*/, GdkEvent* /*event*/)
2509 using namespace Gtkmm2ext;
2511 ArdourPrompter prompter (false);
2513 prompter.set_prompt (_("Name for region:"));
2514 prompter.set_initial_text (clicked_regionview->region()->name());
2515 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
2516 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
2517 prompter.show_all ();
2518 switch (prompter.run ()) {
2519 case Gtk::RESPONSE_ACCEPT:
2521 prompter.get_result(str);
2523 clicked_regionview->region()->set_name (str);
2532 Editor::mouse_brush_insert_region (RegionView* rv, framepos_t pos)
2534 /* no brushing without a useful snap setting */
2536 switch (_snap_mode) {
2538 return; /* can't work because it allows region to be placed anywhere */
2543 switch (_snap_type) {
2551 /* don't brush a copy over the original */
2553 if (pos == rv->region()->position()) {
2557 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
2559 if (rtv == 0 || !rtv->is_track()) {
2563 boost::shared_ptr<Playlist> playlist = rtv->playlist();
2564 double speed = rtv->track()->speed();
2566 playlist->clear_changes ();
2567 boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region(), true));
2568 playlist->add_region (new_region, (framepos_t) (pos * speed));
2569 _session->add_command (new StatefulDiffCommand (playlist));
2571 // playlist is frozen, so we have to update manually XXX this is disgusting
2573 playlist->RegionAdded (new_region); /* EMIT SIGNAL */
2577 Editor::track_height_step_timeout ()
2579 if (get_microseconds() - last_track_height_step_timestamp < 250000) {
2580 current_stepping_trackview = 0;
2587 Editor::add_region_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 if (Config->get_edit_mode() == Splice) {
2598 _drags->add (new RegionSpliceDrag (this, item, region_view, selection->regions.by_layer()));
2600 RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id);
2601 _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), false, false));
2604 /* sync the canvas to what we think is its current state */
2605 update_canvas_now();
2609 Editor::add_region_copy_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2611 assert (region_view);
2613 if (!region_view->region()->playlist()) {
2617 _region_motion_group->raise_to_top ();
2619 RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id);
2620 _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), false, true));
2624 Editor::add_region_brush_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2626 assert (region_view);
2628 if (!region_view->region()->playlist()) {
2632 if (Config->get_edit_mode() == Splice) {
2636 RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id);
2637 _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), true, false));
2639 begin_reversible_command (Operations::drag_region_brush);
2642 /** Start a grab where a time range is selected, track(s) are selected, and the
2643 * user clicks and drags a region with a modifier in order to create a new region containing
2644 * the section of the clicked region that lies within the time range.
2647 Editor::start_selection_grab (ArdourCanvas::Item* /*item*/, GdkEvent* event)
2649 if (clicked_regionview == 0) {
2653 /* lets try to create new Region for the selection */
2655 vector<boost::shared_ptr<Region> > new_regions;
2656 create_region_from_selection (new_regions);
2658 if (new_regions.empty()) {
2662 /* XXX fix me one day to use all new regions */
2664 boost::shared_ptr<Region> region (new_regions.front());
2666 /* add it to the current stream/playlist.
2668 tricky: the streamview for the track will add a new regionview. we will
2669 catch the signal it sends when it creates the regionview to
2670 set the regionview we want to then drag.
2673 latest_regionviews.clear();
2674 sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
2676 /* A selection grab currently creates two undo/redo operations, one for
2677 creating the new region and another for moving it.
2680 begin_reversible_command (Operations::selection_grab);
2682 boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
2684 playlist->clear_changes ();
2685 clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
2686 _session->add_command(new StatefulDiffCommand (playlist));
2688 commit_reversible_command ();
2692 if (latest_regionviews.empty()) {
2693 /* something went wrong */
2697 /* we need to deselect all other regionviews, and select this one
2698 i'm ignoring undo stuff, because the region creation will take care of it
2700 selection->set (latest_regionviews);
2702 _drags->set (new RegionMoveDrag (this, latest_regionviews.front()->get_canvas_group(), latest_regionviews.front(), latest_regionviews, false, false), event);
2708 if (_drags->active ()) {
2711 selection->clear ();
2716 Editor::set_internal_edit (bool yn)
2718 if (_internal_editing == yn) {
2722 _internal_editing = yn;
2725 pre_internal_mouse_mode = mouse_mode;
2726 pre_internal_snap_type = _snap_type;
2727 pre_internal_snap_mode = _snap_mode;
2729 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2730 (*i)->enter_internal_edit_mode ();
2733 set_snap_to (internal_snap_type);
2734 set_snap_mode (internal_snap_mode);
2738 internal_snap_mode = _snap_mode;
2739 internal_snap_type = _snap_type;
2741 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2742 (*i)->leave_internal_edit_mode ();
2745 if (mouse_mode == MouseDraw && pre_internal_mouse_mode != MouseDraw) {
2746 /* we were drawing .. flip back to something sensible */
2747 set_mouse_mode (pre_internal_mouse_mode);
2750 set_snap_to (pre_internal_snap_type);
2751 set_snap_mode (pre_internal_snap_mode);
2754 set_canvas_cursor ();
2757 /** Update _join_object_range_state which indicate whether we are over the top or bottom half of a region view,
2758 * used by the `join object/range' tool mode.
2761 Editor::update_join_object_range_location (double /*x*/, double y)
2763 /* XXX: actually, this decides based on whether the mouse is in the top
2764 or bottom half of a the waveform part RouteTimeAxisView;
2766 Note that entered_{track,regionview} is not always setup (e.g. if
2767 the mouse is over a TimeSelection), and to get a Region
2768 that we're over requires searching the playlist.
2771 if (!smart_mode_action->get_active() || (mouse_mode != MouseRange && mouse_mode != MouseObject)) {
2772 _join_object_range_state = JOIN_OBJECT_RANGE_NONE;
2776 if (mouse_mode == MouseObject) {
2777 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2778 } else if (mouse_mode == MouseRange) {
2779 _join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
2782 /* XXX: maybe we should make entered_track work in all cases, rather than resorting to this */
2783 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y + vertical_adjustment.get_value() - canvas_timebars_vsize);
2787 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
2792 rtv->canvas_display()->w2i (cx, cy);
2794 double const c = cy / (rtv->view()->child_height() - TimeAxisViewItem::NAME_HIGHLIGHT_SIZE);
2796 double const f = modf (c, &d);
2798 _join_object_range_state = f < 0.5 ? JOIN_OBJECT_RANGE_RANGE : JOIN_OBJECT_RANGE_OBJECT;
2804 Editor::effective_mouse_mode () const
2806 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
2808 } else if (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE) {
2816 Editor::remove_midi_note (ArdourCanvas::Item* item, GdkEvent *)
2818 ArdourCanvas::CanvasNoteEvent* e = dynamic_cast<ArdourCanvas::CanvasNoteEvent*> (item);
2821 e->region_view().delete_note (e->note ());
2825 Editor::set_canvas_cursor_for_region_view (double x, RegionView* rv)
2829 ArdourCanvas::Group* g = rv->get_canvas_group ();
2830 ArdourCanvas::Group* p = g->get_parent_group ();
2832 /* Compute x in region view parent coordinates */
2836 double x1, x2, y1, y2;
2837 g->get_bounds (x1, y1, x2, y2);
2839 /* Halfway across the region */
2840 double const h = (x1 + x2) / 2;
2842 Trimmable::CanTrim ct = rv->region()->can_trim ();
2844 if (ct & Trimmable::FrontTrimEarlier) {
2845 set_canvas_cursor (_cursors->left_side_trim);
2847 set_canvas_cursor (_cursors->left_side_trim_right_only);
2850 if (ct & Trimmable::EndTrimLater) {
2851 set_canvas_cursor (_cursors->right_side_trim);
2853 set_canvas_cursor (_cursors->right_side_trim_left_only);
2858 /** Obtain the pointer position in world coordinates */
2860 Editor::get_pointer_position (double& x, double& y) const
2863 track_canvas->get_pointer (px, py);
2864 track_canvas->window_to_world (px, py, x, y);