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);
851 case LeftFrameHandle:
852 case RightFrameHandle:
853 if (!internal_editing() && !clicked_regionview->region()->locked()) {
854 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
855 _drags->set (new TrimDrag (this, item, clicked_regionview, s.by_layer()), event);
861 if (!internal_editing()) {
862 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::RangeSelectModifier)) {
863 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionExtend), event);
865 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
875 if (internal_editing()) {
876 /* trim notes if we're in internal edit mode and near the ends of the note */
877 ArdourCanvas::CanvasNote* cn = dynamic_cast<ArdourCanvas::CanvasNote*> (item);
878 if (cn && cn->big_enough_to_trim() && cn->mouse_near_ends()) {
879 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
881 _drags->set (new NoteDrag (this, item), event);
895 if (internal_editing()) {
896 ArdourCanvas::CanvasNoteEvent* cn = dynamic_cast<ArdourCanvas::CanvasNoteEvent*> (item);
897 if (cn->mouse_near_ends()) {
898 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
900 _drags->set (new NoteDrag (this, item), event);
910 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)) &&
911 event->type == GDK_BUTTON_PRESS) {
913 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
915 } else if (event->type == GDK_BUTTON_PRESS) {
918 case FadeInHandleItem:
920 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
921 _drags->set (new FadeInDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), s), event, _cursors->fade_in);
925 case FadeOutHandleItem:
927 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
928 _drags->set (new FadeOutDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), s), event, _cursors->fade_out);
932 case StartCrossFadeItem:
933 case EndCrossFadeItem:
934 /* we might allow user to grab inside the fade to trim a region with preserve_fade_anchor. for not this is not fully implemented */
935 // if (!clicked_regionview->region()->locked()) {
936 // RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
937 // _drags->set (new TrimDrag (this, item, clicked_regionview, s.by_layer(), true), event);
942 case FeatureLineItem:
944 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier)) {
945 remove_transient(item);
949 _drags->set (new FeatureLineDrag (this, item), event);
955 if (dynamic_cast<AutomationRegionView*> (clicked_regionview)) {
956 /* click on an automation region view; do nothing here and let the ARV's signal handler
962 if (internal_editing ()) {
963 if (event->type == GDK_2BUTTON_PRESS && event->button.button == 1) {
964 Glib::RefPtr<Action> act = ActionManager::get_action (X_("MouseMode"), X_("toggle-internal-edit"));
970 /* click on a normal region view */
971 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
972 add_region_copy_drag (item, event, clicked_regionview);
973 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
974 add_region_brush_drag (item, event, clicked_regionview);
976 add_region_drag (item, event, clicked_regionview);
980 // if (!internal_editing() && (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE && !selection->regions.empty())) {
981 // _drags->add (new SelectionDrag (this, clicked_axisview->get_selection_rect (clicked_selection)->rect, SelectionDrag::SelectionMove));
984 _drags->start_grab (event);
987 case RegionViewNameHighlight:
988 case LeftFrameHandle:
989 case RightFrameHandle:
990 if (!clicked_regionview->region()->locked()) {
991 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
992 _drags->set (new TrimDrag (this, item, clicked_regionview, s.by_layer()), event);
999 /* rename happens on edit clicks */
1000 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
1001 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, s.by_layer()), event);
1006 case ControlPointItem:
1007 _drags->set (new ControlPointDrag (this, item), event);
1011 case AutomationLineItem:
1012 _drags->set (new LineDrag (this, item), event);
1017 if (internal_editing()) {
1018 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
1019 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
1023 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
1027 case AutomationTrackItem:
1029 TimeAxisView* parent = clicked_axisview->get_parent ();
1030 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (clicked_axisview);
1032 if (parent && dynamic_cast<MidiTimeAxisView*> (parent) && atv->show_regions ()) {
1034 RouteTimeAxisView* p = dynamic_cast<RouteTimeAxisView*> (parent);
1036 boost::shared_ptr<Playlist> pl = p->track()->playlist ();
1037 if (pl->n_regions() == 0) {
1038 /* Parent has no regions; create one so that we have somewhere to put automation */
1039 _drags->set (new RegionCreateDrag (this, item, parent), event);
1041 /* See if there's a region before the click that we can extend, and extend it if so */
1042 framepos_t const t = event_frame (event);
1043 boost::shared_ptr<Region> prev = pl->find_next_region (t, End, -1);
1045 _drags->set (new RegionCreateDrag (this, item, parent), event);
1047 prev->set_length (t - prev->position ());
1051 /* rubberband drag to select automation points */
1052 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
1059 if ( get_smart_mode() ) {
1060 /* we're in "smart" joined mode, and we've clicked on a Selection */
1061 double const y = event->button.y + vertical_adjustment.get_value() - canvas_timebars_vsize;
1062 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
1064 /* if we're over an automation track, start a drag of its data */
1065 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
1067 _drags->set (new AutomationRangeDrag (this, atv, selection->time), event, _cursors->up_down);
1070 /* if we're over a track and a region, and in the `object' part of a region,
1071 put a selection around the region and drag both
1073 /* RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1074 if (rtv && _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
1075 boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track> (rtv->route ());
1077 boost::shared_ptr<Playlist> pl = t->playlist ();
1080 boost::shared_ptr<Region> r = pl->top_region_at (event_frame (event));
1082 RegionView* rv = rtv->view()->find_view (r);
1083 clicked_selection = select_range (rv->region()->position(),
1084 rv->region()->last_frame()+1);
1085 _drags->add (new SelectionDrag (this, item, SelectionDrag::SelectionMove));
1086 list<RegionView*> rvs;
1088 _drags->add (new RegionMoveDrag (this, item, rv, rvs, false, false));
1089 _drags->start_grab (event);
1101 case ImageFrameHandleStartItem:
1102 imageframe_start_handle_op(item, event) ;
1105 case ImageFrameHandleEndItem:
1106 imageframe_end_handle_op(item, event) ;
1109 case MarkerViewHandleStartItem:
1110 markerview_item_start_handle_op(item, event) ;
1113 case MarkerViewHandleEndItem:
1114 markerview_item_end_handle_op(item, event) ;
1117 case MarkerViewItem:
1118 start_markerview_grab(item, event) ;
1120 case ImageFrameItem:
1121 start_imageframe_grab(item, event) ;
1137 switch (item_type) {
1139 _drags->set (new LineDrag (this, item), event);
1142 case ControlPointItem:
1143 _drags->set (new ControlPointDrag (this, item), event);
1149 AudioRegionView* arv = dynamic_cast<AudioRegionView *> (clicked_regionview);
1151 _drags->set (new AutomationRangeDrag (this, arv, selection->time), event, _cursors->up_down);
1152 _drags->start_grab (event);
1158 case AutomationLineItem:
1159 _drags->set (new LineDrag (this, item), event);
1169 if (event->type == GDK_BUTTON_PRESS) {
1170 _drags->set (new MouseZoomDrag (this, item), event);
1177 if (internal_editing() && item_type == NoteItem) {
1178 /* drag notes if we're in internal edit mode */
1179 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
1181 } else if (clicked_regionview) {
1183 _drags->set (new TimeFXDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1189 _drags->set (new ScrubDrag (this, item), event);
1190 scrub_reversals = 0;
1191 scrub_reverse_distance = 0;
1192 last_scrub_x = event->button.x;
1193 scrubbing_direction = 0;
1194 set_canvas_cursor (_cursors->transparent);
1206 Editor::button_press_handler_2 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1208 Editing::MouseMode const eff = effective_mouse_mode ();
1211 switch (item_type) {
1213 if (internal_editing ()) {
1214 /* no region drags in internal edit mode */
1218 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
1219 add_region_copy_drag (item, event, clicked_regionview);
1221 add_region_drag (item, event, clicked_regionview);
1223 _drags->start_grab (event);
1226 case ControlPointItem:
1227 _drags->set (new ControlPointDrag (this, item), event);
1235 switch (item_type) {
1236 case RegionViewNameHighlight:
1237 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1241 case LeftFrameHandle:
1242 case RightFrameHandle:
1243 if (!internal_editing ()) {
1244 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1249 case RegionViewName:
1250 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer()), event);
1264 /* relax till release */
1270 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1271 temporal_zoom_to_frame (false, event_frame (event));
1273 temporal_zoom_to_frame (true, event_frame(event));
1286 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1288 if (event->type != GDK_BUTTON_PRESS) {
1292 Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->track_canvas->get_window();
1294 if (canvas_window) {
1295 Glib::RefPtr<const Gdk::Window> pointer_window;
1298 Gdk::ModifierType mask;
1300 pointer_window = canvas_window->get_pointer (x, y, mask);
1302 if (pointer_window == track_canvas->get_bin_window()) {
1303 track_canvas->window_to_world (x, y, wx, wy);
1307 pre_press_cursor = current_canvas_cursor;
1309 track_canvas->grab_focus();
1311 if (_session && _session->actively_recording()) {
1315 if (internal_editing()) {
1316 bool leave_internal_edit_mode = false;
1318 switch (item_type) {
1323 if (!dynamic_cast<MidiRegionView*> (clicked_regionview) && !dynamic_cast<AutomationRegionView*> (clicked_regionview)) {
1324 leave_internal_edit_mode = true;
1328 case PlayheadCursorItem:
1330 case TempoMarkerItem:
1331 case MeterMarkerItem:
1335 case RangeMarkerBarItem:
1336 case CdMarkerBarItem:
1337 case TransportMarkerBarItem:
1338 /* button press on these events never does anything to
1339 change the editing mode.
1344 leave_internal_edit_mode = true;
1351 if (leave_internal_edit_mode) {
1352 ActionManager::do_action ("MouseMode", "toggle-internal-edit");
1356 button_selection (item, event, item_type);
1358 if (!_drags->active () &&
1359 (Keyboard::is_delete_event (&event->button) ||
1360 Keyboard::is_context_menu_event (&event->button) ||
1361 Keyboard::is_edit_event (&event->button))) {
1363 /* handled by button release */
1367 //not rolling, range mode click + join_play_range : locate the PH here
1368 if ( !_drags->active () && !_session->transport_rolling() && ( effective_mouse_mode() == MouseRange ) && Config->get_always_play_range() ) {
1369 framepos_t where = event_frame (event, 0, 0);
1371 _session->request_locate (where, false);
1374 switch (event->button.button) {
1376 return button_press_handler_1 (item, event, item_type);
1380 return button_press_handler_2 (item, event, item_type);
1387 return button_press_dispatch (&event->button);
1396 Editor::button_press_dispatch (GdkEventButton* ev)
1398 /* this function is intended only for buttons 4 and above.
1401 Gtkmm2ext::MouseButton b (ev->state, ev->button);
1402 return button_bindings->activate (b, Gtkmm2ext::Bindings::Press);
1406 Editor::button_release_dispatch (GdkEventButton* ev)
1408 /* this function is intended only for buttons 4 and above.
1411 Gtkmm2ext::MouseButton b (ev->state, ev->button);
1412 return button_bindings->activate (b, Gtkmm2ext::Bindings::Release);
1416 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1418 framepos_t where = event_frame (event, 0, 0);
1419 AutomationTimeAxisView* atv = 0;
1421 if (pre_press_cursor) {
1422 set_canvas_cursor (pre_press_cursor);
1423 pre_press_cursor = 0;
1426 /* no action if we're recording */
1428 if (_session && _session->actively_recording()) {
1432 /* see if we're finishing a drag */
1434 bool were_dragging = false;
1435 if (_drags->active ()) {
1436 bool const r = _drags->end_grab (event);
1438 /* grab dragged, so do nothing else */
1442 were_dragging = true;
1445 update_region_layering_order_editor ();
1447 /* edit events get handled here */
1449 if (!_drags->active () && Keyboard::is_edit_event (&event->button)) {
1450 switch (item_type) {
1452 show_region_properties ();
1455 case TempoMarkerItem:
1456 edit_tempo_marker (item);
1459 case MeterMarkerItem:
1460 edit_meter_marker (item);
1463 case RegionViewName:
1464 if (clicked_regionview->name_active()) {
1465 return mouse_rename_region (item, event);
1469 case ControlPointItem:
1470 edit_control_point (item);
1475 ArdourCanvas::CanvasNoteEvent* e = dynamic_cast<ArdourCanvas::CanvasNoteEvent*> (item);
1477 edit_notes (e->region_view().selection ());
1487 /* context menu events get handled here */
1488 if (Keyboard::is_context_menu_event (&event->button)) {
1490 context_click_event = *event;
1492 if (!_drags->active ()) {
1494 /* no matter which button pops up the context menu, tell the menu
1495 widget to use button 1 to drive menu selection.
1498 switch (item_type) {
1500 case FadeInHandleItem:
1502 case FadeOutHandleItem:
1503 popup_fade_context_menu (1, event->button.time, item, item_type);
1506 case StartCrossFadeItem:
1507 popup_xfade_in_context_menu (1, event->button.time, item, item_type);
1510 case EndCrossFadeItem:
1511 popup_xfade_out_context_menu (1, event->button.time, item, item_type);
1515 popup_track_context_menu (1, event->button.time, item_type, false);
1519 case RegionViewNameHighlight:
1520 case LeftFrameHandle:
1521 case RightFrameHandle:
1522 case RegionViewName:
1523 popup_track_context_menu (1, event->button.time, item_type, false);
1527 popup_track_context_menu (1, event->button.time, item_type, true);
1530 case AutomationTrackItem:
1531 popup_track_context_menu (1, event->button.time, item_type, false);
1535 case RangeMarkerBarItem:
1536 case TransportMarkerBarItem:
1537 case CdMarkerBarItem:
1540 popup_ruler_menu (where, item_type);
1544 marker_context_menu (&event->button, item);
1547 case TempoMarkerItem:
1548 tempo_or_meter_marker_context_menu (&event->button, item);
1551 case MeterMarkerItem:
1552 tempo_or_meter_marker_context_menu (&event->button, item);
1555 case CrossfadeViewItem:
1556 popup_track_context_menu (1, event->button.time, item_type, false);
1559 case ControlPointItem:
1560 popup_control_point_context_menu (item, event);
1564 case ImageFrameItem:
1565 popup_imageframe_edit_menu(1, event->button.time, item, true) ;
1567 case ImageFrameTimeAxisItem:
1568 popup_imageframe_edit_menu(1, event->button.time, item, false) ;
1570 case MarkerViewItem:
1571 popup_marker_time_axis_edit_menu(1, event->button.time, item, true) ;
1573 case MarkerTimeAxisItem:
1574 popup_marker_time_axis_edit_menu(1, event->button.time, item, false) ;
1586 /* delete events get handled here */
1588 Editing::MouseMode const eff = effective_mouse_mode ();
1590 if (!_drags->active () && Keyboard::is_delete_event (&event->button)) {
1592 switch (item_type) {
1593 case TempoMarkerItem:
1594 remove_tempo_marker (item);
1597 case MeterMarkerItem:
1598 remove_meter_marker (item);
1602 remove_marker (*item, event);
1606 if (eff == MouseObject) {
1607 remove_clicked_region ();
1611 case ControlPointItem:
1612 remove_control_point (item);
1616 remove_midi_note (item, event);
1625 switch (event->button.button) {
1628 switch (item_type) {
1629 /* see comments in button_press_handler */
1630 case PlayheadCursorItem:
1633 case AutomationLineItem:
1634 case StartSelectionTrimItem:
1635 case EndSelectionTrimItem:
1639 if (!_dragging_playhead) {
1640 snap_to_with_modifier (where, event, 0, true);
1641 mouse_add_new_marker (where);
1645 case CdMarkerBarItem:
1646 if (!_dragging_playhead) {
1647 // if we get here then a dragged range wasn't done
1648 snap_to_with_modifier (where, event, 0, true);
1649 mouse_add_new_marker (where, true);
1654 if (!_dragging_playhead) {
1655 snap_to_with_modifier (where, event);
1656 mouse_add_new_tempo_event (where);
1661 if (!_dragging_playhead) {
1662 mouse_add_new_meter_event (pixel_to_frame (event->button.x));
1673 switch (item_type) {
1674 case AutomationTrackItem:
1675 atv = dynamic_cast<AutomationTimeAxisView*>(clicked_axisview);
1677 atv->add_automation_event (event, where, event->button.y);
1687 switch (item_type) {
1690 /* check that we didn't drag before releasing, since
1691 its really annoying to create new control
1692 points when doing this.
1694 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (clicked_regionview);
1695 if (!were_dragging && arv) {
1696 arv->add_gain_point_event (item, event);
1702 case AutomationTrackItem:
1703 dynamic_cast<AutomationTimeAxisView*>(clicked_axisview)->
1704 add_automation_event (event, where, event->button.y);
1713 set_canvas_cursor (current_canvas_cursor);
1714 if (scrubbing_direction == 0) {
1715 /* no drag, just a click */
1716 switch (item_type) {
1718 play_selected_region ();
1724 /* make sure we stop */
1725 _session->request_transport_speed (0.0);
1734 /* do any (de)selection operations that should occur on button release */
1735 button_selection (item, event, item_type);
1744 switch (item_type) {
1746 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1748 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier|Keyboard::SecondaryModifier))) {
1751 // Button2 click is unused
1766 // x_style_paste (where, 1.0);
1787 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1794 switch (item_type) {
1795 case ControlPointItem:
1796 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1797 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1798 cp->set_visible (true);
1802 at_y = cp->get_y ();
1803 cp->i2w (at_x, at_y);
1807 fraction = 1.0 - (cp->get_y() / cp->line().height());
1809 if (is_drawable() && !_drags->active ()) {
1810 set_canvas_cursor (_cursors->fader);
1813 _verbose_cursor->set (cp->line().get_verbose_cursor_string (fraction), at_x, at_y);
1814 _verbose_cursor->show ();
1819 if (mouse_mode == MouseGain) {
1820 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1822 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredGainLine.get();
1823 if (is_drawable()) {
1824 set_canvas_cursor (_cursors->fader);
1829 case AutomationLineItem:
1830 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1831 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1833 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredAutomationLine.get();
1835 if (is_drawable()) {
1836 set_canvas_cursor (_cursors->fader);
1841 case RegionViewNameHighlight:
1842 if (is_drawable() && effective_mouse_mode() == MouseObject && entered_regionview) {
1843 set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1844 _over_region_trim_target = true;
1848 case LeftFrameHandle:
1849 case RightFrameHandle:
1850 if (is_drawable() && effective_mouse_mode() == MouseObject && !internal_editing() && entered_regionview) {
1851 set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1855 case StartSelectionTrimItem:
1857 case ImageFrameHandleStartItem:
1858 case MarkerViewHandleStartItem:
1860 if (is_drawable()) {
1861 set_canvas_cursor (_cursors->left_side_trim);
1864 case EndSelectionTrimItem:
1866 case ImageFrameHandleEndItem:
1867 case MarkerViewHandleEndItem:
1869 if (is_drawable()) {
1870 set_canvas_cursor (_cursors->right_side_trim);
1874 case PlayheadCursorItem:
1875 if (is_drawable()) {
1876 switch (_edit_point) {
1878 set_canvas_cursor (_cursors->grabber_edit_point);
1881 set_canvas_cursor (_cursors->grabber);
1887 case RegionViewName:
1889 /* when the name is not an active item, the entire name highlight is for trimming */
1891 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1892 if (mouse_mode == MouseObject && is_drawable()) {
1893 set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1894 _over_region_trim_target = true;
1900 case AutomationTrackItem:
1901 if (is_drawable()) {
1902 Gdk::Cursor *cursor;
1903 switch (mouse_mode) {
1905 cursor = _cursors->selector;
1908 cursor = _cursors->zoom_in;
1911 cursor = _cursors->cross_hair;
1915 set_canvas_cursor (cursor);
1917 AutomationTimeAxisView* atv;
1918 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1919 clear_entered_track = false;
1920 set_entered_track (atv);
1926 case RangeMarkerBarItem:
1927 case TransportMarkerBarItem:
1928 case CdMarkerBarItem:
1931 if (is_drawable()) {
1932 set_canvas_cursor (_cursors->timebar);
1937 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1940 entered_marker = marker;
1941 marker->set_color_rgba (ARDOUR_UI::config()->canvasvar_EnteredMarker.get());
1943 case MeterMarkerItem:
1944 case TempoMarkerItem:
1945 if (is_drawable()) {
1946 set_canvas_cursor (_cursors->timebar);
1950 case FadeInHandleItem:
1951 if (mouse_mode == MouseObject && !internal_editing()) {
1952 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1954 rect->property_fill_color_rgba() = 0xBBBBBBAA;
1956 set_canvas_cursor (_cursors->fade_in);
1960 case FadeOutHandleItem:
1961 if (mouse_mode == MouseObject && !internal_editing()) {
1962 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1964 rect->property_fill_color_rgba() = 0xBBBBBBAA;
1966 set_canvas_cursor (_cursors->fade_out);
1969 case FeatureLineItem:
1971 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1972 line->property_fill_color_rgba() = 0xFF0000FF;
1976 if ( get_smart_mode() ) {
1977 set_canvas_cursor ();
1985 /* second pass to handle entered track status in a comprehensible way.
1988 switch (item_type) {
1990 case AutomationLineItem:
1991 case ControlPointItem:
1992 /* these do not affect the current entered track state */
1993 clear_entered_track = false;
1996 case AutomationTrackItem:
1997 /* handled above already */
2001 set_entered_track (0);
2009 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent*, ItemType item_type)
2019 switch (item_type) {
2020 case ControlPointItem:
2021 cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
2022 if (cp->line().the_list()->interpolation() != AutomationList::Discrete) {
2023 if (cp->line().npoints() > 1 && !cp->get_selected()) {
2024 cp->set_visible (false);
2028 if (is_drawable()) {
2029 set_canvas_cursor (current_canvas_cursor);
2032 _verbose_cursor->hide ();
2035 case RegionViewNameHighlight:
2036 case LeftFrameHandle:
2037 case RightFrameHandle:
2038 case StartSelectionTrimItem:
2039 case EndSelectionTrimItem:
2040 case PlayheadCursorItem:
2043 case ImageFrameHandleStartItem:
2044 case ImageFrameHandleEndItem:
2045 case MarkerViewHandleStartItem:
2046 case MarkerViewHandleEndItem:
2049 _over_region_trim_target = false;
2051 if (is_drawable()) {
2052 set_canvas_cursor (current_canvas_cursor);
2057 case AutomationLineItem:
2058 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
2060 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
2062 line->property_fill_color_rgba() = al->get_line_color();
2064 if (is_drawable()) {
2065 set_canvas_cursor (current_canvas_cursor);
2069 case RegionViewName:
2070 /* see enter_handler() for notes */
2071 _over_region_trim_target = false;
2073 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
2074 if (is_drawable() && mouse_mode == MouseObject) {
2075 set_canvas_cursor (current_canvas_cursor);
2080 case RangeMarkerBarItem:
2081 case TransportMarkerBarItem:
2082 case CdMarkerBarItem:
2086 if (is_drawable()) {
2087 set_canvas_cursor (current_canvas_cursor);
2092 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2096 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
2097 location_flags_changed (loc, this);
2100 case MeterMarkerItem:
2101 case TempoMarkerItem:
2103 if (is_drawable()) {
2104 set_canvas_cursor (current_canvas_cursor);
2109 case FadeInHandleItem:
2110 case FadeOutHandleItem:
2111 rv = static_cast<RegionView*>(item->get_data ("regionview"));
2113 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
2115 rect->property_fill_color_rgba() = rv->get_fill_color();
2118 set_canvas_cursor (current_canvas_cursor);
2121 case AutomationTrackItem:
2122 if (is_drawable()) {
2123 set_canvas_cursor (current_canvas_cursor);
2124 clear_entered_track = true;
2125 Glib::signal_idle().connect (sigc::mem_fun(*this, &Editor::left_automation_track));
2128 case FeatureLineItem:
2130 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
2131 line->property_fill_color_rgba() = (guint) ARDOUR_UI::config()->canvasvar_ZeroLine.get();;
2143 Editor::left_automation_track ()
2145 if (clear_entered_track) {
2146 set_entered_track (0);
2147 clear_entered_track = false;
2153 Editor::scrub (framepos_t frame, double current_x)
2157 if (scrubbing_direction == 0) {
2159 _session->request_locate (frame, false);
2160 _session->request_transport_speed (0.1);
2161 scrubbing_direction = 1;
2165 if (last_scrub_x > current_x) {
2167 /* pointer moved to the left */
2169 if (scrubbing_direction > 0) {
2171 /* we reversed direction to go backwards */
2174 scrub_reverse_distance += (int) (last_scrub_x - current_x);
2178 /* still moving to the left (backwards) */
2180 scrub_reversals = 0;
2181 scrub_reverse_distance = 0;
2183 delta = 0.01 * (last_scrub_x - current_x);
2184 _session->request_transport_speed_nonzero (_session->transport_speed() - delta);
2188 /* pointer moved to the right */
2190 if (scrubbing_direction < 0) {
2191 /* we reversed direction to go forward */
2194 scrub_reverse_distance += (int) (current_x - last_scrub_x);
2197 /* still moving to the right */
2199 scrub_reversals = 0;
2200 scrub_reverse_distance = 0;
2202 delta = 0.01 * (current_x - last_scrub_x);
2203 _session->request_transport_speed_nonzero (_session->transport_speed() + delta);
2207 /* if there have been more than 2 opposite motion moves detected, or one that moves
2208 back more than 10 pixels, reverse direction
2211 if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
2213 if (scrubbing_direction > 0) {
2214 /* was forwards, go backwards */
2215 _session->request_transport_speed (-0.1);
2216 scrubbing_direction = -1;
2218 /* was backwards, go forwards */
2219 _session->request_transport_speed (0.1);
2220 scrubbing_direction = 1;
2223 scrub_reverse_distance = 0;
2224 scrub_reversals = 0;
2228 last_scrub_x = current_x;
2232 Editor::motion_handler (ArdourCanvas::Item* /*item*/, GdkEvent* event, bool from_autoscroll)
2234 _last_motion_y = event->motion.y;
2236 if (event->motion.is_hint) {
2239 /* We call this so that MOTION_NOTIFY events continue to be
2240 delivered to the canvas. We need to do this because we set
2241 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
2242 the density of the events, at the expense of a round-trip
2243 to the server. Given that this will mostly occur on cases
2244 where DISPLAY = :0.0, and given the cost of what the motion
2245 event might do, its a good tradeoff.
2248 track_canvas->get_pointer (x, y);
2251 if (current_stepping_trackview) {
2252 /* don't keep the persistent stepped trackview if the mouse moves */
2253 current_stepping_trackview = 0;
2254 step_timeout.disconnect ();
2257 if (_session && _session->actively_recording()) {
2258 /* Sorry. no dragging stuff around while we record */
2262 JoinObjectRangeState const old = _join_object_range_state;
2263 update_join_object_range_location (event->motion.x, event->motion.y);
2264 if (_join_object_range_state != old) {
2265 set_canvas_cursor ();
2268 if (_over_region_trim_target) {
2269 set_canvas_cursor_for_region_view (event->motion.x, entered_regionview);
2272 bool handled = false;
2273 if (_drags->active ()) {
2274 handled = _drags->motion_handler (event, from_autoscroll);
2281 track_canvas_motion (event);
2286 Editor::can_remove_control_point (ArdourCanvas::Item* item)
2288 ControlPoint* control_point;
2290 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2291 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2295 AutomationLine& line = control_point->line ();
2296 if (dynamic_cast<AudioRegionGainLine*> (&line)) {
2297 /* we shouldn't remove the first or last gain point in region gain lines */
2298 if (line.is_last_point(*control_point) || line.is_first_point(*control_point)) {
2307 Editor::remove_control_point (ArdourCanvas::Item* item)
2309 if (!can_remove_control_point (item)) {
2313 ControlPoint* control_point;
2315 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2316 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2320 control_point->line().remove_point (*control_point);
2324 Editor::edit_control_point (ArdourCanvas::Item* item)
2326 ControlPoint* p = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"));
2329 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2333 ControlPointDialog d (p);
2334 d.set_position (Gtk::WIN_POS_MOUSE);
2337 if (d.run () != RESPONSE_ACCEPT) {
2341 p->line().modify_point_y (*p, d.get_y_fraction ());
2345 Editor::edit_notes (MidiRegionView::Selection const & s)
2351 EditNoteDialog d (&(*s.begin())->region_view(), s);
2352 d.set_position (Gtk::WIN_POS_MOUSE);
2360 Editor::visible_order_range (int* low, int* high) const
2362 *low = TimeAxisView::max_order ();
2365 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
2367 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
2369 if (!rtv->hidden()) {
2371 if (*high < rtv->order()) {
2372 *high = rtv->order ();
2375 if (*low > rtv->order()) {
2376 *low = rtv->order ();
2383 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
2385 /* Either add to or set the set the region selection, unless
2386 this is an alignment click (control used)
2389 if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
2390 TimeAxisView* tv = &rv.get_time_axis_view();
2391 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
2393 if (rtv && rtv->is_track()) {
2394 speed = rtv->track()->speed();
2397 framepos_t where = get_preferred_edit_position();
2401 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
2403 align_region (rv.region(), SyncPoint, (framepos_t) (where * speed));
2405 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
2407 align_region (rv.region(), End, (framepos_t) (where * speed));
2411 align_region (rv.region(), Start, (framepos_t) (where * speed));
2418 Editor::collect_new_region_view (RegionView* rv)
2420 latest_regionviews.push_back (rv);
2424 Editor::collect_and_select_new_region_view (RegionView* rv)
2427 latest_regionviews.push_back (rv);
2431 Editor::cancel_selection ()
2433 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2434 (*i)->hide_selection ();
2437 selection->clear ();
2438 clicked_selection = 0;
2442 Editor::cancel_time_selection ()
2444 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2445 (*i)->hide_selection ();
2447 selection->time.clear ();
2448 clicked_selection = 0;
2452 Editor::point_trim (GdkEvent* event, framepos_t new_bound)
2454 RegionView* rv = clicked_regionview;
2456 /* Choose action dependant on which button was pressed */
2457 switch (event->button.button) {
2459 begin_reversible_command (_("start point trim"));
2461 if (selection->selected (rv)) {
2462 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
2463 i != selection->regions.by_layer().end(); ++i)
2465 if (!(*i)->region()->locked()) {
2466 (*i)->region()->clear_changes ();
2467 (*i)->region()->trim_front (new_bound);
2468 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2473 if (!rv->region()->locked()) {
2474 rv->region()->clear_changes ();
2475 rv->region()->trim_front (new_bound);
2476 _session->add_command(new StatefulDiffCommand (rv->region()));
2480 commit_reversible_command();
2484 begin_reversible_command (_("End point trim"));
2486 if (selection->selected (rv)) {
2488 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
2490 if (!(*i)->region()->locked()) {
2491 (*i)->region()->clear_changes();
2492 (*i)->region()->trim_end (new_bound);
2493 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2499 if (!rv->region()->locked()) {
2500 rv->region()->clear_changes ();
2501 rv->region()->trim_end (new_bound);
2502 _session->add_command (new StatefulDiffCommand (rv->region()));
2506 commit_reversible_command();
2515 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* /*event*/)
2520 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2521 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2525 Location* location = find_location_from_marker (marker, is_start);
2526 location->set_hidden (true, this);
2531 Editor::reposition_zoom_rect (framepos_t start, framepos_t end)
2533 double x1 = frame_to_pixel (start);
2534 double x2 = frame_to_pixel (end);
2535 double y2 = full_canvas_height - 1.0;
2537 zoom_rect->property_x1() = x1;
2538 zoom_rect->property_y1() = 1.0;
2539 zoom_rect->property_x2() = x2;
2540 zoom_rect->property_y2() = y2;
2545 Editor::mouse_rename_region (ArdourCanvas::Item* /*item*/, GdkEvent* /*event*/)
2547 using namespace Gtkmm2ext;
2549 ArdourPrompter prompter (false);
2551 prompter.set_prompt (_("Name for region:"));
2552 prompter.set_initial_text (clicked_regionview->region()->name());
2553 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
2554 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
2555 prompter.show_all ();
2556 switch (prompter.run ()) {
2557 case Gtk::RESPONSE_ACCEPT:
2559 prompter.get_result(str);
2561 clicked_regionview->region()->set_name (str);
2570 Editor::mouse_brush_insert_region (RegionView* rv, framepos_t pos)
2572 /* no brushing without a useful snap setting */
2574 switch (_snap_mode) {
2576 return; /* can't work because it allows region to be placed anywhere */
2581 switch (_snap_type) {
2589 /* don't brush a copy over the original */
2591 if (pos == rv->region()->position()) {
2595 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
2597 if (rtv == 0 || !rtv->is_track()) {
2601 boost::shared_ptr<Playlist> playlist = rtv->playlist();
2602 double speed = rtv->track()->speed();
2604 playlist->clear_changes ();
2605 boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region(), true));
2606 playlist->add_region (new_region, (framepos_t) (pos * speed));
2607 _session->add_command (new StatefulDiffCommand (playlist));
2609 // playlist is frozen, so we have to update manually XXX this is disgusting
2611 playlist->RegionAdded (new_region); /* EMIT SIGNAL */
2615 Editor::track_height_step_timeout ()
2617 if (get_microseconds() - last_track_height_step_timestamp < 250000) {
2618 current_stepping_trackview = 0;
2625 Editor::add_region_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2627 assert (region_view);
2629 if (!region_view->region()->playlist()) {
2633 _region_motion_group->raise_to_top ();
2635 if (Config->get_edit_mode() == Splice) {
2636 _drags->add (new RegionSpliceDrag (this, item, region_view, selection->regions.by_layer()));
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(), false, false));
2642 /* sync the canvas to what we think is its current state */
2643 update_canvas_now();
2647 Editor::add_region_copy_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2649 assert (region_view);
2651 if (!region_view->region()->playlist()) {
2655 _region_motion_group->raise_to_top ();
2657 RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id);
2658 _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), false, true));
2662 Editor::add_region_brush_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2664 assert (region_view);
2666 if (!region_view->region()->playlist()) {
2670 if (Config->get_edit_mode() == Splice) {
2674 RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id);
2675 _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), true, false));
2677 begin_reversible_command (Operations::drag_region_brush);
2680 /** Start a grab where a time range is selected, track(s) are selected, and the
2681 * user clicks and drags a region with a modifier in order to create a new region containing
2682 * the section of the clicked region that lies within the time range.
2685 Editor::start_selection_grab (ArdourCanvas::Item* /*item*/, GdkEvent* event)
2687 if (clicked_regionview == 0) {
2691 /* lets try to create new Region for the selection */
2693 vector<boost::shared_ptr<Region> > new_regions;
2694 create_region_from_selection (new_regions);
2696 if (new_regions.empty()) {
2700 /* XXX fix me one day to use all new regions */
2702 boost::shared_ptr<Region> region (new_regions.front());
2704 /* add it to the current stream/playlist.
2706 tricky: the streamview for the track will add a new regionview. we will
2707 catch the signal it sends when it creates the regionview to
2708 set the regionview we want to then drag.
2711 latest_regionviews.clear();
2712 sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
2714 /* A selection grab currently creates two undo/redo operations, one for
2715 creating the new region and another for moving it.
2718 begin_reversible_command (Operations::selection_grab);
2720 boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
2722 playlist->clear_changes ();
2723 clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
2724 _session->add_command(new StatefulDiffCommand (playlist));
2726 commit_reversible_command ();
2730 if (latest_regionviews.empty()) {
2731 /* something went wrong */
2735 /* we need to deselect all other regionviews, and select this one
2736 i'm ignoring undo stuff, because the region creation will take care of it
2738 selection->set (latest_regionviews);
2740 _drags->set (new RegionMoveDrag (this, latest_regionviews.front()->get_canvas_group(), latest_regionviews.front(), latest_regionviews, false, false), event);
2746 if (_drags->active ()) {
2749 selection->clear ();
2754 Editor::set_internal_edit (bool yn)
2756 if (_internal_editing == yn) {
2760 _internal_editing = yn;
2763 pre_internal_mouse_mode = mouse_mode;
2764 pre_internal_snap_type = _snap_type;
2765 pre_internal_snap_mode = _snap_mode;
2767 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2768 (*i)->enter_internal_edit_mode ();
2771 set_snap_to (internal_snap_type);
2772 set_snap_mode (internal_snap_mode);
2776 internal_snap_mode = _snap_mode;
2777 internal_snap_type = _snap_type;
2779 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2780 (*i)->leave_internal_edit_mode ();
2783 if (mouse_mode == MouseDraw && pre_internal_mouse_mode != MouseDraw) {
2784 /* we were drawing .. flip back to something sensible */
2785 set_mouse_mode (pre_internal_mouse_mode);
2788 set_snap_to (pre_internal_snap_type);
2789 set_snap_mode (pre_internal_snap_mode);
2792 set_canvas_cursor ();
2795 /** Update _join_object_range_state which indicate whether we are over the top or bottom half of a region view,
2796 * used by the `join object/range' tool mode.
2799 Editor::update_join_object_range_location (double /*x*/, double y)
2801 /* XXX: actually, this decides based on whether the mouse is in the top
2802 or bottom half of a the waveform part RouteTimeAxisView;
2804 Note that entered_{track,regionview} is not always setup (e.g. if
2805 the mouse is over a TimeSelection), and to get a Region
2806 that we're over requires searching the playlist.
2809 if ( !get_smart_mode() ) {
2810 _join_object_range_state = JOIN_OBJECT_RANGE_NONE;
2814 if (mouse_mode == MouseObject) {
2815 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2816 } else if (mouse_mode == MouseRange) {
2817 _join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
2820 /* XXX: maybe we should make entered_track work in all cases, rather than resorting to this */
2821 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y + vertical_adjustment.get_value() - canvas_timebars_vsize);
2825 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
2830 rtv->canvas_display()->w2i (cx, cy);
2832 double const c = cy / (rtv->view()->child_height() - TimeAxisViewItem::NAME_HIGHLIGHT_SIZE);
2834 _join_object_range_state = c <= 0.5 ? JOIN_OBJECT_RANGE_RANGE : JOIN_OBJECT_RANGE_OBJECT;
2840 Editor::effective_mouse_mode () const
2842 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
2844 } else if (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE) {
2852 Editor::remove_midi_note (ArdourCanvas::Item* item, GdkEvent *)
2854 ArdourCanvas::CanvasNoteEvent* e = dynamic_cast<ArdourCanvas::CanvasNoteEvent*> (item);
2857 e->region_view().delete_note (e->note ());
2861 Editor::set_canvas_cursor_for_region_view (double x, RegionView* rv)
2865 ArdourCanvas::Group* g = rv->get_canvas_group ();
2866 ArdourCanvas::Group* p = g->get_parent_group ();
2868 /* Compute x in region view parent coordinates */
2872 double x1, x2, y1, y2;
2873 g->get_bounds (x1, y1, x2, y2);
2875 /* Halfway across the region */
2876 double const h = (x1 + x2) / 2;
2878 Trimmable::CanTrim ct = rv->region()->can_trim ();
2880 if (ct & Trimmable::FrontTrimEarlier) {
2881 set_canvas_cursor (_cursors->left_side_trim);
2883 set_canvas_cursor (_cursors->left_side_trim_right_only);
2886 if (ct & Trimmable::EndTrimLater) {
2887 set_canvas_cursor (_cursors->right_side_trim);
2889 set_canvas_cursor (_cursors->right_side_trim_left_only);
2894 /** Obtain the pointer position in world coordinates */
2896 Editor::get_pointer_position (double& x, double& y) const
2899 track_canvas->get_pointer (px, py);
2900 track_canvas->window_to_world (px, py, x, y);