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 ();
476 cerr << "mouse-driven sgev\n";
477 set_gain_envelope_visibility ();
479 MouseModeChanged (); /* EMIT SIGNAL */
483 Editor::step_mouse_mode (bool next)
485 switch (current_mouse_mode()) {
488 if (Profile->get_sae()) {
489 set_mouse_mode (MouseZoom);
491 set_mouse_mode (MouseRange);
494 set_mouse_mode (MouseTimeFX);
499 if (next) set_mouse_mode (MouseDraw);
500 else set_mouse_mode (MouseObject);
504 if (next) set_mouse_mode (MouseZoom);
505 else set_mouse_mode (MouseRange);
510 if (Profile->get_sae()) {
511 set_mouse_mode (MouseTimeFX);
513 set_mouse_mode (MouseGain);
516 if (Profile->get_sae()) {
517 set_mouse_mode (MouseObject);
519 set_mouse_mode (MouseDraw);
525 if (next) set_mouse_mode (MouseTimeFX);
526 else set_mouse_mode (MouseZoom);
531 set_mouse_mode (MouseAudition);
533 if (Profile->get_sae()) {
534 set_mouse_mode (MouseZoom);
536 set_mouse_mode (MouseGain);
542 if (next) set_mouse_mode (MouseObject);
543 else set_mouse_mode (MouseTimeFX);
549 Editor::toggle_internal_editing_from_double_click (GdkEvent* event)
551 if (_drags->active()) {
552 _drags->end_grab (event);
555 ActionManager::do_action ("MouseMode", "toggle-internal-edit");
557 /* prevent reversion of edit cursor on button release */
559 pre_press_cursor = 0;
565 Editor::button_selection (ArdourCanvas::Item* /*item*/, GdkEvent* event, ItemType item_type)
567 /* in object/audition/timefx/gain-automation mode,
568 any button press sets the selection if the object
569 can be selected. this is a bit of hack, because
570 we want to avoid this if the mouse operation is a
573 note: not dbl-click or triple-click
575 Also note that there is no region selection in internal edit mode, otherwise
576 for operations operating on the selection (e.g. cut) it is not obvious whether
577 to cut notes or regions.
580 if (((mouse_mode != MouseObject) &&
581 (_join_object_range_state != JOIN_OBJECT_RANGE_OBJECT) &&
582 (mouse_mode != MouseAudition || item_type != RegionItem) &&
583 (mouse_mode != MouseTimeFX || item_type != RegionItem) &&
584 (mouse_mode != MouseGain) &&
585 (mouse_mode != MouseRange) &&
586 (mouse_mode != MouseDraw)) ||
587 ((event->type != GDK_BUTTON_PRESS && event->type != GDK_BUTTON_RELEASE) || event->button.button > 3) ||
588 internal_editing()) {
593 if (event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) {
595 if ((event->button.state & Keyboard::RelevantModifierKeyMask) && event->button.button != 1) {
597 /* almost no selection action on modified button-2 or button-3 events */
599 if (item_type != RegionItem && event->button.button != 2) {
605 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
606 bool press = (event->type == GDK_BUTTON_PRESS);
610 if (!doing_range_stuff()) {
611 set_selected_regionview_from_click (press, op);
615 if (doing_range_stuff()) {
616 /* don't change the selection unless the
617 clicked track is not currently selected. if
618 so, "collapse" the selection to just this
621 if (!selection->selected (clicked_axisview)) {
622 set_selected_track_as_side_effect (Selection::Set);
628 case RegionViewNameHighlight:
630 case LeftFrameHandle:
631 case RightFrameHandle:
632 if (doing_object_stuff() || (mouse_mode != MouseRange && mouse_mode != MouseObject)) {
633 set_selected_regionview_from_click (press, op);
634 } else if (event->type == GDK_BUTTON_PRESS) {
635 set_selected_track_as_side_effect (op);
639 case FadeInHandleItem:
641 case FadeOutHandleItem:
643 case StartCrossFadeItem:
644 case EndCrossFadeItem:
645 if (doing_object_stuff() || (mouse_mode != MouseRange && mouse_mode != MouseObject)) {
646 set_selected_regionview_from_click (press, op);
647 } else if (event->type == GDK_BUTTON_PRESS) {
648 set_selected_track_as_side_effect (op);
652 case ControlPointItem:
653 set_selected_track_as_side_effect (op);
654 if (doing_object_stuff() || (mouse_mode != MouseRange && mouse_mode != MouseObject)) {
655 set_selected_control_point_from_click (press, op);
660 /* for context click, select track */
661 if (event->button.button == 3) {
662 selection->clear_tracks ();
663 set_selected_track_as_side_effect (op);
667 case AutomationTrackItem:
668 set_selected_track_as_side_effect (op);
677 Editor::button_press_handler_1 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
679 /* single mouse clicks on any of these item types operate
680 independent of mouse mode, mostly because they are
681 not on the main track canvas or because we want
686 case PlayheadCursorItem:
687 _drags->set (new CursorDrag (this, item, true), event);
691 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
692 hide_marker (item, event);
694 _drags->set (new MarkerDrag (this, item), event);
698 case TempoMarkerItem:
700 TempoMarker* m = reinterpret_cast<TempoMarker*> (item->get_data ("marker"));
702 if (m->tempo().movable ()) {
704 new TempoMarkerDrag (
707 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
717 case MeterMarkerItem:
719 MeterMarker* m = reinterpret_cast<MeterMarker*> (item->get_data ("marker"));
721 if (m->meter().movable ()) {
723 new MeterMarkerDrag (
726 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
739 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
740 _drags->set (new CursorDrag (this, &playhead_cursor->canvas_item, false), event);
746 case RangeMarkerBarItem:
747 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
748 _drags->set (new CursorDrag (this, &playhead_cursor->canvas_item, false), event);
750 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateRangeMarker), event);
755 case CdMarkerBarItem:
756 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
757 _drags->set (new CursorDrag (this, &playhead_cursor->canvas_item, false), event);
759 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateCDMarker), event);
764 case TransportMarkerBarItem:
765 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
766 _drags->set (new CursorDrag (this, &playhead_cursor->canvas_item, false), event);
768 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateTransportMarker), event);
777 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
778 /* special case: allow trim of range selections in joined object mode;
779 in theory eff should equal MouseRange in this case, but it doesn't
780 because entering the range selection canvas item results in entered_regionview
781 being set to 0, so update_join_object_range_location acts as if we aren't
784 if (item_type == StartSelectionTrimItem) {
785 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
786 } else if (item_type == EndSelectionTrimItem) {
787 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
791 Editing::MouseMode eff = effective_mouse_mode ();
793 /* special case: allow drag of region fade in/out in object mode with join object/range enabled */
794 if (item_type == FadeInHandleItem || item_type == FadeOutHandleItem) {
801 case StartSelectionTrimItem:
802 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
805 case EndSelectionTrimItem:
806 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
810 if (Keyboard::modifier_state_contains
811 (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier))) {
812 // contains and not equals because I can't use alt as a modifier alone.
813 start_selection_grab (item, event);
814 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
815 /* grab selection for moving */
816 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionMove), event);
818 double const y = event->button.y + vertical_adjustment.get_value() - canvas_timebars_vsize;
819 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
821 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
822 if (smart_mode_action->get_active() && atv) {
823 /* smart "join" mode: drag automation */
824 _drags->set (new AutomationRangeDrag (this, atv, selection->time), event, _cursors->up_down);
826 /* this was debated, but decided the more common action was to
827 make a new selection */
828 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
835 if (internal_editing()) {
836 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
837 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
841 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
846 case RegionViewNameHighlight:
847 if (!clicked_regionview->region()->locked()) {
848 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
849 _drags->set (new TrimDrag (this, item, clicked_regionview, s.by_layer()), event);
854 case LeftFrameHandle:
855 case RightFrameHandle:
856 if (!internal_editing() && doing_object_stuff() && !clicked_regionview->region()->locked()) {
857 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
858 _drags->set (new TrimDrag (this, item, clicked_regionview, s.by_layer()), event);
864 if (!internal_editing()) {
865 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
874 if (internal_editing()) {
875 /* trim notes if we're in internal edit mode and near the ends of the note */
876 ArdourCanvas::CanvasNote* cn = dynamic_cast<ArdourCanvas::CanvasNote*> (item);
877 if (cn && cn->big_enough_to_trim() && cn->mouse_near_ends()) {
878 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
880 _drags->set (new NoteDrag (this, item), event);
894 if (internal_editing()) {
895 ArdourCanvas::CanvasNoteEvent* cn = dynamic_cast<ArdourCanvas::CanvasNoteEvent*> (item);
896 if (cn->mouse_near_ends()) {
897 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
899 _drags->set (new NoteDrag (this, item), event);
909 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)) &&
910 event->type == GDK_BUTTON_PRESS) {
912 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
914 } else if (event->type == GDK_BUTTON_PRESS) {
917 case FadeInHandleItem:
919 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
920 _drags->set (new FadeInDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), s), event, _cursors->fade_in);
924 case FadeOutHandleItem:
926 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
927 _drags->set (new FadeOutDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), s), event, _cursors->fade_out);
931 case StartCrossFadeItem:
932 _drags->set (new CrossfadeEdgeDrag (this, reinterpret_cast<AudioRegionView*>(item->get_data("regionview")), item, true), event, 0);
935 case EndCrossFadeItem:
936 _drags->set (new CrossfadeEdgeDrag (this, reinterpret_cast<AudioRegionView*>(item->get_data("regionview")), item, false), event, 0);
939 case FeatureLineItem:
941 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier)) {
942 remove_transient(item);
946 _drags->set (new FeatureLineDrag (this, item), event);
952 if (dynamic_cast<AutomationRegionView*> (clicked_regionview)) {
953 /* click on an automation region view; do nothing here and let the ARV's signal handler
959 if (internal_editing ()) {
960 if (event->type == GDK_2BUTTON_PRESS && event->button.button == 1) {
961 Glib::RefPtr<Action> act = ActionManager::get_action (X_("MouseMode"), X_("toggle-internal-edit"));
967 /* click on a normal region view */
968 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
969 add_region_copy_drag (item, event, clicked_regionview);
970 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
971 add_region_brush_drag (item, event, clicked_regionview);
973 add_region_drag (item, event, clicked_regionview);
977 if (!internal_editing() && (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE && !selection->regions.empty())) {
978 _drags->add (new SelectionDrag (this, clicked_axisview->get_selection_rect (clicked_selection)->rect, SelectionDrag::SelectionMove));
981 _drags->start_grab (event);
984 case RegionViewNameHighlight:
985 case LeftFrameHandle:
986 case RightFrameHandle:
987 if (!clicked_regionview->region()->locked()) {
988 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
989 _drags->set (new TrimDrag (this, item, clicked_regionview, s.by_layer()), event);
996 /* rename happens on edit clicks */
997 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
998 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, s.by_layer()), event);
1003 case ControlPointItem:
1004 _drags->set (new ControlPointDrag (this, item), event);
1008 case AutomationLineItem:
1009 _drags->set (new LineDrag (this, item), event);
1014 if (internal_editing()) {
1015 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
1016 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
1020 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
1024 case AutomationTrackItem:
1026 TimeAxisView* parent = clicked_axisview->get_parent ();
1027 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (clicked_axisview);
1029 if (parent && dynamic_cast<MidiTimeAxisView*> (parent) && atv->show_regions ()) {
1030 /* create a MIDI region so that we have somewhere to put automation */
1031 _drags->set (new RegionCreateDrag (this, item, parent), event);
1033 /* rubberband drag to select automation points */
1034 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
1041 if (smart_mode_action->get_active()) {
1042 /* we're in "smart" joined mode, and we've clicked on a Selection */
1043 double const y = event->button.y + vertical_adjustment.get_value() - canvas_timebars_vsize;
1044 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
1046 /* if we're over an automation track, start a drag of its data */
1047 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
1049 _drags->set (new AutomationRangeDrag (this, atv, selection->time), event, _cursors->up_down);
1052 /* if we're over a track and a region, and in the `object' part of a region,
1053 put a selection around the region and drag both
1055 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1056 if (rtv && _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
1057 boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track> (rtv->route ());
1059 boost::shared_ptr<Playlist> pl = t->playlist ();
1062 boost::shared_ptr<Region> r = pl->top_region_at (event_frame (event));
1064 RegionView* rv = rtv->view()->find_view (r);
1065 clicked_selection = select_range (rv->region()->position(),
1066 rv->region()->last_frame()+1);
1067 _drags->add (new SelectionDrag (this, item, SelectionDrag::SelectionMove));
1068 list<RegionView*> rvs;
1070 _drags->add (new RegionMoveDrag (this, item, rv, rvs, false, false));
1071 _drags->start_grab (event);
1082 case ImageFrameHandleStartItem:
1083 imageframe_start_handle_op(item, event) ;
1086 case ImageFrameHandleEndItem:
1087 imageframe_end_handle_op(item, event) ;
1090 case MarkerViewHandleStartItem:
1091 markerview_item_start_handle_op(item, event) ;
1094 case MarkerViewHandleEndItem:
1095 markerview_item_end_handle_op(item, event) ;
1098 case MarkerViewItem:
1099 start_markerview_grab(item, event) ;
1101 case ImageFrameItem:
1102 start_imageframe_grab(item, event) ;
1118 switch (item_type) {
1120 _drags->set (new LineDrag (this, item), event);
1123 case ControlPointItem:
1124 _drags->set (new ControlPointDrag (this, item), event);
1130 AudioRegionView* arv = dynamic_cast<AudioRegionView *> (clicked_regionview);
1132 _drags->set (new AutomationRangeDrag (this, arv, selection->time), event, _cursors->up_down);
1133 _drags->start_grab (event);
1139 case AutomationLineItem:
1140 _drags->set (new LineDrag (this, item), event);
1150 if (event->type == GDK_BUTTON_PRESS) {
1151 _drags->set (new MouseZoomDrag (this, item), event);
1158 if (internal_editing() && item_type == NoteItem) {
1159 /* drag notes if we're in internal edit mode */
1160 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
1162 } else if ((!internal_editing() || dynamic_cast<AudioRegionView*> (clicked_regionview)) && clicked_regionview) {
1163 /* do time-FX if we're not in internal edit mode, or we are but we clicked on an audio region */
1164 _drags->set (new TimeFXDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1170 _drags->set (new ScrubDrag (this, item), event);
1171 scrub_reversals = 0;
1172 scrub_reverse_distance = 0;
1173 last_scrub_x = event->button.x;
1174 scrubbing_direction = 0;
1175 set_canvas_cursor (_cursors->transparent);
1187 Editor::button_press_handler_2 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1189 Editing::MouseMode const eff = effective_mouse_mode ();
1192 switch (item_type) {
1194 if (internal_editing ()) {
1195 /* no region drags in internal edit mode */
1199 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
1200 add_region_copy_drag (item, event, clicked_regionview);
1202 add_region_drag (item, event, clicked_regionview);
1204 _drags->start_grab (event);
1207 case ControlPointItem:
1208 _drags->set (new ControlPointDrag (this, item), event);
1216 switch (item_type) {
1217 case RegionViewNameHighlight:
1218 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1222 case LeftFrameHandle:
1223 case RightFrameHandle:
1224 if (!internal_editing ()) {
1225 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1230 case RegionViewName:
1231 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer()), event);
1245 /* relax till release */
1251 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1252 temporal_zoom_to_frame (false, event_frame (event));
1254 temporal_zoom_to_frame (true, event_frame(event));
1267 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1269 if (event->type != GDK_BUTTON_PRESS) {
1273 Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->track_canvas->get_window();
1275 if (canvas_window) {
1276 Glib::RefPtr<const Gdk::Window> pointer_window;
1279 Gdk::ModifierType mask;
1281 pointer_window = canvas_window->get_pointer (x, y, mask);
1283 if (pointer_window == track_canvas->get_bin_window()) {
1284 track_canvas->window_to_world (x, y, wx, wy);
1288 pre_press_cursor = current_canvas_cursor;
1290 track_canvas->grab_focus();
1292 if (_session && _session->actively_recording()) {
1298 if (internal_editing()) {
1299 bool leave_internal_edit_mode = false;
1301 switch (item_type) {
1306 if (!dynamic_cast<MidiRegionView*> (clicked_regionview) && !dynamic_cast<AutomationRegionView*> (clicked_regionview)) {
1307 leave_internal_edit_mode = true;
1311 case PlayheadCursorItem:
1313 case TempoMarkerItem:
1314 case MeterMarkerItem:
1318 case RangeMarkerBarItem:
1319 case CdMarkerBarItem:
1320 case TransportMarkerBarItem:
1321 /* button press on these events never does anything to
1322 change the editing mode.
1327 leave_internal_edit_mode = true;
1334 if (leave_internal_edit_mode) {
1335 ActionManager::do_action ("MouseMode", "toggle-internal-edit");
1339 button_selection (item, event, item_type);
1341 if (!_drags->active () &&
1342 (Keyboard::is_delete_event (&event->button) ||
1343 Keyboard::is_context_menu_event (&event->button) ||
1344 Keyboard::is_edit_event (&event->button))) {
1346 /* handled by button release */
1350 switch (event->button.button) {
1352 return button_press_handler_1 (item, event, item_type);
1356 return button_press_handler_2 (item, event, item_type);
1363 return button_press_dispatch (&event->button);
1372 Editor::button_press_dispatch (GdkEventButton* ev)
1374 /* this function is intended only for buttons 4 and above.
1377 Gtkmm2ext::MouseButton b (ev->state, ev->button);
1378 return button_bindings->activate (b, Gtkmm2ext::Bindings::Press);
1382 Editor::button_release_dispatch (GdkEventButton* ev)
1384 /* this function is intended only for buttons 4 and above.
1387 Gtkmm2ext::MouseButton b (ev->state, ev->button);
1388 return button_bindings->activate (b, Gtkmm2ext::Bindings::Release);
1392 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1394 framepos_t where = event_frame (event, 0, 0);
1395 AutomationTimeAxisView* atv = 0;
1397 if (pre_press_cursor) {
1398 set_canvas_cursor (pre_press_cursor);
1399 pre_press_cursor = 0;
1402 /* no action if we're recording */
1404 if (_session && _session->actively_recording()) {
1408 /* see if we're finishing a drag */
1410 bool were_dragging = false;
1411 if (_drags->active ()) {
1412 bool const r = _drags->end_grab (event);
1414 /* grab dragged, so do nothing else */
1418 were_dragging = true;
1421 update_region_layering_order_editor ();
1423 /* edit events get handled here */
1425 if (!_drags->active () && Keyboard::is_edit_event (&event->button)) {
1426 switch (item_type) {
1428 show_region_properties ();
1431 case TempoMarkerItem:
1432 edit_tempo_marker (item);
1435 case MeterMarkerItem:
1436 edit_meter_marker (item);
1439 case RegionViewName:
1440 if (clicked_regionview->name_active()) {
1441 return mouse_rename_region (item, event);
1445 case ControlPointItem:
1446 edit_control_point (item);
1459 /* context menu events get handled here */
1461 if (Keyboard::is_context_menu_event (&event->button)) {
1463 context_click_event = *event;
1465 if (!_drags->active ()) {
1467 /* no matter which button pops up the context menu, tell the menu
1468 widget to use button 1 to drive menu selection.
1471 switch (item_type) {
1473 case FadeInHandleItem:
1475 case FadeOutHandleItem:
1476 popup_fade_context_menu (1, event->button.time, item, item_type);
1479 case StartCrossFadeItem:
1480 popup_xfade_in_context_menu (1, event->button.time, item, item_type);
1483 case EndCrossFadeItem:
1484 popup_xfade_out_context_menu (1, event->button.time, item, item_type);
1488 popup_track_context_menu (1, event->button.time, item_type, false);
1492 case RegionViewNameHighlight:
1493 case LeftFrameHandle:
1494 case RightFrameHandle:
1495 case RegionViewName:
1496 popup_track_context_menu (1, event->button.time, item_type, false);
1500 popup_track_context_menu (1, event->button.time, item_type, true);
1503 case AutomationTrackItem:
1504 popup_track_context_menu (1, event->button.time, item_type, false);
1508 case RangeMarkerBarItem:
1509 case TransportMarkerBarItem:
1510 case CdMarkerBarItem:
1513 popup_ruler_menu (where, item_type);
1517 marker_context_menu (&event->button, item);
1520 case TempoMarkerItem:
1521 tempo_or_meter_marker_context_menu (&event->button, item);
1524 case MeterMarkerItem:
1525 tempo_or_meter_marker_context_menu (&event->button, item);
1528 case CrossfadeViewItem:
1529 popup_track_context_menu (1, event->button.time, item_type, false);
1532 case ControlPointItem:
1533 popup_control_point_context_menu (item, event);
1537 case ImageFrameItem:
1538 popup_imageframe_edit_menu(1, event->button.time, item, true) ;
1540 case ImageFrameTimeAxisItem:
1541 popup_imageframe_edit_menu(1, event->button.time, item, false) ;
1543 case MarkerViewItem:
1544 popup_marker_time_axis_edit_menu(1, event->button.time, item, true) ;
1546 case MarkerTimeAxisItem:
1547 popup_marker_time_axis_edit_menu(1, event->button.time, item, false) ;
1559 /* delete events get handled here */
1561 Editing::MouseMode const eff = effective_mouse_mode ();
1563 if (!_drags->active () && Keyboard::is_delete_event (&event->button)) {
1565 switch (item_type) {
1566 case TempoMarkerItem:
1567 remove_tempo_marker (item);
1570 case MeterMarkerItem:
1571 remove_meter_marker (item);
1575 remove_marker (*item, event);
1579 if (eff == MouseObject) {
1580 remove_clicked_region ();
1584 case ControlPointItem:
1585 remove_control_point (item);
1589 remove_midi_note (item, event);
1598 switch (event->button.button) {
1601 switch (item_type) {
1602 /* see comments in button_press_handler */
1603 case PlayheadCursorItem:
1606 case AutomationLineItem:
1607 case StartSelectionTrimItem:
1608 case EndSelectionTrimItem:
1612 if (!_dragging_playhead) {
1613 snap_to_with_modifier (where, event, 0, true);
1614 mouse_add_new_marker (where);
1618 case CdMarkerBarItem:
1619 if (!_dragging_playhead) {
1620 // if we get here then a dragged range wasn't done
1621 snap_to_with_modifier (where, event, 0, true);
1622 mouse_add_new_marker (where, true);
1627 if (!_dragging_playhead) {
1628 snap_to_with_modifier (where, event);
1629 mouse_add_new_tempo_event (where);
1634 if (!_dragging_playhead) {
1635 mouse_add_new_meter_event (pixel_to_frame (event->button.x));
1646 switch (item_type) {
1647 case AutomationTrackItem:
1648 atv = dynamic_cast<AutomationTimeAxisView*>(clicked_axisview);
1650 atv->add_automation_event (event, where, event->button.y);
1660 switch (item_type) {
1663 /* check that we didn't drag before releasing, since
1664 its really annoying to create new control
1665 points when doing this.
1667 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (clicked_regionview);
1668 if (!were_dragging && arv) {
1669 arv->add_gain_point_event (item, event);
1675 case AutomationTrackItem:
1676 dynamic_cast<AutomationTimeAxisView*>(clicked_axisview)->
1677 add_automation_event (event, where, event->button.y);
1686 set_canvas_cursor (current_canvas_cursor);
1687 if (scrubbing_direction == 0) {
1688 /* no drag, just a click */
1689 switch (item_type) {
1691 play_selected_region ();
1697 /* make sure we stop */
1698 _session->request_transport_speed (0.0);
1707 /* do any (de)selection operations that should occur on button release */
1708 button_selection (item, event, item_type);
1717 switch (item_type) {
1719 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1721 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier|Keyboard::SecondaryModifier))) {
1724 // Button2 click is unused
1739 // x_style_paste (where, 1.0);
1760 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1767 switch (item_type) {
1768 case ControlPointItem:
1769 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1770 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1771 cp->set_visible (true);
1775 at_y = cp->get_y ();
1776 cp->i2w (at_x, at_y);
1780 fraction = 1.0 - (cp->get_y() / cp->line().height());
1782 if (is_drawable() && !_drags->active ()) {
1783 set_canvas_cursor (_cursors->fader);
1786 _verbose_cursor->set (cp->line().get_verbose_cursor_string (fraction), at_x, at_y);
1787 _verbose_cursor->show ();
1792 if (mouse_mode == MouseGain) {
1793 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1795 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredGainLine.get();
1796 if (is_drawable()) {
1797 set_canvas_cursor (_cursors->fader);
1802 case AutomationLineItem:
1803 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1804 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1806 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredAutomationLine.get();
1808 if (is_drawable()) {
1809 set_canvas_cursor (_cursors->fader);
1814 case RegionViewNameHighlight:
1815 if (is_drawable() && doing_object_stuff() && entered_regionview) {
1816 set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1817 _over_region_trim_target = true;
1821 case LeftFrameHandle:
1822 case RightFrameHandle:
1823 if (is_drawable() && doing_object_stuff() && !internal_editing() && entered_regionview) {
1824 set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1828 case StartSelectionTrimItem:
1830 case ImageFrameHandleStartItem:
1831 case MarkerViewHandleStartItem:
1833 if (is_drawable()) {
1834 set_canvas_cursor (_cursors->left_side_trim);
1837 case EndSelectionTrimItem:
1839 case ImageFrameHandleEndItem:
1840 case MarkerViewHandleEndItem:
1842 if (is_drawable()) {
1843 set_canvas_cursor (_cursors->right_side_trim);
1847 case PlayheadCursorItem:
1848 if (is_drawable()) {
1849 switch (_edit_point) {
1851 set_canvas_cursor (_cursors->grabber_edit_point);
1854 set_canvas_cursor (_cursors->grabber);
1860 case RegionViewName:
1862 /* when the name is not an active item, the entire name highlight is for trimming */
1864 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1865 if (mouse_mode == MouseObject && is_drawable()) {
1866 set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1867 _over_region_trim_target = true;
1873 case AutomationTrackItem:
1874 if (is_drawable()) {
1875 Gdk::Cursor *cursor;
1876 switch (mouse_mode) {
1878 cursor = _cursors->selector;
1881 cursor = _cursors->zoom_in;
1884 cursor = _cursors->cross_hair;
1888 set_canvas_cursor (cursor);
1890 AutomationTimeAxisView* atv;
1891 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1892 clear_entered_track = false;
1893 set_entered_track (atv);
1899 case RangeMarkerBarItem:
1900 case TransportMarkerBarItem:
1901 case CdMarkerBarItem:
1904 if (is_drawable()) {
1905 set_canvas_cursor (_cursors->timebar);
1910 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1913 entered_marker = marker;
1914 marker->set_color_rgba (ARDOUR_UI::config()->canvasvar_EnteredMarker.get());
1916 case MeterMarkerItem:
1917 case TempoMarkerItem:
1918 if (is_drawable()) {
1919 set_canvas_cursor (_cursors->timebar);
1923 case FadeInHandleItem:
1924 if (mouse_mode == MouseObject && !internal_editing()) {
1925 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1927 rect->property_fill_color_rgba() = 0xBBBBBBAA;
1929 set_canvas_cursor (_cursors->fade_in);
1933 case FadeOutHandleItem:
1934 if (mouse_mode == MouseObject && !internal_editing()) {
1935 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1937 rect->property_fill_color_rgba() = 0xBBBBBBAA;
1939 set_canvas_cursor (_cursors->fade_out);
1942 case FeatureLineItem:
1944 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1945 line->property_fill_color_rgba() = 0xFF0000FF;
1949 if (smart_mode_action->get_active()) {
1950 set_canvas_cursor ();
1958 /* second pass to handle entered track status in a comprehensible way.
1961 switch (item_type) {
1963 case AutomationLineItem:
1964 case ControlPointItem:
1965 /* these do not affect the current entered track state */
1966 clear_entered_track = false;
1969 case AutomationTrackItem:
1970 /* handled above already */
1974 set_entered_track (0);
1982 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent*, ItemType item_type)
1992 switch (item_type) {
1993 case ControlPointItem:
1994 cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
1995 if (cp->line().the_list()->interpolation() != AutomationList::Discrete) {
1996 if (cp->line().npoints() > 1 && !cp->get_selected()) {
1997 cp->set_visible (false);
2001 if (is_drawable()) {
2002 set_canvas_cursor (current_canvas_cursor);
2005 _verbose_cursor->hide ();
2008 case RegionViewNameHighlight:
2009 case LeftFrameHandle:
2010 case RightFrameHandle:
2011 case StartSelectionTrimItem:
2012 case EndSelectionTrimItem:
2013 case PlayheadCursorItem:
2016 case ImageFrameHandleStartItem:
2017 case ImageFrameHandleEndItem:
2018 case MarkerViewHandleStartItem:
2019 case MarkerViewHandleEndItem:
2022 _over_region_trim_target = false;
2024 if (is_drawable()) {
2025 set_canvas_cursor (current_canvas_cursor);
2030 case AutomationLineItem:
2031 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
2033 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
2035 line->property_fill_color_rgba() = al->get_line_color();
2037 if (is_drawable()) {
2038 set_canvas_cursor (current_canvas_cursor);
2042 case RegionViewName:
2043 /* see enter_handler() for notes */
2044 _over_region_trim_target = false;
2046 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
2047 if (is_drawable() && mouse_mode == MouseObject) {
2048 set_canvas_cursor (current_canvas_cursor);
2053 case RangeMarkerBarItem:
2054 case TransportMarkerBarItem:
2055 case CdMarkerBarItem:
2059 if (is_drawable()) {
2060 set_canvas_cursor (current_canvas_cursor);
2065 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2069 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
2070 location_flags_changed (loc, this);
2073 case MeterMarkerItem:
2074 case TempoMarkerItem:
2076 if (is_drawable()) {
2077 set_canvas_cursor (current_canvas_cursor);
2082 case FadeInHandleItem:
2083 case FadeOutHandleItem:
2084 rv = static_cast<RegionView*>(item->get_data ("regionview"));
2086 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
2088 rect->property_fill_color_rgba() = rv->get_fill_color();
2089 rect->property_outline_pixels() = 0;
2092 set_canvas_cursor (current_canvas_cursor);
2095 case AutomationTrackItem:
2096 if (is_drawable()) {
2097 set_canvas_cursor (current_canvas_cursor);
2098 clear_entered_track = true;
2099 Glib::signal_idle().connect (sigc::mem_fun(*this, &Editor::left_automation_track));
2102 case FeatureLineItem:
2104 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
2105 line->property_fill_color_rgba() = (guint) ARDOUR_UI::config()->canvasvar_ZeroLine.get();;
2117 Editor::left_automation_track ()
2119 if (clear_entered_track) {
2120 set_entered_track (0);
2121 clear_entered_track = false;
2127 Editor::scrub (framepos_t frame, double current_x)
2131 if (scrubbing_direction == 0) {
2133 _session->request_locate (frame, false);
2134 _session->request_transport_speed (0.1);
2135 scrubbing_direction = 1;
2139 if (last_scrub_x > current_x) {
2141 /* pointer moved to the left */
2143 if (scrubbing_direction > 0) {
2145 /* we reversed direction to go backwards */
2148 scrub_reverse_distance += (int) (last_scrub_x - current_x);
2152 /* still moving to the left (backwards) */
2154 scrub_reversals = 0;
2155 scrub_reverse_distance = 0;
2157 delta = 0.01 * (last_scrub_x - current_x);
2158 _session->request_transport_speed_nonzero (_session->transport_speed() - delta);
2162 /* pointer moved to the right */
2164 if (scrubbing_direction < 0) {
2165 /* we reversed direction to go forward */
2168 scrub_reverse_distance += (int) (current_x - last_scrub_x);
2171 /* still moving to the right */
2173 scrub_reversals = 0;
2174 scrub_reverse_distance = 0;
2176 delta = 0.01 * (current_x - last_scrub_x);
2177 _session->request_transport_speed_nonzero (_session->transport_speed() + delta);
2181 /* if there have been more than 2 opposite motion moves detected, or one that moves
2182 back more than 10 pixels, reverse direction
2185 if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
2187 if (scrubbing_direction > 0) {
2188 /* was forwards, go backwards */
2189 _session->request_transport_speed (-0.1);
2190 scrubbing_direction = -1;
2192 /* was backwards, go forwards */
2193 _session->request_transport_speed (0.1);
2194 scrubbing_direction = 1;
2197 scrub_reverse_distance = 0;
2198 scrub_reversals = 0;
2202 last_scrub_x = current_x;
2206 Editor::motion_handler (ArdourCanvas::Item* /*item*/, GdkEvent* event, bool from_autoscroll)
2208 _last_motion_y = event->motion.y;
2210 if (event->motion.is_hint) {
2213 /* We call this so that MOTION_NOTIFY events continue to be
2214 delivered to the canvas. We need to do this because we set
2215 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
2216 the density of the events, at the expense of a round-trip
2217 to the server. Given that this will mostly occur on cases
2218 where DISPLAY = :0.0, and given the cost of what the motion
2219 event might do, its a good tradeoff.
2222 track_canvas->get_pointer (x, y);
2225 if (current_stepping_trackview) {
2226 /* don't keep the persistent stepped trackview if the mouse moves */
2227 current_stepping_trackview = 0;
2228 step_timeout.disconnect ();
2231 if (_session && _session->actively_recording()) {
2232 /* Sorry. no dragging stuff around while we record */
2236 JoinObjectRangeState const old = _join_object_range_state;
2237 update_join_object_range_location (event->motion.x, event->motion.y);
2238 if (_join_object_range_state != old) {
2239 set_canvas_cursor ();
2242 if (_over_region_trim_target) {
2243 set_canvas_cursor_for_region_view (event->motion.x, entered_regionview);
2246 bool handled = false;
2247 if (_drags->active ()) {
2248 handled = _drags->motion_handler (event, from_autoscroll);
2255 track_canvas_motion (event);
2260 Editor::can_remove_control_point (ArdourCanvas::Item* item)
2262 ControlPoint* control_point;
2264 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2265 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2269 AutomationLine& line = control_point->line ();
2270 if (dynamic_cast<AudioRegionGainLine*> (&line)) {
2271 /* we shouldn't remove the first or last gain point in region gain lines */
2272 if (line.is_last_point(*control_point) || line.is_first_point(*control_point)) {
2281 Editor::remove_control_point (ArdourCanvas::Item* item)
2283 if (!can_remove_control_point (item)) {
2287 ControlPoint* control_point;
2289 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2290 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2294 control_point->line().remove_point (*control_point);
2298 Editor::edit_control_point (ArdourCanvas::Item* item)
2300 ControlPoint* p = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"));
2303 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2307 ControlPointDialog d (p);
2308 d.set_position (Gtk::WIN_POS_MOUSE);
2311 if (d.run () != RESPONSE_ACCEPT) {
2315 p->line().modify_point_y (*p, d.get_y_fraction ());
2319 Editor::edit_note (ArdourCanvas::Item* item)
2321 ArdourCanvas::CanvasNoteEvent* e = dynamic_cast<ArdourCanvas::CanvasNoteEvent*> (item);
2324 EditNoteDialog d (&e->region_view(), e);
2325 d.set_position (Gtk::WIN_POS_MOUSE);
2333 Editor::visible_order_range (int* low, int* high) const
2335 *low = TimeAxisView::max_order ();
2338 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
2340 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
2342 if (!rtv->hidden()) {
2344 if (*high < rtv->order()) {
2345 *high = rtv->order ();
2348 if (*low > rtv->order()) {
2349 *low = rtv->order ();
2356 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
2358 /* Either add to or set the set the region selection, unless
2359 this is an alignment click (control used)
2362 if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
2363 TimeAxisView* tv = &rv.get_time_axis_view();
2364 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
2366 if (rtv && rtv->is_track()) {
2367 speed = rtv->track()->speed();
2370 framepos_t where = get_preferred_edit_position();
2374 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
2376 align_region (rv.region(), SyncPoint, (framepos_t) (where * speed));
2378 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
2380 align_region (rv.region(), End, (framepos_t) (where * speed));
2384 align_region (rv.region(), Start, (framepos_t) (where * speed));
2391 Editor::collect_new_region_view (RegionView* rv)
2393 latest_regionviews.push_back (rv);
2397 Editor::collect_and_select_new_region_view (RegionView* rv)
2400 latest_regionviews.push_back (rv);
2404 Editor::cancel_selection ()
2406 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2407 (*i)->hide_selection ();
2410 selection->clear ();
2411 clicked_selection = 0;
2416 Editor::point_trim (GdkEvent* event, framepos_t new_bound)
2418 RegionView* rv = clicked_regionview;
2420 /* Choose action dependant on which button was pressed */
2421 switch (event->button.button) {
2423 begin_reversible_command (_("start point trim"));
2425 if (selection->selected (rv)) {
2426 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
2427 i != selection->regions.by_layer().end(); ++i)
2429 if (!(*i)->region()->locked()) {
2430 (*i)->region()->clear_changes ();
2431 (*i)->region()->trim_front (new_bound);
2432 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2437 if (!rv->region()->locked()) {
2438 rv->region()->clear_changes ();
2439 rv->region()->trim_front (new_bound);
2440 _session->add_command(new StatefulDiffCommand (rv->region()));
2444 commit_reversible_command();
2448 begin_reversible_command (_("End point trim"));
2450 if (selection->selected (rv)) {
2452 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
2454 if (!(*i)->region()->locked()) {
2455 (*i)->region()->clear_changes();
2456 (*i)->region()->trim_end (new_bound);
2457 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2463 if (!rv->region()->locked()) {
2464 rv->region()->clear_changes ();
2465 rv->region()->trim_end (new_bound);
2466 _session->add_command (new StatefulDiffCommand (rv->region()));
2470 commit_reversible_command();
2479 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* /*event*/)
2484 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2485 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2489 Location* location = find_location_from_marker (marker, is_start);
2490 location->set_hidden (true, this);
2495 Editor::reposition_zoom_rect (framepos_t start, framepos_t end)
2497 double x1 = frame_to_pixel (start);
2498 double x2 = frame_to_pixel (end);
2499 double y2 = full_canvas_height - 1.0;
2501 zoom_rect->property_x1() = x1;
2502 zoom_rect->property_y1() = 1.0;
2503 zoom_rect->property_x2() = x2;
2504 zoom_rect->property_y2() = y2;
2509 Editor::mouse_rename_region (ArdourCanvas::Item* /*item*/, GdkEvent* /*event*/)
2511 using namespace Gtkmm2ext;
2513 ArdourPrompter prompter (false);
2515 prompter.set_prompt (_("Name for region:"));
2516 prompter.set_initial_text (clicked_regionview->region()->name());
2517 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
2518 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
2519 prompter.show_all ();
2520 switch (prompter.run ()) {
2521 case Gtk::RESPONSE_ACCEPT:
2523 prompter.get_result(str);
2525 clicked_regionview->region()->set_name (str);
2534 Editor::mouse_brush_insert_region (RegionView* rv, framepos_t pos)
2536 /* no brushing without a useful snap setting */
2538 switch (_snap_mode) {
2540 return; /* can't work because it allows region to be placed anywhere */
2545 switch (_snap_type) {
2553 /* don't brush a copy over the original */
2555 if (pos == rv->region()->position()) {
2559 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
2561 if (rtv == 0 || !rtv->is_track()) {
2565 boost::shared_ptr<Playlist> playlist = rtv->playlist();
2566 double speed = rtv->track()->speed();
2568 playlist->clear_changes ();
2569 boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region(), true));
2570 playlist->add_region (new_region, (framepos_t) (pos * speed));
2571 _session->add_command (new StatefulDiffCommand (playlist));
2573 // playlist is frozen, so we have to update manually XXX this is disgusting
2575 playlist->RegionAdded (new_region); /* EMIT SIGNAL */
2579 Editor::track_height_step_timeout ()
2581 if (get_microseconds() - last_track_height_step_timestamp < 250000) {
2582 current_stepping_trackview = 0;
2589 Editor::add_region_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2591 assert (region_view);
2593 if (!region_view->region()->playlist()) {
2597 _region_motion_group->raise_to_top ();
2599 if (Config->get_edit_mode() == Splice) {
2600 _drags->add (new RegionSpliceDrag (this, item, region_view, selection->regions.by_layer()));
2602 RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id);
2603 _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), false, false));
2606 /* sync the canvas to what we think is its current state */
2607 update_canvas_now();
2611 Editor::add_region_copy_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2613 assert (region_view);
2615 if (!region_view->region()->playlist()) {
2619 _region_motion_group->raise_to_top ();
2621 RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id);
2622 _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), false, true));
2626 Editor::add_region_brush_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2628 assert (region_view);
2630 if (!region_view->region()->playlist()) {
2634 if (Config->get_edit_mode() == Splice) {
2638 RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id);
2639 _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), true, false));
2641 begin_reversible_command (Operations::drag_region_brush);
2644 /** Start a grab where a time range is selected, track(s) are selected, and the
2645 * user clicks and drags a region with a modifier in order to create a new region containing
2646 * the section of the clicked region that lies within the time range.
2649 Editor::start_selection_grab (ArdourCanvas::Item* /*item*/, GdkEvent* event)
2651 if (clicked_regionview == 0) {
2655 /* lets try to create new Region for the selection */
2657 vector<boost::shared_ptr<Region> > new_regions;
2658 create_region_from_selection (new_regions);
2660 if (new_regions.empty()) {
2664 /* XXX fix me one day to use all new regions */
2666 boost::shared_ptr<Region> region (new_regions.front());
2668 /* add it to the current stream/playlist.
2670 tricky: the streamview for the track will add a new regionview. we will
2671 catch the signal it sends when it creates the regionview to
2672 set the regionview we want to then drag.
2675 latest_regionviews.clear();
2676 sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
2678 /* A selection grab currently creates two undo/redo operations, one for
2679 creating the new region and another for moving it.
2682 begin_reversible_command (Operations::selection_grab);
2684 boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
2686 playlist->clear_changes ();
2687 clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
2688 _session->add_command(new StatefulDiffCommand (playlist));
2690 commit_reversible_command ();
2694 if (latest_regionviews.empty()) {
2695 /* something went wrong */
2699 /* we need to deselect all other regionviews, and select this one
2700 i'm ignoring undo stuff, because the region creation will take care of it
2702 selection->set (latest_regionviews);
2704 _drags->set (new RegionMoveDrag (this, latest_regionviews.front()->get_canvas_group(), latest_regionviews.front(), latest_regionviews, false, false), event);
2710 if (_drags->active ()) {
2713 selection->clear ();
2718 Editor::set_internal_edit (bool yn)
2720 if (_internal_editing == yn) {
2724 _internal_editing = yn;
2727 pre_internal_mouse_mode = mouse_mode;
2728 pre_internal_snap_type = _snap_type;
2729 pre_internal_snap_mode = _snap_mode;
2731 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2732 (*i)->enter_internal_edit_mode ();
2735 set_snap_to (internal_snap_type);
2736 set_snap_mode (internal_snap_mode);
2740 internal_snap_mode = _snap_mode;
2741 internal_snap_type = _snap_type;
2743 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2744 (*i)->leave_internal_edit_mode ();
2747 if (mouse_mode == MouseDraw && pre_internal_mouse_mode != MouseDraw) {
2748 /* we were drawing .. flip back to something sensible */
2749 set_mouse_mode (pre_internal_mouse_mode);
2752 set_snap_to (pre_internal_snap_type);
2753 set_snap_mode (pre_internal_snap_mode);
2756 set_canvas_cursor ();
2759 /** Update _join_object_range_state which indicate whether we are over the top or bottom half of a region view,
2760 * used by the `join object/range' tool mode.
2763 Editor::update_join_object_range_location (double /*x*/, double y)
2765 /* XXX: actually, this decides based on whether the mouse is in the top
2766 or bottom half of a the waveform part RouteTimeAxisView;
2768 Note that entered_{track,regionview} is not always setup (e.g. if
2769 the mouse is over a TimeSelection), and to get a Region
2770 that we're over requires searching the playlist.
2773 if (!smart_mode_action->get_active() || (mouse_mode != MouseRange && mouse_mode != MouseObject)) {
2774 _join_object_range_state = JOIN_OBJECT_RANGE_NONE;
2778 if (mouse_mode == MouseObject) {
2779 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2780 } else if (mouse_mode == MouseRange) {
2781 _join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
2784 /* XXX: maybe we should make entered_track work in all cases, rather than resorting to this */
2785 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y + vertical_adjustment.get_value() - canvas_timebars_vsize);
2789 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
2794 rtv->canvas_display()->w2i (cx, cy);
2796 double const c = cy / (rtv->view()->child_height() - TimeAxisViewItem::NAME_HIGHLIGHT_SIZE);
2798 double const f = modf (c, &d);
2800 _join_object_range_state = f < 0.5 ? JOIN_OBJECT_RANGE_RANGE : JOIN_OBJECT_RANGE_OBJECT;
2806 Editor::effective_mouse_mode () const
2808 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
2810 } else if (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE) {
2818 Editor::remove_midi_note (ArdourCanvas::Item* item, GdkEvent *)
2820 ArdourCanvas::CanvasNoteEvent* e = dynamic_cast<ArdourCanvas::CanvasNoteEvent*> (item);
2823 e->region_view().delete_note (e->note ());
2827 Editor::set_canvas_cursor_for_region_view (double x, RegionView* rv)
2831 ArdourCanvas::Group* g = rv->get_canvas_group ();
2832 ArdourCanvas::Group* p = g->get_parent_group ();
2834 /* Compute x in region view parent coordinates */
2838 double x1, x2, y1, y2;
2839 g->get_bounds (x1, y1, x2, y2);
2841 /* Halfway across the region */
2842 double const h = (x1 + x2) / 2;
2844 Trimmable::CanTrim ct = rv->region()->can_trim ();
2846 if (ct & Trimmable::FrontTrimEarlier) {
2847 set_canvas_cursor (_cursors->left_side_trim);
2849 set_canvas_cursor (_cursors->left_side_trim_right_only);
2852 if (ct & Trimmable::EndTrimLater) {
2853 set_canvas_cursor (_cursors->right_side_trim);
2855 set_canvas_cursor (_cursors->right_side_trim_left_only);
2860 /** Obtain the pointer position in world coordinates */
2862 Editor::get_pointer_position (double& x, double& y) const
2865 track_canvas->get_pointer (px, py);
2866 track_canvas->window_to_world (px, py, x, y);