2 Copyright (C) 2000-2001 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
28 #include "pbd/error.h"
29 #include "pbd/enumwriter.h"
30 #include "pbd/memento_command.h"
31 #include "pbd/basename.h"
32 #include "pbd/stateful_diff_command.h"
34 #include "gtkmm2ext/bindings.h"
35 #include "gtkmm2ext/utils.h"
36 #include "gtkmm2ext/tearoff.h"
38 #include "ardour_ui.h"
40 #include "canvas-note.h"
42 #include "time_axis_view.h"
43 #include "audio_time_axis.h"
44 #include "audio_region_view.h"
45 #include "midi_region_view.h"
47 #include "streamview.h"
48 #include "region_gain_line.h"
49 #include "automation_time_axis.h"
50 #include "control_point.h"
53 #include "selection.h"
56 #include "rgb_macros.h"
57 #include "control_point_dialog.h"
58 #include "editor_drag.h"
59 #include "automation_region_view.h"
60 #include "edit_note_dialog.h"
61 #include "mouse_cursors.h"
62 #include "editor_cursors.h"
63 #include "verbose_cursor.h"
65 #include "ardour/audioregion.h"
66 #include "ardour/operations.h"
67 #include "ardour/playlist.h"
68 #include "ardour/profile.h"
69 #include "ardour/region_factory.h"
70 #include "ardour/route.h"
71 #include "ardour/session.h"
72 #include "ardour/types.h"
79 using namespace ARDOUR;
82 using namespace Editing;
83 using Gtkmm2ext::Keyboard;
86 Editor::mouse_frame (framepos_t& where, bool& in_track_canvas) const
88 /* gdk_window_get_pointer() has X11's XQueryPointer semantics in that it only
89 pays attentions to subwindows. this means that menu windows are ignored, and
90 if the pointer is in a menu, the return window from the call will be the
91 the regular subwindow *under* the menu.
93 this matters quite a lot if the pointer is moving around in a menu that overlaps
94 the track canvas because we will believe that we are within the track canvas
95 when we are not. therefore, we track enter/leave events for the track canvas
96 and allow that to override the result of gdk_window_get_pointer().
99 if (!within_track_canvas) {
105 Gdk::ModifierType mask;
106 Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->track_canvas->get_window();
107 Glib::RefPtr<const Gdk::Window> pointer_window;
109 if (!canvas_window) {
113 pointer_window = canvas_window->get_pointer (x, y, mask);
115 if (pointer_window == track_canvas->get_bin_window()) {
118 in_track_canvas = true;
121 in_track_canvas = false;
126 event.type = GDK_BUTTON_RELEASE;
130 where = event_frame (&event, 0, 0);
135 Editor::event_frame (GdkEvent const * event, double* pcx, double* pcy) const
149 switch (event->type) {
150 case GDK_BUTTON_RELEASE:
151 case GDK_BUTTON_PRESS:
152 case GDK_2BUTTON_PRESS:
153 case GDK_3BUTTON_PRESS:
154 *pcx = event->button.x;
155 *pcy = event->button.y;
156 _trackview_group->w2i(*pcx, *pcy);
158 case GDK_MOTION_NOTIFY:
159 *pcx = event->motion.x;
160 *pcy = event->motion.y;
161 _trackview_group->w2i(*pcx, *pcy);
163 case GDK_ENTER_NOTIFY:
164 case GDK_LEAVE_NOTIFY:
165 track_canvas->w2c(event->crossing.x, event->crossing.y, *pcx, *pcy);
168 case GDK_KEY_RELEASE:
169 // track_canvas->w2c(event->key.x, event->key.y, *pcx, *pcy);
172 warning << string_compose (_("Editor::event_frame() used on unhandled event type %1"), event->type) << endmsg;
176 /* note that pixel_to_frame() never returns less than zero, so even if the pixel
177 position is negative (as can be the case with motion events in particular),
178 the frame location is always positive.
181 return pixel_to_frame (*pcx);
185 Editor::which_grabber_cursor ()
187 Gdk::Cursor* c = _cursors->grabber;
189 if (_internal_editing) {
190 switch (mouse_mode) {
192 c = _cursors->midi_pencil;
196 c = _cursors->grabber_note;
200 c = _cursors->midi_resize;
209 switch (_edit_point) {
211 c = _cursors->grabber_edit_point;
214 boost::shared_ptr<Movable> m = _movable.lock();
215 if (m && m->locked()) {
216 c = _cursors->speaker;
226 Editor::set_current_trimmable (boost::shared_ptr<Trimmable> t)
228 boost::shared_ptr<Trimmable> st = _trimmable.lock();
230 if (!st || st == t) {
232 set_canvas_cursor ();
237 Editor::set_current_movable (boost::shared_ptr<Movable> m)
239 boost::shared_ptr<Movable> sm = _movable.lock();
241 if (!sm || sm != m) {
243 set_canvas_cursor ();
248 Editor::set_canvas_cursor ()
250 switch (mouse_mode) {
252 current_canvas_cursor = _cursors->selector;
256 current_canvas_cursor = which_grabber_cursor();
260 current_canvas_cursor = _cursors->midi_pencil;
264 current_canvas_cursor = _cursors->cross_hair;
268 if (Keyboard::the_keyboard().key_is_down (GDK_Control_L)) {
269 current_canvas_cursor = _cursors->zoom_out;
271 current_canvas_cursor = _cursors->zoom_in;
276 current_canvas_cursor = _cursors->time_fx; // just use playhead
280 current_canvas_cursor = _cursors->speaker;
284 switch (_join_object_range_state) {
285 case JOIN_OBJECT_RANGE_NONE:
287 case JOIN_OBJECT_RANGE_OBJECT:
288 current_canvas_cursor = which_grabber_cursor ();
290 case JOIN_OBJECT_RANGE_RANGE:
291 current_canvas_cursor = _cursors->selector;
295 /* up-down cursor as a cue that automation can be dragged up and down when in join object/range mode */
296 if ( get_smart_mode() ) {
298 get_pointer_position (x, y);
299 ArdourCanvas::Item* i = track_canvas->get_item_at (x, y);
300 if (i && i->property_parent() && (*i->property_parent()).get_data (X_("timeselection"))) {
301 pair<TimeAxisView*, int> tvp = trackview_by_y_position (_last_motion_y + vertical_adjustment.get_value() - canvas_timebars_vsize);
302 if (dynamic_cast<AutomationTimeAxisView*> (tvp.first)) {
303 current_canvas_cursor = _cursors->up_down;
308 set_canvas_cursor (current_canvas_cursor, true);
312 Editor::mouse_mode_object_range_toggled()
314 MouseMode m = mouse_mode;
316 Glib::RefPtr<Action> act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-range"));
318 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
320 if (tact->get_active())
321 m = MouseObject; //Smart mode turned to ON, force editing to Object mode
323 set_mouse_mode(m, true); //call this so the button styles can get updated
327 Editor::set_mouse_mode (MouseMode m, bool force)
329 if (_drags->active ()) {
333 if (!force && m == mouse_mode) {
337 Glib::RefPtr<Action> act;
341 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-range"));
345 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-object"));
349 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-draw"));
353 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-gain"));
357 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-zoom"));
361 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-timefx"));
365 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-audition"));
371 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
374 /* go there and back to ensure that the toggled handler is called to set up mouse_mode */
375 tact->set_active (false);
376 tact->set_active (true);
378 //NOTE: this will result in a call to mouse_mode_toggled which does the heavy lifting
382 Editor::mouse_mode_toggled (MouseMode m)
384 Glib::RefPtr<Action> act;
385 Glib::RefPtr<ToggleAction> tact;
389 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-range"));
393 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-object"));
397 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-draw"));
401 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-gain"));
405 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-zoom"));
409 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-timefx"));
413 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-audition"));
419 tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
422 if (!tact->get_active()) {
423 /* this was just the notification that the old mode has been
424 * left. we'll get called again with the new mode active in a
432 act = ActionManager::get_action (X_("MouseMode"), X_("toggle-internal-edit"));
433 tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
434 tact->set_active (true);
440 if (_session && mouse_mode == MouseAudition) {
441 /* stop transport and reset default speed to avoid oddness with
443 _session->request_transport_speed (0.0, true);
450 //TODO: set button styles for smart buttons
452 if ( smart_mode_action->get_active() ) {
453 if( mouse_mode == MouseObject ) { //smart active and object active
454 smart_mode_button.set_active(1);
455 smart_mode_button.set_name("smart mode button");
456 mouse_move_button.set_name("smart mode button");
457 } else { //smart active but object inactive
458 smart_mode_button.set_active(0);
459 smart_mode_button.set_name("smart mode button");
460 mouse_move_button.set_name("mouse mode button");
463 smart_mode_button.set_active(0);
464 smart_mode_button.set_name("mouse mode button");
465 mouse_move_button.set_name("mouse mode button");
469 set_canvas_cursor ();
470 set_gain_envelope_visibility ();
472 MouseModeChanged (); /* EMIT SIGNAL */
476 Editor::step_mouse_mode (bool next)
478 switch (current_mouse_mode()) {
481 if (Profile->get_sae()) {
482 set_mouse_mode (MouseZoom);
484 set_mouse_mode (MouseRange);
487 set_mouse_mode (MouseTimeFX);
492 if (next) set_mouse_mode (MouseDraw);
493 else set_mouse_mode (MouseObject);
497 if (next) set_mouse_mode (MouseZoom);
498 else set_mouse_mode (MouseRange);
503 if (Profile->get_sae()) {
504 set_mouse_mode (MouseTimeFX);
506 set_mouse_mode (MouseGain);
509 if (Profile->get_sae()) {
510 set_mouse_mode (MouseObject);
512 set_mouse_mode (MouseDraw);
518 if (next) set_mouse_mode (MouseTimeFX);
519 else set_mouse_mode (MouseZoom);
524 set_mouse_mode (MouseAudition);
526 if (Profile->get_sae()) {
527 set_mouse_mode (MouseZoom);
529 set_mouse_mode (MouseGain);
535 if (next) set_mouse_mode (MouseObject);
536 else set_mouse_mode (MouseTimeFX);
542 Editor::toggle_internal_editing_from_double_click (GdkEvent* event)
544 if (_drags->active()) {
545 _drags->end_grab (event);
548 ActionManager::do_action ("MouseMode", "toggle-internal-edit");
550 /* prevent reversion of edit cursor on button release */
552 pre_press_cursor = 0;
558 Editor::button_selection (ArdourCanvas::Item* /*item*/, GdkEvent* event, ItemType item_type)
560 /* in object/audition/timefx/gain-automation mode,
561 any button press sets the selection if the object
562 can be selected. this is a bit of hack, because
563 we want to avoid this if the mouse operation is a
566 note: not dbl-click or triple-click
568 Also note that there is no region selection in internal edit mode, otherwise
569 for operations operating on the selection (e.g. cut) it is not obvious whether
570 to cut notes or regions.
573 if (((mouse_mode != MouseObject) &&
574 (mouse_mode != MouseAudition || item_type != RegionItem) &&
575 (mouse_mode != MouseTimeFX || item_type != RegionItem) &&
576 (mouse_mode != MouseGain) &&
577 (mouse_mode != MouseDraw)) ||
578 ((event->type != GDK_BUTTON_PRESS && event->type != GDK_BUTTON_RELEASE) || event->button.button > 3) ||
579 (internal_editing() && mouse_mode != MouseTimeFX)) {
584 if (event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) {
586 if ((event->button.state & Keyboard::RelevantModifierKeyMask) && event->button.button != 1) {
588 /* almost no selection action on modified button-2 or button-3 events */
590 if (item_type != RegionItem && event->button.button != 2) {
596 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
597 bool press = (event->type == GDK_BUTTON_PRESS);
601 if (!get_smart_mode() || (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT)) {
603 if (mouse_mode != MouseRange) {
604 set_selected_regionview_from_click (press, op);
606 /* don't change the selection unless the
607 clicked track is not currently selected. if
608 so, "collapse" the selection to just this
611 if (!selection->selected (clicked_axisview)) {
612 set_selected_track_as_side_effect (Selection::Set);
616 if (mouse_mode != MouseRange) {
617 set_selected_regionview_from_click (press, op);
623 case RegionViewNameHighlight:
625 case LeftFrameHandle:
626 case RightFrameHandle:
627 if ( mouse_mode != MouseRange ) {
628 set_selected_regionview_from_click (press, op);
629 } else if (event->type == GDK_BUTTON_PRESS) {
630 set_selected_track_as_side_effect (op);
634 case FadeInHandleItem:
636 case FadeOutHandleItem:
638 case StartCrossFadeItem:
639 case EndCrossFadeItem:
640 if ( mouse_mode != MouseRange ) {
641 set_selected_regionview_from_click (press, op);
642 } else if (event->type == GDK_BUTTON_PRESS) {
643 set_selected_track_as_side_effect (op);
647 case ControlPointItem:
648 set_selected_track_as_side_effect (op);
649 if ( mouse_mode != MouseRange ) {
650 set_selected_control_point_from_click (press, op);
655 /* for context click, select track */
656 if (event->button.button == 3) {
657 selection->clear_tracks ();
658 set_selected_track_as_side_effect (op);
662 case AutomationTrackItem:
663 set_selected_track_as_side_effect (op);
672 Editor::button_press_handler_1 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
674 /* single mouse clicks on any of these item types operate
675 independent of mouse mode, mostly because they are
676 not on the main track canvas or because we want
681 case PlayheadCursorItem:
682 _drags->set (new CursorDrag (this, item, true), event);
686 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
687 hide_marker (item, event);
689 _drags->set (new MarkerDrag (this, item), event);
693 case TempoMarkerItem:
695 TempoMarker* m = reinterpret_cast<TempoMarker*> (item->get_data ("marker"));
697 if (m->tempo().movable ()) {
699 new TempoMarkerDrag (
702 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
712 case MeterMarkerItem:
714 MeterMarker* m = reinterpret_cast<MeterMarker*> (item->get_data ("marker"));
716 if (m->meter().movable ()) {
718 new MeterMarkerDrag (
721 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
734 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
735 _drags->set (new CursorDrag (this, &playhead_cursor->canvas_item, false), event);
741 case RangeMarkerBarItem:
742 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
743 _drags->set (new CursorDrag (this, &playhead_cursor->canvas_item, false), event);
745 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateRangeMarker), event);
750 case CdMarkerBarItem:
751 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
752 _drags->set (new CursorDrag (this, &playhead_cursor->canvas_item, false), event);
754 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateCDMarker), event);
759 case TransportMarkerBarItem:
760 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
761 _drags->set (new CursorDrag (this, &playhead_cursor->canvas_item, false), event);
763 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateTransportMarker), event);
772 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
773 /* special case: allow trim of range selections in joined object mode;
774 in theory eff should equal MouseRange in this case, but it doesn't
775 because entering the range selection canvas item results in entered_regionview
776 being set to 0, so update_join_object_range_location acts as if we aren't
779 if (item_type == StartSelectionTrimItem) {
780 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
781 } else if (item_type == EndSelectionTrimItem) {
782 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
786 Editing::MouseMode eff = effective_mouse_mode ();
788 /* special case: allow drag of region fade in/out in object mode with join object/range enabled */
789 if (item_type == FadeInHandleItem || item_type == FadeOutHandleItem) {
796 case StartSelectionTrimItem:
797 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
800 case EndSelectionTrimItem:
801 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
805 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
806 start_selection_grab (item, event);
807 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
808 /* grab selection for moving */
809 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionMove), event);
811 double const y = event->button.y + vertical_adjustment.get_value() - canvas_timebars_vsize;
812 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
814 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
815 if ( get_smart_mode() && atv) {
816 /* smart "join" mode: drag automation */
817 _drags->set (new AutomationRangeDrag (this, atv, selection->time), event, _cursors->up_down);
819 /* this was debated, but decided the more common action was to
820 make a new selection */
821 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
828 if (internal_editing()) {
829 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
830 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
834 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::RangeSelectModifier)) {
835 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionExtend), event);
837 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
843 case RegionViewNameHighlight:
844 if (!clicked_regionview->region()->locked()) {
845 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
846 _drags->set (new TrimDrag (this, item, clicked_regionview, s.by_layer()), event);
852 if (!internal_editing()) {
853 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::RangeSelectModifier)) {
854 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionExtend), event);
856 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
866 if (internal_editing()) {
867 /* trim notes if we're in internal edit mode and near the ends of the note */
868 ArdourCanvas::CanvasNote* cn = dynamic_cast<ArdourCanvas::CanvasNote*> (item);
869 if (cn && cn->big_enough_to_trim() && cn->mouse_near_ends()) {
870 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
872 _drags->set (new NoteDrag (this, item), event);
886 if (internal_editing()) {
887 ArdourCanvas::CanvasNoteEvent* cn = dynamic_cast<ArdourCanvas::CanvasNoteEvent*> (item);
888 if (cn->mouse_near_ends()) {
889 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
891 _drags->set (new NoteDrag (this, item), event);
901 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)) &&
902 event->type == GDK_BUTTON_PRESS) {
904 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
906 } else if (event->type == GDK_BUTTON_PRESS) {
909 case FadeInHandleItem:
911 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
912 _drags->set (new FadeInDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), s), event, _cursors->fade_in);
916 case FadeOutHandleItem:
918 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
919 _drags->set (new FadeOutDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), s), event, _cursors->fade_out);
923 case StartCrossFadeItem:
924 case EndCrossFadeItem:
925 /* we might allow user to grab inside the fade to trim a region with preserve_fade_anchor. for not this is not fully implemented */
926 // if (!clicked_regionview->region()->locked()) {
927 // RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
928 // _drags->set (new TrimDrag (this, item, clicked_regionview, s.by_layer(), true), event);
933 case FeatureLineItem:
935 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier)) {
936 remove_transient(item);
940 _drags->set (new FeatureLineDrag (this, item), event);
946 if (dynamic_cast<AutomationRegionView*> (clicked_regionview)) {
947 /* click on an automation region view; do nothing here and let the ARV's signal handler
953 if (internal_editing ()) {
954 if (event->type == GDK_2BUTTON_PRESS && event->button.button == 1) {
955 Glib::RefPtr<Action> act = ActionManager::get_action (X_("MouseMode"), X_("toggle-internal-edit"));
961 /* click on a normal region view */
962 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
963 add_region_copy_drag (item, event, clicked_regionview);
964 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
965 add_region_brush_drag (item, event, clicked_regionview);
967 add_region_drag (item, event, clicked_regionview);
971 // if (!internal_editing() && (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE && !selection->regions.empty())) {
972 // _drags->add (new SelectionDrag (this, clicked_axisview->get_selection_rect (clicked_selection)->rect, SelectionDrag::SelectionMove));
975 _drags->start_grab (event);
978 case RegionViewNameHighlight:
979 case LeftFrameHandle:
980 case RightFrameHandle:
981 if (!clicked_regionview->region()->locked()) {
982 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
983 _drags->set (new TrimDrag (this, item, clicked_regionview, s.by_layer()), event);
990 /* rename happens on edit clicks */
991 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
992 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, s.by_layer()), event);
997 case ControlPointItem:
998 _drags->set (new ControlPointDrag (this, item), event);
1002 case AutomationLineItem:
1003 _drags->set (new LineDrag (this, item), event);
1008 if (internal_editing()) {
1009 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
1010 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
1014 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
1018 case AutomationTrackItem:
1020 TimeAxisView* parent = clicked_axisview->get_parent ();
1021 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (clicked_axisview);
1023 if (parent && dynamic_cast<MidiTimeAxisView*> (parent) && atv->show_regions ()) {
1025 RouteTimeAxisView* p = dynamic_cast<RouteTimeAxisView*> (parent);
1027 boost::shared_ptr<Playlist> pl = p->track()->playlist ();
1028 if (pl->n_regions() == 0) {
1029 /* Parent has no regions; create one so that we have somewhere to put automation */
1030 _drags->set (new RegionCreateDrag (this, item, parent), event);
1032 /* See if there's a region before the click that we can extend, and extend it if so */
1033 framepos_t const t = event_frame (event);
1034 boost::shared_ptr<Region> prev = pl->find_next_region (t, End, -1);
1036 _drags->set (new RegionCreateDrag (this, item, parent), event);
1038 prev->set_length (t - prev->position ());
1042 /* rubberband drag to select automation points */
1043 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
1050 if ( get_smart_mode() ) {
1051 /* we're in "smart" joined mode, and we've clicked on a Selection */
1052 double const y = event->button.y + vertical_adjustment.get_value() - canvas_timebars_vsize;
1053 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
1055 /* if we're over an automation track, start a drag of its data */
1056 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
1058 _drags->set (new AutomationRangeDrag (this, atv, selection->time), event, _cursors->up_down);
1061 /* if we're over a track and a region, and in the `object' part of a region,
1062 put a selection around the region and drag both
1064 /* RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1065 if (rtv && _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
1066 boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track> (rtv->route ());
1068 boost::shared_ptr<Playlist> pl = t->playlist ();
1071 boost::shared_ptr<Region> r = pl->top_region_at (event_frame (event));
1073 RegionView* rv = rtv->view()->find_view (r);
1074 clicked_selection = select_range (rv->region()->position(),
1075 rv->region()->last_frame()+1);
1076 _drags->add (new SelectionDrag (this, item, SelectionDrag::SelectionMove));
1077 list<RegionView*> rvs;
1079 _drags->add (new RegionMoveDrag (this, item, rv, rvs, false, false));
1080 _drags->start_grab (event);
1092 case ImageFrameHandleStartItem:
1093 imageframe_start_handle_op(item, event) ;
1096 case ImageFrameHandleEndItem:
1097 imageframe_end_handle_op(item, event) ;
1100 case MarkerViewHandleStartItem:
1101 markerview_item_start_handle_op(item, event) ;
1104 case MarkerViewHandleEndItem:
1105 markerview_item_end_handle_op(item, event) ;
1108 case MarkerViewItem:
1109 start_markerview_grab(item, event) ;
1111 case ImageFrameItem:
1112 start_imageframe_grab(item, event) ;
1128 switch (item_type) {
1130 _drags->set (new LineDrag (this, item), event);
1133 case ControlPointItem:
1134 _drags->set (new ControlPointDrag (this, item), event);
1140 AudioRegionView* arv = dynamic_cast<AudioRegionView *> (clicked_regionview);
1142 _drags->set (new AutomationRangeDrag (this, arv, selection->time), event, _cursors->up_down);
1143 _drags->start_grab (event);
1149 case AutomationLineItem:
1150 _drags->set (new LineDrag (this, item), event);
1160 if (event->type == GDK_BUTTON_PRESS) {
1161 _drags->set (new MouseZoomDrag (this, item), event);
1168 if (internal_editing() && item_type == NoteItem) {
1169 /* drag notes if we're in internal edit mode */
1170 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
1172 } else if (clicked_regionview) {
1174 _drags->set (new TimeFXDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1180 _drags->set (new ScrubDrag (this, item), event);
1181 scrub_reversals = 0;
1182 scrub_reverse_distance = 0;
1183 last_scrub_x = event->button.x;
1184 scrubbing_direction = 0;
1185 set_canvas_cursor (_cursors->transparent);
1197 Editor::button_press_handler_2 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1199 Editing::MouseMode const eff = effective_mouse_mode ();
1202 switch (item_type) {
1204 if (internal_editing ()) {
1205 /* no region drags in internal edit mode */
1209 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
1210 add_region_copy_drag (item, event, clicked_regionview);
1212 add_region_drag (item, event, clicked_regionview);
1214 _drags->start_grab (event);
1217 case ControlPointItem:
1218 _drags->set (new ControlPointDrag (this, item), event);
1226 switch (item_type) {
1227 case RegionViewNameHighlight:
1228 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1232 case LeftFrameHandle:
1233 case RightFrameHandle:
1234 if (!internal_editing ()) {
1235 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1240 case RegionViewName:
1241 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer()), event);
1255 /* relax till release */
1261 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1262 temporal_zoom_to_frame (false, event_frame (event));
1264 temporal_zoom_to_frame (true, event_frame(event));
1277 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1279 if (event->type != GDK_BUTTON_PRESS) {
1283 Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->track_canvas->get_window();
1285 if (canvas_window) {
1286 Glib::RefPtr<const Gdk::Window> pointer_window;
1289 Gdk::ModifierType mask;
1291 pointer_window = canvas_window->get_pointer (x, y, mask);
1293 if (pointer_window == track_canvas->get_bin_window()) {
1294 track_canvas->window_to_world (x, y, wx, wy);
1298 pre_press_cursor = current_canvas_cursor;
1300 track_canvas->grab_focus();
1302 if (_session && _session->actively_recording()) {
1306 if (internal_editing()) {
1307 bool leave_internal_edit_mode = false;
1309 switch (item_type) {
1314 if (!dynamic_cast<MidiRegionView*> (clicked_regionview) && !dynamic_cast<AutomationRegionView*> (clicked_regionview)) {
1315 leave_internal_edit_mode = true;
1319 case PlayheadCursorItem:
1321 case TempoMarkerItem:
1322 case MeterMarkerItem:
1326 case RangeMarkerBarItem:
1327 case CdMarkerBarItem:
1328 case TransportMarkerBarItem:
1329 /* button press on these events never does anything to
1330 change the editing mode.
1335 leave_internal_edit_mode = true;
1342 if (leave_internal_edit_mode) {
1343 ActionManager::do_action ("MouseMode", "toggle-internal-edit");
1347 button_selection (item, event, item_type);
1349 if (!_drags->active () &&
1350 (Keyboard::is_delete_event (&event->button) ||
1351 Keyboard::is_context_menu_event (&event->button) ||
1352 Keyboard::is_edit_event (&event->button))) {
1354 /* handled by button release */
1358 //not rolling, range mode click + join_play_range : locate the PH here
1359 if ( !_drags->active () && !_session->transport_rolling() && ( effective_mouse_mode() == MouseRange ) && Config->get_always_play_range() ) {
1360 framepos_t where = event_frame (event, 0, 0);
1362 _session->request_locate (where, false);
1365 switch (event->button.button) {
1367 return button_press_handler_1 (item, event, item_type);
1371 return button_press_handler_2 (item, event, item_type);
1378 return button_press_dispatch (&event->button);
1387 Editor::button_press_dispatch (GdkEventButton* ev)
1389 /* this function is intended only for buttons 4 and above.
1392 Gtkmm2ext::MouseButton b (ev->state, ev->button);
1393 return button_bindings->activate (b, Gtkmm2ext::Bindings::Press);
1397 Editor::button_release_dispatch (GdkEventButton* ev)
1399 /* this function is intended only for buttons 4 and above.
1402 Gtkmm2ext::MouseButton b (ev->state, ev->button);
1403 return button_bindings->activate (b, Gtkmm2ext::Bindings::Release);
1407 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1409 framepos_t where = event_frame (event, 0, 0);
1410 AutomationTimeAxisView* atv = 0;
1412 if (pre_press_cursor) {
1413 set_canvas_cursor (pre_press_cursor);
1414 pre_press_cursor = 0;
1417 /* no action if we're recording */
1419 if (_session && _session->actively_recording()) {
1423 /* see if we're finishing a drag */
1425 bool were_dragging = false;
1426 if (_drags->active ()) {
1427 bool const r = _drags->end_grab (event);
1429 /* grab dragged, so do nothing else */
1433 were_dragging = true;
1436 update_region_layering_order_editor ();
1438 /* edit events get handled here */
1440 if (!_drags->active () && Keyboard::is_edit_event (&event->button)) {
1441 switch (item_type) {
1443 show_region_properties ();
1446 case TempoMarkerItem:
1447 edit_tempo_marker (item);
1450 case MeterMarkerItem:
1451 edit_meter_marker (item);
1454 case RegionViewName:
1455 if (clicked_regionview->name_active()) {
1456 return mouse_rename_region (item, event);
1460 case ControlPointItem:
1461 edit_control_point (item);
1466 ArdourCanvas::CanvasNoteEvent* e = dynamic_cast<ArdourCanvas::CanvasNoteEvent*> (item);
1468 edit_notes (e->region_view().selection ());
1478 /* context menu events get handled here */
1479 if (Keyboard::is_context_menu_event (&event->button)) {
1481 context_click_event = *event;
1483 if (!_drags->active ()) {
1485 /* no matter which button pops up the context menu, tell the menu
1486 widget to use button 1 to drive menu selection.
1489 switch (item_type) {
1491 case FadeInHandleItem:
1493 case FadeOutHandleItem:
1494 popup_fade_context_menu (1, event->button.time, item, item_type);
1497 case StartCrossFadeItem:
1498 popup_xfade_in_context_menu (1, event->button.time, item, item_type);
1501 case EndCrossFadeItem:
1502 popup_xfade_out_context_menu (1, event->button.time, item, item_type);
1506 popup_track_context_menu (1, event->button.time, item_type, false);
1510 case RegionViewNameHighlight:
1511 case LeftFrameHandle:
1512 case RightFrameHandle:
1513 case RegionViewName:
1514 popup_track_context_menu (1, event->button.time, item_type, false);
1518 popup_track_context_menu (1, event->button.time, item_type, true);
1521 case AutomationTrackItem:
1522 popup_track_context_menu (1, event->button.time, item_type, false);
1526 case RangeMarkerBarItem:
1527 case TransportMarkerBarItem:
1528 case CdMarkerBarItem:
1531 popup_ruler_menu (where, item_type);
1535 marker_context_menu (&event->button, item);
1538 case TempoMarkerItem:
1539 tempo_or_meter_marker_context_menu (&event->button, item);
1542 case MeterMarkerItem:
1543 tempo_or_meter_marker_context_menu (&event->button, item);
1546 case CrossfadeViewItem:
1547 popup_track_context_menu (1, event->button.time, item_type, false);
1550 case ControlPointItem:
1551 popup_control_point_context_menu (item, event);
1555 case ImageFrameItem:
1556 popup_imageframe_edit_menu(1, event->button.time, item, true) ;
1558 case ImageFrameTimeAxisItem:
1559 popup_imageframe_edit_menu(1, event->button.time, item, false) ;
1561 case MarkerViewItem:
1562 popup_marker_time_axis_edit_menu(1, event->button.time, item, true) ;
1564 case MarkerTimeAxisItem:
1565 popup_marker_time_axis_edit_menu(1, event->button.time, item, false) ;
1577 /* delete events get handled here */
1579 Editing::MouseMode const eff = effective_mouse_mode ();
1581 if (!_drags->active () && Keyboard::is_delete_event (&event->button)) {
1583 switch (item_type) {
1584 case TempoMarkerItem:
1585 remove_tempo_marker (item);
1588 case MeterMarkerItem:
1589 remove_meter_marker (item);
1593 remove_marker (*item, event);
1597 if (eff == MouseObject) {
1598 remove_clicked_region ();
1602 case ControlPointItem:
1603 remove_control_point (item);
1607 remove_midi_note (item, event);
1616 switch (event->button.button) {
1619 switch (item_type) {
1620 /* see comments in button_press_handler */
1621 case PlayheadCursorItem:
1624 case AutomationLineItem:
1625 case StartSelectionTrimItem:
1626 case EndSelectionTrimItem:
1630 if (!_dragging_playhead) {
1631 snap_to_with_modifier (where, event, 0, true);
1632 mouse_add_new_marker (where);
1636 case CdMarkerBarItem:
1637 if (!_dragging_playhead) {
1638 // if we get here then a dragged range wasn't done
1639 snap_to_with_modifier (where, event, 0, true);
1640 mouse_add_new_marker (where, true);
1645 if (!_dragging_playhead) {
1646 snap_to_with_modifier (where, event);
1647 mouse_add_new_tempo_event (where);
1652 if (!_dragging_playhead) {
1653 mouse_add_new_meter_event (pixel_to_frame (event->button.x));
1664 switch (item_type) {
1665 case AutomationTrackItem:
1666 atv = dynamic_cast<AutomationTimeAxisView*>(clicked_axisview);
1668 atv->add_automation_event (event, where, event->button.y);
1678 switch (item_type) {
1681 /* check that we didn't drag before releasing, since
1682 its really annoying to create new control
1683 points when doing this.
1685 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (clicked_regionview);
1686 if (!were_dragging && arv) {
1687 arv->add_gain_point_event (item, event);
1693 case AutomationTrackItem:
1694 dynamic_cast<AutomationTimeAxisView*>(clicked_axisview)->
1695 add_automation_event (event, where, event->button.y);
1704 set_canvas_cursor (current_canvas_cursor);
1705 if (scrubbing_direction == 0) {
1706 /* no drag, just a click */
1707 switch (item_type) {
1709 play_selected_region ();
1715 /* make sure we stop */
1716 _session->request_transport_speed (0.0);
1725 /* do any (de)selection operations that should occur on button release */
1726 button_selection (item, event, item_type);
1735 switch (item_type) {
1737 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1739 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier|Keyboard::SecondaryModifier))) {
1742 // Button2 click is unused
1757 // x_style_paste (where, 1.0);
1778 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1785 switch (item_type) {
1786 case ControlPointItem:
1787 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1788 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1789 cp->set_visible (true);
1793 at_y = cp->get_y ();
1794 cp->i2w (at_x, at_y);
1798 fraction = 1.0 - (cp->get_y() / cp->line().height());
1800 if (is_drawable() && !_drags->active ()) {
1801 set_canvas_cursor (_cursors->fader);
1804 _verbose_cursor->set (cp->line().get_verbose_cursor_string (fraction), at_x, at_y);
1805 _verbose_cursor->show ();
1810 if (mouse_mode == MouseGain) {
1811 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1813 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredGainLine.get();
1814 if (is_drawable()) {
1815 set_canvas_cursor (_cursors->fader);
1820 case AutomationLineItem:
1821 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1822 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1824 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredAutomationLine.get();
1826 if (is_drawable()) {
1827 set_canvas_cursor (_cursors->fader);
1832 case RegionViewNameHighlight:
1833 if (is_drawable() && effective_mouse_mode() == MouseObject && entered_regionview) {
1834 set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1835 _over_region_trim_target = true;
1839 case LeftFrameHandle:
1840 case RightFrameHandle:
1841 if (is_drawable() && effective_mouse_mode() == MouseObject && !internal_editing() && entered_regionview) {
1842 set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1846 case StartSelectionTrimItem:
1848 case ImageFrameHandleStartItem:
1849 case MarkerViewHandleStartItem:
1851 if (is_drawable()) {
1852 set_canvas_cursor (_cursors->left_side_trim);
1855 case EndSelectionTrimItem:
1857 case ImageFrameHandleEndItem:
1858 case MarkerViewHandleEndItem:
1860 if (is_drawable()) {
1861 set_canvas_cursor (_cursors->right_side_trim);
1865 case PlayheadCursorItem:
1866 if (is_drawable()) {
1867 switch (_edit_point) {
1869 set_canvas_cursor (_cursors->grabber_edit_point);
1872 set_canvas_cursor (_cursors->grabber);
1878 case RegionViewName:
1880 /* when the name is not an active item, the entire name highlight is for trimming */
1882 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1883 if (mouse_mode == MouseObject && is_drawable()) {
1884 set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1885 _over_region_trim_target = true;
1891 case AutomationTrackItem:
1892 if (is_drawable()) {
1893 Gdk::Cursor *cursor;
1894 switch (mouse_mode) {
1896 cursor = _cursors->selector;
1899 cursor = _cursors->zoom_in;
1902 cursor = _cursors->cross_hair;
1906 set_canvas_cursor (cursor);
1908 AutomationTimeAxisView* atv;
1909 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1910 clear_entered_track = false;
1911 set_entered_track (atv);
1917 case RangeMarkerBarItem:
1918 case TransportMarkerBarItem:
1919 case CdMarkerBarItem:
1922 if (is_drawable()) {
1923 set_canvas_cursor (_cursors->timebar);
1928 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1931 entered_marker = marker;
1932 marker->set_color_rgba (ARDOUR_UI::config()->canvasvar_EnteredMarker.get());
1934 case MeterMarkerItem:
1935 case TempoMarkerItem:
1936 if (is_drawable()) {
1937 set_canvas_cursor (_cursors->timebar);
1941 case FadeInHandleItem:
1942 if (mouse_mode == MouseObject && !internal_editing()) {
1943 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1945 rect->property_fill_color_rgba() = 0xBBBBBBAA;
1947 set_canvas_cursor (_cursors->fade_in);
1951 case FadeOutHandleItem:
1952 if (mouse_mode == MouseObject && !internal_editing()) {
1953 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1955 rect->property_fill_color_rgba() = 0xBBBBBBAA;
1957 set_canvas_cursor (_cursors->fade_out);
1960 case FeatureLineItem:
1962 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1963 line->property_fill_color_rgba() = 0xFF0000FF;
1967 if ( get_smart_mode() ) {
1968 set_canvas_cursor ();
1976 /* second pass to handle entered track status in a comprehensible way.
1979 switch (item_type) {
1981 case AutomationLineItem:
1982 case ControlPointItem:
1983 /* these do not affect the current entered track state */
1984 clear_entered_track = false;
1987 case AutomationTrackItem:
1988 /* handled above already */
1992 set_entered_track (0);
2000 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent*, ItemType item_type)
2010 switch (item_type) {
2011 case ControlPointItem:
2012 cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
2013 if (cp->line().the_list()->interpolation() != AutomationList::Discrete) {
2014 if (cp->line().npoints() > 1 && !cp->get_selected()) {
2015 cp->set_visible (false);
2019 if (is_drawable()) {
2020 set_canvas_cursor (current_canvas_cursor);
2023 _verbose_cursor->hide ();
2026 case RegionViewNameHighlight:
2027 case LeftFrameHandle:
2028 case RightFrameHandle:
2029 case StartSelectionTrimItem:
2030 case EndSelectionTrimItem:
2031 case PlayheadCursorItem:
2034 case ImageFrameHandleStartItem:
2035 case ImageFrameHandleEndItem:
2036 case MarkerViewHandleStartItem:
2037 case MarkerViewHandleEndItem:
2040 _over_region_trim_target = false;
2042 if (is_drawable()) {
2043 set_canvas_cursor (current_canvas_cursor);
2048 case AutomationLineItem:
2049 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
2051 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
2053 line->property_fill_color_rgba() = al->get_line_color();
2055 if (is_drawable()) {
2056 set_canvas_cursor (current_canvas_cursor);
2060 case RegionViewName:
2061 /* see enter_handler() for notes */
2062 _over_region_trim_target = false;
2064 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
2065 if (is_drawable() && mouse_mode == MouseObject) {
2066 set_canvas_cursor (current_canvas_cursor);
2071 case RangeMarkerBarItem:
2072 case TransportMarkerBarItem:
2073 case CdMarkerBarItem:
2077 if (is_drawable()) {
2078 set_canvas_cursor (current_canvas_cursor);
2083 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2087 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
2088 location_flags_changed (loc, this);
2091 case MeterMarkerItem:
2092 case TempoMarkerItem:
2094 if (is_drawable()) {
2095 set_canvas_cursor (current_canvas_cursor);
2100 case FadeInHandleItem:
2101 case FadeOutHandleItem:
2102 rv = static_cast<RegionView*>(item->get_data ("regionview"));
2104 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
2106 rect->property_fill_color_rgba() = rv->get_fill_color();
2109 set_canvas_cursor (current_canvas_cursor);
2112 case AutomationTrackItem:
2113 if (is_drawable()) {
2114 set_canvas_cursor (current_canvas_cursor);
2115 clear_entered_track = true;
2116 Glib::signal_idle().connect (sigc::mem_fun(*this, &Editor::left_automation_track));
2119 case FeatureLineItem:
2121 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
2122 line->property_fill_color_rgba() = (guint) ARDOUR_UI::config()->canvasvar_ZeroLine.get();;
2134 Editor::left_automation_track ()
2136 if (clear_entered_track) {
2137 set_entered_track (0);
2138 clear_entered_track = false;
2144 Editor::scrub (framepos_t frame, double current_x)
2148 if (scrubbing_direction == 0) {
2150 _session->request_locate (frame, false);
2151 _session->request_transport_speed (0.1);
2152 scrubbing_direction = 1;
2156 if (last_scrub_x > current_x) {
2158 /* pointer moved to the left */
2160 if (scrubbing_direction > 0) {
2162 /* we reversed direction to go backwards */
2165 scrub_reverse_distance += (int) (last_scrub_x - current_x);
2169 /* still moving to the left (backwards) */
2171 scrub_reversals = 0;
2172 scrub_reverse_distance = 0;
2174 delta = 0.01 * (last_scrub_x - current_x);
2175 _session->request_transport_speed_nonzero (_session->transport_speed() - delta);
2179 /* pointer moved to the right */
2181 if (scrubbing_direction < 0) {
2182 /* we reversed direction to go forward */
2185 scrub_reverse_distance += (int) (current_x - last_scrub_x);
2188 /* still moving to the right */
2190 scrub_reversals = 0;
2191 scrub_reverse_distance = 0;
2193 delta = 0.01 * (current_x - last_scrub_x);
2194 _session->request_transport_speed_nonzero (_session->transport_speed() + delta);
2198 /* if there have been more than 2 opposite motion moves detected, or one that moves
2199 back more than 10 pixels, reverse direction
2202 if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
2204 if (scrubbing_direction > 0) {
2205 /* was forwards, go backwards */
2206 _session->request_transport_speed (-0.1);
2207 scrubbing_direction = -1;
2209 /* was backwards, go forwards */
2210 _session->request_transport_speed (0.1);
2211 scrubbing_direction = 1;
2214 scrub_reverse_distance = 0;
2215 scrub_reversals = 0;
2219 last_scrub_x = current_x;
2223 Editor::motion_handler (ArdourCanvas::Item* /*item*/, GdkEvent* event, bool from_autoscroll)
2225 _last_motion_y = event->motion.y;
2227 if (event->motion.is_hint) {
2230 /* We call this so that MOTION_NOTIFY events continue to be
2231 delivered to the canvas. We need to do this because we set
2232 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
2233 the density of the events, at the expense of a round-trip
2234 to the server. Given that this will mostly occur on cases
2235 where DISPLAY = :0.0, and given the cost of what the motion
2236 event might do, its a good tradeoff.
2239 track_canvas->get_pointer (x, y);
2242 if (current_stepping_trackview) {
2243 /* don't keep the persistent stepped trackview if the mouse moves */
2244 current_stepping_trackview = 0;
2245 step_timeout.disconnect ();
2248 if (_session && _session->actively_recording()) {
2249 /* Sorry. no dragging stuff around while we record */
2253 JoinObjectRangeState const old = _join_object_range_state;
2254 update_join_object_range_location (event->motion.x, event->motion.y);
2255 if (_join_object_range_state != old) {
2256 set_canvas_cursor ();
2259 if (_over_region_trim_target) {
2260 set_canvas_cursor_for_region_view (event->motion.x, entered_regionview);
2263 bool handled = false;
2264 if (_drags->active ()) {
2265 handled = _drags->motion_handler (event, from_autoscroll);
2272 track_canvas_motion (event);
2277 Editor::can_remove_control_point (ArdourCanvas::Item* item)
2279 ControlPoint* control_point;
2281 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2282 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2286 AutomationLine& line = control_point->line ();
2287 if (dynamic_cast<AudioRegionGainLine*> (&line)) {
2288 /* we shouldn't remove the first or last gain point in region gain lines */
2289 if (line.is_last_point(*control_point) || line.is_first_point(*control_point)) {
2298 Editor::remove_control_point (ArdourCanvas::Item* item)
2300 if (!can_remove_control_point (item)) {
2304 ControlPoint* control_point;
2306 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2307 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2311 control_point->line().remove_point (*control_point);
2315 Editor::edit_control_point (ArdourCanvas::Item* item)
2317 ControlPoint* p = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"));
2320 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2324 ControlPointDialog d (p);
2325 d.set_position (Gtk::WIN_POS_MOUSE);
2328 if (d.run () != RESPONSE_ACCEPT) {
2332 p->line().modify_point_y (*p, d.get_y_fraction ());
2336 Editor::edit_notes (MidiRegionView::Selection const & s)
2342 EditNoteDialog d (&(*s.begin())->region_view(), s);
2343 d.set_position (Gtk::WIN_POS_MOUSE);
2351 Editor::visible_order_range (int* low, int* high) const
2353 *low = TimeAxisView::max_order ();
2356 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
2358 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
2360 if (!rtv->hidden()) {
2362 if (*high < rtv->order()) {
2363 *high = rtv->order ();
2366 if (*low > rtv->order()) {
2367 *low = rtv->order ();
2374 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
2376 /* Either add to or set the set the region selection, unless
2377 this is an alignment click (control used)
2380 if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
2381 TimeAxisView* tv = &rv.get_time_axis_view();
2382 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
2384 if (rtv && rtv->is_track()) {
2385 speed = rtv->track()->speed();
2388 framepos_t where = get_preferred_edit_position();
2392 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
2394 align_region (rv.region(), SyncPoint, (framepos_t) (where * speed));
2396 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
2398 align_region (rv.region(), End, (framepos_t) (where * speed));
2402 align_region (rv.region(), Start, (framepos_t) (where * speed));
2409 Editor::collect_new_region_view (RegionView* rv)
2411 latest_regionviews.push_back (rv);
2415 Editor::collect_and_select_new_region_view (RegionView* rv)
2418 latest_regionviews.push_back (rv);
2422 Editor::cancel_selection ()
2424 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2425 (*i)->hide_selection ();
2428 selection->clear ();
2429 clicked_selection = 0;
2433 Editor::cancel_time_selection ()
2435 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2436 (*i)->hide_selection ();
2438 selection->time.clear ();
2439 clicked_selection = 0;
2443 Editor::point_trim (GdkEvent* event, framepos_t new_bound)
2445 RegionView* rv = clicked_regionview;
2447 /* Choose action dependant on which button was pressed */
2448 switch (event->button.button) {
2450 begin_reversible_command (_("start point trim"));
2452 if (selection->selected (rv)) {
2453 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
2454 i != selection->regions.by_layer().end(); ++i)
2456 if (!(*i)->region()->locked()) {
2457 (*i)->region()->clear_changes ();
2458 (*i)->region()->trim_front (new_bound);
2459 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2464 if (!rv->region()->locked()) {
2465 rv->region()->clear_changes ();
2466 rv->region()->trim_front (new_bound);
2467 _session->add_command(new StatefulDiffCommand (rv->region()));
2471 commit_reversible_command();
2475 begin_reversible_command (_("End point trim"));
2477 if (selection->selected (rv)) {
2479 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
2481 if (!(*i)->region()->locked()) {
2482 (*i)->region()->clear_changes();
2483 (*i)->region()->trim_end (new_bound);
2484 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2490 if (!rv->region()->locked()) {
2491 rv->region()->clear_changes ();
2492 rv->region()->trim_end (new_bound);
2493 _session->add_command (new StatefulDiffCommand (rv->region()));
2497 commit_reversible_command();
2506 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* /*event*/)
2511 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2512 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2516 Location* location = find_location_from_marker (marker, is_start);
2517 location->set_hidden (true, this);
2522 Editor::reposition_zoom_rect (framepos_t start, framepos_t end)
2524 double x1 = frame_to_pixel (start);
2525 double x2 = frame_to_pixel (end);
2526 double y2 = full_canvas_height - 1.0;
2528 zoom_rect->property_x1() = x1;
2529 zoom_rect->property_y1() = 1.0;
2530 zoom_rect->property_x2() = x2;
2531 zoom_rect->property_y2() = y2;
2536 Editor::mouse_rename_region (ArdourCanvas::Item* /*item*/, GdkEvent* /*event*/)
2538 using namespace Gtkmm2ext;
2540 ArdourPrompter prompter (false);
2542 prompter.set_prompt (_("Name for region:"));
2543 prompter.set_initial_text (clicked_regionview->region()->name());
2544 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
2545 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
2546 prompter.show_all ();
2547 switch (prompter.run ()) {
2548 case Gtk::RESPONSE_ACCEPT:
2550 prompter.get_result(str);
2552 clicked_regionview->region()->set_name (str);
2561 Editor::mouse_brush_insert_region (RegionView* rv, framepos_t pos)
2563 /* no brushing without a useful snap setting */
2565 switch (_snap_mode) {
2567 return; /* can't work because it allows region to be placed anywhere */
2572 switch (_snap_type) {
2580 /* don't brush a copy over the original */
2582 if (pos == rv->region()->position()) {
2586 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
2588 if (rtv == 0 || !rtv->is_track()) {
2592 boost::shared_ptr<Playlist> playlist = rtv->playlist();
2593 double speed = rtv->track()->speed();
2595 playlist->clear_changes ();
2596 boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region(), true));
2597 playlist->add_region (new_region, (framepos_t) (pos * speed));
2598 _session->add_command (new StatefulDiffCommand (playlist));
2600 // playlist is frozen, so we have to update manually XXX this is disgusting
2602 playlist->RegionAdded (new_region); /* EMIT SIGNAL */
2606 Editor::track_height_step_timeout ()
2608 if (get_microseconds() - last_track_height_step_timestamp < 250000) {
2609 current_stepping_trackview = 0;
2616 Editor::add_region_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2618 assert (region_view);
2620 if (!region_view->region()->playlist()) {
2624 _region_motion_group->raise_to_top ();
2626 if (Config->get_edit_mode() == Splice) {
2627 _drags->add (new RegionSpliceDrag (this, item, region_view, selection->regions.by_layer()));
2629 RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id);
2630 _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), false, false));
2633 /* sync the canvas to what we think is its current state */
2634 update_canvas_now();
2638 Editor::add_region_copy_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2640 assert (region_view);
2642 if (!region_view->region()->playlist()) {
2646 _region_motion_group->raise_to_top ();
2648 RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id);
2649 _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), false, true));
2653 Editor::add_region_brush_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2655 assert (region_view);
2657 if (!region_view->region()->playlist()) {
2661 if (Config->get_edit_mode() == Splice) {
2665 RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id);
2666 _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), true, false));
2668 begin_reversible_command (Operations::drag_region_brush);
2671 /** Start a grab where a time range is selected, track(s) are selected, and the
2672 * user clicks and drags a region with a modifier in order to create a new region containing
2673 * the section of the clicked region that lies within the time range.
2676 Editor::start_selection_grab (ArdourCanvas::Item* /*item*/, GdkEvent* event)
2678 if (clicked_regionview == 0) {
2682 /* lets try to create new Region for the selection */
2684 vector<boost::shared_ptr<Region> > new_regions;
2685 create_region_from_selection (new_regions);
2687 if (new_regions.empty()) {
2691 /* XXX fix me one day to use all new regions */
2693 boost::shared_ptr<Region> region (new_regions.front());
2695 /* add it to the current stream/playlist.
2697 tricky: the streamview for the track will add a new regionview. we will
2698 catch the signal it sends when it creates the regionview to
2699 set the regionview we want to then drag.
2702 latest_regionviews.clear();
2703 sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
2705 /* A selection grab currently creates two undo/redo operations, one for
2706 creating the new region and another for moving it.
2709 begin_reversible_command (Operations::selection_grab);
2711 boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
2713 playlist->clear_changes ();
2714 clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
2715 _session->add_command(new StatefulDiffCommand (playlist));
2717 commit_reversible_command ();
2721 if (latest_regionviews.empty()) {
2722 /* something went wrong */
2726 /* we need to deselect all other regionviews, and select this one
2727 i'm ignoring undo stuff, because the region creation will take care of it
2729 selection->set (latest_regionviews);
2731 _drags->set (new RegionMoveDrag (this, latest_regionviews.front()->get_canvas_group(), latest_regionviews.front(), latest_regionviews, false, false), event);
2737 if (_drags->active ()) {
2740 selection->clear ();
2745 Editor::set_internal_edit (bool yn)
2747 if (_internal_editing == yn) {
2751 _internal_editing = yn;
2754 pre_internal_mouse_mode = mouse_mode;
2755 pre_internal_snap_type = _snap_type;
2756 pre_internal_snap_mode = _snap_mode;
2758 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2759 (*i)->enter_internal_edit_mode ();
2762 set_snap_to (internal_snap_type);
2763 set_snap_mode (internal_snap_mode);
2767 internal_snap_mode = _snap_mode;
2768 internal_snap_type = _snap_type;
2770 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2771 (*i)->leave_internal_edit_mode ();
2774 if (mouse_mode == MouseDraw && pre_internal_mouse_mode != MouseDraw) {
2775 /* we were drawing .. flip back to something sensible */
2776 set_mouse_mode (pre_internal_mouse_mode);
2779 set_snap_to (pre_internal_snap_type);
2780 set_snap_mode (pre_internal_snap_mode);
2783 set_canvas_cursor ();
2786 /** Update _join_object_range_state which indicate whether we are over the top or bottom half of a region view,
2787 * used by the `join object/range' tool mode.
2790 Editor::update_join_object_range_location (double /*x*/, double y)
2792 /* XXX: actually, this decides based on whether the mouse is in the top
2793 or bottom half of a the waveform part RouteTimeAxisView;
2795 Note that entered_{track,regionview} is not always setup (e.g. if
2796 the mouse is over a TimeSelection), and to get a Region
2797 that we're over requires searching the playlist.
2800 if ( !get_smart_mode() ) {
2801 _join_object_range_state = JOIN_OBJECT_RANGE_NONE;
2805 if (mouse_mode == MouseObject) {
2806 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2807 } else if (mouse_mode == MouseRange) {
2808 _join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
2811 /* XXX: maybe we should make entered_track work in all cases, rather than resorting to this */
2812 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y + vertical_adjustment.get_value() - canvas_timebars_vsize);
2816 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
2821 rtv->canvas_display()->w2i (cx, cy);
2823 double const c = cy / (rtv->view()->child_height() - TimeAxisViewItem::NAME_HIGHLIGHT_SIZE);
2825 _join_object_range_state = c <= 0.5 ? JOIN_OBJECT_RANGE_RANGE : JOIN_OBJECT_RANGE_OBJECT;
2831 Editor::effective_mouse_mode () const
2833 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
2835 } else if (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE) {
2843 Editor::remove_midi_note (ArdourCanvas::Item* item, GdkEvent *)
2845 ArdourCanvas::CanvasNoteEvent* e = dynamic_cast<ArdourCanvas::CanvasNoteEvent*> (item);
2848 e->region_view().delete_note (e->note ());
2852 Editor::set_canvas_cursor_for_region_view (double x, RegionView* rv)
2856 ArdourCanvas::Group* g = rv->get_canvas_group ();
2857 ArdourCanvas::Group* p = g->get_parent_group ();
2859 /* Compute x in region view parent coordinates */
2863 double x1, x2, y1, y2;
2864 g->get_bounds (x1, y1, x2, y2);
2866 /* Halfway across the region */
2867 double const h = (x1 + x2) / 2;
2869 Trimmable::CanTrim ct = rv->region()->can_trim ();
2871 if (ct & Trimmable::FrontTrimEarlier) {
2872 set_canvas_cursor (_cursors->left_side_trim);
2874 set_canvas_cursor (_cursors->left_side_trim_right_only);
2877 if (ct & Trimmable::EndTrimLater) {
2878 set_canvas_cursor (_cursors->right_side_trim);
2880 set_canvas_cursor (_cursors->right_side_trim_left_only);
2885 /** Obtain the pointer position in world coordinates */
2887 Editor::get_pointer_position (double& x, double& y) const
2890 track_canvas->get_pointer (px, py);
2891 track_canvas->window_to_world (px, py, x, y);