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/types.h"
66 #include "ardour/profile.h"
67 #include "ardour/route.h"
68 #include "ardour/audio_track.h"
69 #include "ardour/playlist.h"
70 #include "ardour/audioplaylist.h"
71 #include "ardour/audioregion.h"
72 #include "ardour/midi_region.h"
73 #include "ardour/dB.h"
74 #include "ardour/utils.h"
75 #include "ardour/region_factory.h"
76 #include "ardour/source_factory.h"
77 #include "ardour/session.h"
78 #include "ardour/operations.h"
85 using namespace ARDOUR;
88 using namespace Editing;
89 using Gtkmm2ext::Keyboard;
92 Editor::mouse_frame (framepos_t& where, bool& in_track_canvas) const
96 Gdk::ModifierType mask;
97 Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->track_canvas->get_window();
98 Glib::RefPtr<const Gdk::Window> pointer_window;
100 if (!canvas_window) {
104 pointer_window = canvas_window->get_pointer (x, y, mask);
106 if (pointer_window == track_canvas->get_bin_window()) {
109 in_track_canvas = true;
112 in_track_canvas = false;
117 event.type = GDK_BUTTON_RELEASE;
121 where = event_frame (&event, 0, 0);
126 Editor::event_frame (GdkEvent const * event, double* pcx, double* pcy) const
140 switch (event->type) {
141 case GDK_BUTTON_RELEASE:
142 case GDK_BUTTON_PRESS:
143 case GDK_2BUTTON_PRESS:
144 case GDK_3BUTTON_PRESS:
145 *pcx = event->button.x;
146 *pcy = event->button.y;
147 _trackview_group->w2i(*pcx, *pcy);
149 case GDK_MOTION_NOTIFY:
150 *pcx = event->motion.x;
151 *pcy = event->motion.y;
152 _trackview_group->w2i(*pcx, *pcy);
154 case GDK_ENTER_NOTIFY:
155 case GDK_LEAVE_NOTIFY:
156 track_canvas->w2c(event->crossing.x, event->crossing.y, *pcx, *pcy);
159 case GDK_KEY_RELEASE:
160 // track_canvas->w2c(event->key.x, event->key.y, *pcx, *pcy);
163 warning << string_compose (_("Editor::event_frame() used on unhandled event type %1"), event->type) << endmsg;
167 /* note that pixel_to_frame() never returns less than zero, so even if the pixel
168 position is negative (as can be the case with motion events in particular),
169 the frame location is always positive.
172 return pixel_to_frame (*pcx);
176 Editor::which_grabber_cursor ()
178 Gdk::Cursor* c = _cursors->grabber;
180 if (_internal_editing) {
181 switch (mouse_mode) {
183 c = _cursors->midi_pencil;
187 c = _cursors->grabber_note;
191 c = _cursors->midi_resize;
200 switch (_edit_point) {
202 c = _cursors->grabber_edit_point;
205 boost::shared_ptr<Movable> m = _movable.lock();
206 if (m && m->locked()) {
207 c = _cursors->speaker;
217 Editor::set_current_trimmable (boost::shared_ptr<Trimmable> t)
219 boost::shared_ptr<Trimmable> st = _trimmable.lock();
221 if (!st || st == t) {
223 set_canvas_cursor ();
228 Editor::set_current_movable (boost::shared_ptr<Movable> m)
230 boost::shared_ptr<Movable> sm = _movable.lock();
232 if (!sm || sm != m) {
234 set_canvas_cursor ();
239 Editor::set_canvas_cursor ()
241 if (_internal_editing) {
243 switch (mouse_mode) {
245 current_canvas_cursor = _cursors->midi_pencil;
249 current_canvas_cursor = which_grabber_cursor();
253 current_canvas_cursor = _cursors->midi_resize;
262 switch (mouse_mode) {
264 current_canvas_cursor = _cursors->selector;
268 current_canvas_cursor = which_grabber_cursor();
272 current_canvas_cursor = _cursors->cross_hair;
276 if (Keyboard::the_keyboard().key_is_down (GDK_Control_L)) {
277 current_canvas_cursor = _cursors->zoom_out;
279 current_canvas_cursor = _cursors->zoom_in;
284 current_canvas_cursor = _cursors->time_fx; // just use playhead
288 current_canvas_cursor = _cursors->speaker;
293 switch (_join_object_range_state) {
294 case JOIN_OBJECT_RANGE_NONE:
296 case JOIN_OBJECT_RANGE_OBJECT:
297 current_canvas_cursor = which_grabber_cursor ();
299 case JOIN_OBJECT_RANGE_RANGE:
300 current_canvas_cursor = _cursors->selector;
304 /* up-down cursor as a cue that automation can be dragged up and down when in join object/range mode */
305 if (join_object_range_button.get_active() && last_item_entered) {
306 if (last_item_entered->property_parent() && (*last_item_entered->property_parent()).get_data (X_("timeselection"))) {
307 pair<TimeAxisView*, int> tvp = trackview_by_y_position (_last_motion_y + vertical_adjustment.get_value() - canvas_timebars_vsize);
308 if (dynamic_cast<AutomationTimeAxisView*> (tvp.first)) {
309 current_canvas_cursor = _cursors->up_down;
314 set_canvas_cursor (current_canvas_cursor, true);
318 Editor::set_mouse_mode (MouseMode m, bool force)
320 if (_drags->active ()) {
324 if (!force && m == mouse_mode) {
328 Glib::RefPtr<Action> act;
332 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-range"));
336 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-object"));
340 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-gain"));
344 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-zoom"));
348 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-timefx"));
352 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-audition"));
358 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
361 /* go there and back to ensure that the toggled handler is called to set up mouse_mode */
362 tact->set_active (false);
363 tact->set_active (true);
365 MouseModeChanged (); /* EMIT SIGNAL */
369 Editor::mouse_mode_toggled (MouseMode m)
375 if (!internal_editing()) {
376 if (mouse_mode != MouseRange && _join_object_range_state == JOIN_OBJECT_RANGE_NONE) {
378 /* in all modes except range and joined object/range, hide the range selection,
379 show the object (region) selection.
382 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
383 (*i)->hide_selection ();
389 in range or object/range mode, show the range selection.
392 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
393 (*i)->show_selection (selection->time);
398 set_canvas_cursor ();
400 MouseModeChanged (); /* EMIT SIGNAL */
404 Editor::step_mouse_mode (bool next)
406 switch (current_mouse_mode()) {
409 if (Profile->get_sae()) {
410 set_mouse_mode (MouseZoom);
412 set_mouse_mode (MouseRange);
415 set_mouse_mode (MouseTimeFX);
420 if (next) set_mouse_mode (MouseZoom);
421 else set_mouse_mode (MouseObject);
426 if (Profile->get_sae()) {
427 set_mouse_mode (MouseTimeFX);
429 set_mouse_mode (MouseGain);
432 if (Profile->get_sae()) {
433 set_mouse_mode (MouseObject);
435 set_mouse_mode (MouseRange);
441 if (next) set_mouse_mode (MouseTimeFX);
442 else set_mouse_mode (MouseZoom);
447 set_mouse_mode (MouseAudition);
449 if (Profile->get_sae()) {
450 set_mouse_mode (MouseZoom);
452 set_mouse_mode (MouseGain);
458 if (next) set_mouse_mode (MouseObject);
459 else set_mouse_mode (MouseTimeFX);
465 Editor::button_selection (ArdourCanvas::Item* /*item*/, GdkEvent* event, ItemType item_type)
467 /* in object/audition/timefx/gain-automation mode,
468 any button press sets the selection if the object
469 can be selected. this is a bit of hack, because
470 we want to avoid this if the mouse operation is a
473 note: not dbl-click or triple-click
475 Also note that there is no region selection in internal edit mode, otherwise
476 for operations operating on the selection (e.g. cut) it is not obvious whether
477 to cut notes or regions.
480 if (((mouse_mode != MouseObject) &&
481 (_join_object_range_state != JOIN_OBJECT_RANGE_OBJECT) &&
482 (mouse_mode != MouseAudition || item_type != RegionItem) &&
483 (mouse_mode != MouseTimeFX || item_type != RegionItem) &&
484 (mouse_mode != MouseGain) &&
485 (mouse_mode != MouseRange)) ||
486 ((event->type != GDK_BUTTON_PRESS && event->type != GDK_BUTTON_RELEASE) || event->button.button > 3) ||
487 internal_editing()) {
492 if (event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) {
494 if ((event->button.state & Keyboard::RelevantModifierKeyMask) && event->button.button != 1) {
496 /* almost no selection action on modified button-2 or button-3 events */
498 if (item_type != RegionItem && event->button.button != 2) {
504 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
505 bool press = (event->type == GDK_BUTTON_PRESS);
509 if (mouse_mode != MouseRange || _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
510 set_selected_regionview_from_click (press, op, true);
511 } else if (event->type == GDK_BUTTON_PRESS) {
512 selection->clear_tracks ();
513 set_selected_track_as_side_effect (op, true);
515 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT && !selection->regions.empty()) {
516 clicked_selection = select_range_around_region (selection->regions.front());
520 case RegionViewNameHighlight:
522 case LeftFrameHandle:
523 case RightFrameHandle:
524 if (mouse_mode != MouseRange || _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
525 set_selected_regionview_from_click (press, op, true);
526 } else if (event->type == GDK_BUTTON_PRESS) {
527 set_selected_track_as_side_effect (op);
532 case FadeInHandleItem:
534 case FadeOutHandleItem:
536 if (mouse_mode != MouseRange || _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
537 set_selected_regionview_from_click (press, op, true);
538 } else if (event->type == GDK_BUTTON_PRESS) {
539 set_selected_track_as_side_effect (op);
543 case ControlPointItem:
544 set_selected_track_as_side_effect (op, true);
545 if (mouse_mode != MouseRange || _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
546 set_selected_control_point_from_click (op, false);
551 /* for context click, select track */
552 if (event->button.button == 3) {
553 selection->clear_tracks ();
554 set_selected_track_as_side_effect (op, true);
558 case AutomationTrackItem:
559 set_selected_track_as_side_effect (op, true);
568 Editor::button_press_handler_1 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
570 /* single mouse clicks on any of these item types operate
571 independent of mouse mode, mostly because they are
572 not on the main track canvas or because we want
577 case PlayheadCursorItem:
578 _drags->set (new CursorDrag (this, item, true), event);
582 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
583 hide_marker (item, event);
585 _drags->set (new MarkerDrag (this, item), event);
589 case TempoMarkerItem:
591 TempoMarker* m = reinterpret_cast<TempoMarker*> (item->get_data ("marker"));
593 if (m->tempo().movable ()) {
595 new TempoMarkerDrag (
598 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
608 case MeterMarkerItem:
610 MeterMarker* m = reinterpret_cast<MeterMarker*> (item->get_data ("marker"));
612 if (m->meter().movable ()) {
614 new MeterMarkerDrag (
617 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
630 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
631 _drags->set (new CursorDrag (this, &playhead_cursor->canvas_item, false), event);
637 case RangeMarkerBarItem:
638 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
639 _drags->set (new CursorDrag (this, &playhead_cursor->canvas_item, false), event);
641 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateRangeMarker), event);
646 case CdMarkerBarItem:
647 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
648 _drags->set (new CursorDrag (this, &playhead_cursor->canvas_item, false), event);
650 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateCDMarker), event);
655 case TransportMarkerBarItem:
656 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
657 _drags->set (new CursorDrag (this, &playhead_cursor->canvas_item, false), event);
659 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateTransportMarker), event);
668 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
669 /* special case: allow trim of range selections in joined object mode;
670 in theory eff should equal MouseRange in this case, but it doesn't
671 because entering the range selection canvas item results in entered_regionview
672 being set to 0, so update_join_object_range_location acts as if we aren't
675 if (item_type == StartSelectionTrimItem) {
676 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
677 } else if (item_type == EndSelectionTrimItem) {
678 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
682 Editing::MouseMode eff = effective_mouse_mode ();
684 /* special case: allow drag of region fade in/out in object mode with join object/range enabled */
685 if (item_type == FadeInHandleItem || item_type == FadeOutHandleItem) {
692 case StartSelectionTrimItem:
693 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
696 case EndSelectionTrimItem:
697 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
701 if (Keyboard::modifier_state_contains
702 (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier))) {
703 // contains and not equals because I can't use alt as a modifier alone.
704 start_selection_grab (item, event);
705 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
706 /* grab selection for moving */
707 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionMove), event);
709 double const y = event->button.y + vertical_adjustment.get_value() - canvas_timebars_vsize;
710 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
712 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
713 if (join_object_range_button.get_active() && atv) {
714 /* smart "join" mode: drag automation */
715 _drags->set (new AutomationRangeDrag (this, atv->base_item(), selection->time), event, _cursors->up_down);
717 /* this was debated, but decided the more common action was to
718 make a new selection */
719 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
726 if (internal_editing()) {
727 /* trim notes if we're in internal edit mode and near the ends of the note */
728 ArdourCanvas::CanvasNote* cn = dynamic_cast<ArdourCanvas::CanvasNote*> (item);
729 if (cn && cn->big_enough_to_trim() && cn->mouse_near_ends()) {
730 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
732 _drags->set (new NoteDrag (this, item), event);
738 if (internal_editing()) {
739 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
740 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
744 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
749 case RegionViewNameHighlight:
750 if (!clicked_regionview->region()->locked()) {
751 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
752 _drags->set (new TrimDrag (this, item, clicked_regionview, s.by_layer()), event);
757 case LeftFrameHandle:
758 case RightFrameHandle:
759 if (!internal_editing() && !clicked_regionview->region()->locked()) {
760 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
761 _drags->set (new TrimDrag (this, item, clicked_regionview, s.by_layer()), event);
767 if (!internal_editing()) {
768 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
777 if (internal_editing()) {
778 ArdourCanvas::CanvasNoteEvent* cn = dynamic_cast<ArdourCanvas::CanvasNoteEvent*> (item);
779 if (cn->mouse_near_ends()) {
780 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
782 _drags->set (new NoteDrag (this, item), event);
792 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)) &&
793 event->type == GDK_BUTTON_PRESS) {
795 _drags->set (new RubberbandSelectDrag (this, item), event);
797 } else if (event->type == GDK_BUTTON_PRESS) {
800 case FadeInHandleItem:
802 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
803 _drags->set (new FadeInDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), s), event, _cursors->fade_in);
807 case FadeOutHandleItem:
809 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
810 _drags->set (new FadeOutDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), s), event, _cursors->fade_out);
814 case FeatureLineItem:
816 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier)) {
817 remove_transient(item);
821 _drags->set (new FeatureLineDrag (this, item), event);
827 if (dynamic_cast<AutomationRegionView*> (clicked_regionview)) {
828 /* click on an automation region view; do nothing here and let the ARV's signal handler
834 if (internal_editing ()) {
835 /* no region drags in internal edit mode */
839 /* click on a normal region view */
840 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
841 add_region_copy_drag (item, event, clicked_regionview);
842 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
843 add_region_brush_drag (item, event, clicked_regionview);
845 add_region_drag (item, event, clicked_regionview);
848 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT && !selection->regions.empty()) {
849 _drags->add (new SelectionDrag (this, clicked_axisview->get_selection_rect (clicked_selection)->rect, SelectionDrag::SelectionMove));
852 _drags->start_grab (event);
855 case RegionViewNameHighlight:
856 case LeftFrameHandle:
857 case RightFrameHandle:
858 if (!clicked_regionview->region()->locked()) {
859 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
860 _drags->set (new TrimDrag (this, item, clicked_regionview, s.by_layer()), event);
867 /* rename happens on edit clicks */
868 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
869 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, s.by_layer()), event);
874 case ControlPointItem:
875 _drags->set (new ControlPointDrag (this, item), event);
879 case AutomationLineItem:
880 _drags->set (new LineDrag (this, item), event);
885 if (internal_editing()) {
886 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
887 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
891 _drags->set (new RubberbandSelectDrag (this, item), event);
895 case AutomationTrackItem:
897 TimeAxisView* parent = clicked_axisview->get_parent ();
898 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (clicked_axisview);
900 if (parent && dynamic_cast<MidiTimeAxisView*> (parent) && atv->show_regions ()) {
901 /* create a MIDI region so that we have somewhere to put automation */
902 _drags->set (new RegionCreateDrag (this, item, parent), event);
904 /* rubberband drag to select automation points */
905 _drags->set (new RubberbandSelectDrag (this, item), event);
912 if (join_object_range_button.get_active()) {
913 /* we're in "smart" joined mode, and we've clicked on a Selection */
914 double const y = event->button.y + vertical_adjustment.get_value() - canvas_timebars_vsize;
915 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
917 /* if we're over an automation track, start a drag of its data */
918 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
920 _drags->set (new AutomationRangeDrag (this, atv->base_item(), selection->time), event, _cursors->up_down);
923 /* if we're over a track and a region, and in the `object' part of a region,
924 put a selection around the region and drag both
926 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
927 if (rtv && _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
928 boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track> (rtv->route ());
930 boost::shared_ptr<Playlist> pl = t->playlist ();
933 boost::shared_ptr<Region> r = pl->top_region_at (event_frame (event));
935 RegionView* rv = rtv->view()->find_view (r);
936 clicked_selection = select_range_around_region (rv);
937 _drags->add (new SelectionDrag (this, item, SelectionDrag::SelectionMove));
938 list<RegionView*> rvs;
940 _drags->add (new RegionMoveDrag (this, item, rv, rvs, false, false));
941 _drags->start_grab (event);
952 case ImageFrameHandleStartItem:
953 imageframe_start_handle_op(item, event) ;
956 case ImageFrameHandleEndItem:
957 imageframe_end_handle_op(item, event) ;
960 case MarkerViewHandleStartItem:
961 markerview_item_start_handle_op(item, event) ;
964 case MarkerViewHandleEndItem:
965 markerview_item_end_handle_op(item, event) ;
969 start_markerview_grab(item, event) ;
972 start_imageframe_grab(item, event) ;
990 /* start a grab so that if we finish after moving
991 we can tell what happened.
993 _drags->set (new RegionGainDrag (this, item), event, current_canvas_cursor);
997 _drags->set (new LineDrag (this, item), event);
1000 case ControlPointItem:
1001 _drags->set (new ControlPointDrag (this, item), event);
1011 switch (item_type) {
1012 case ControlPointItem:
1013 _drags->set (new ControlPointDrag (this, item), event);
1016 case AutomationLineItem:
1017 _drags->set (new LineDrag (this, item), event);
1021 // XXX need automation mode to identify which
1023 // start_line_grab_from_regionview (item, event);
1033 if (event->type == GDK_BUTTON_PRESS) {
1034 _drags->set (new MouseZoomDrag (this, item), event);
1041 if (internal_editing() && item_type == NoteItem) {
1042 /* drag notes if we're in internal edit mode */
1043 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
1045 } else if ((!internal_editing() || dynamic_cast<AudioRegionView*> (clicked_regionview)) && clicked_regionview) {
1046 /* do time-FX if we're not in internal edit mode, or we are but we clicked on an audio region */
1047 _drags->set (new TimeFXDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1053 _drags->set (new ScrubDrag (this, item), event);
1054 scrub_reversals = 0;
1055 scrub_reverse_distance = 0;
1056 last_scrub_x = event->button.x;
1057 scrubbing_direction = 0;
1058 set_canvas_cursor (_cursors->transparent);
1070 Editor::button_press_handler_2 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1072 Editing::MouseMode const eff = effective_mouse_mode ();
1075 switch (item_type) {
1077 if (internal_editing ()) {
1078 /* no region drags in internal edit mode */
1082 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
1083 add_region_copy_drag (item, event, clicked_regionview);
1085 add_region_drag (item, event, clicked_regionview);
1087 _drags->start_grab (event);
1090 case ControlPointItem:
1091 _drags->set (new ControlPointDrag (this, item), event);
1099 switch (item_type) {
1100 case RegionViewNameHighlight:
1101 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1105 case LeftFrameHandle:
1106 case RightFrameHandle:
1107 if (!internal_editing ()) {
1108 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1113 case RegionViewName:
1114 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer()), event);
1125 /* relax till release */
1131 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1132 temporal_zoom_to_frame (false, event_frame (event));
1134 temporal_zoom_to_frame (true, event_frame(event));
1147 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1149 if (event->type != GDK_BUTTON_PRESS) {
1153 Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->track_canvas->get_window();
1155 if (canvas_window) {
1156 Glib::RefPtr<const Gdk::Window> pointer_window;
1159 Gdk::ModifierType mask;
1161 pointer_window = canvas_window->get_pointer (x, y, mask);
1163 if (pointer_window == track_canvas->get_bin_window()) {
1164 track_canvas->window_to_world (x, y, wx, wy);
1168 pre_press_cursor = current_canvas_cursor;
1170 track_canvas->grab_focus();
1172 if (_session && _session->actively_recording()) {
1176 button_selection (item, event, item_type);
1178 if (!_drags->active () &&
1179 (Keyboard::is_delete_event (&event->button) ||
1180 Keyboard::is_context_menu_event (&event->button) ||
1181 Keyboard::is_edit_event (&event->button))) {
1183 /* handled by button release */
1187 switch (event->button.button) {
1189 return button_press_handler_1 (item, event, item_type);
1193 return button_press_handler_2 (item, event, item_type);
1200 return button_press_dispatch (&event->button);
1209 Editor::button_press_dispatch (GdkEventButton* ev)
1211 /* this function is intended only for buttons 4 and above.
1214 Gtkmm2ext::MouseButton b (ev->state, ev->button);
1215 return button_bindings->activate (b, Gtkmm2ext::Bindings::Press);
1219 Editor::button_release_dispatch (GdkEventButton* ev)
1221 /* this function is intended only for buttons 4 and above.
1224 Gtkmm2ext::MouseButton b (ev->state, ev->button);
1225 return button_bindings->activate (b, Gtkmm2ext::Bindings::Release);
1229 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1231 framepos_t where = event_frame (event, 0, 0);
1232 AutomationTimeAxisView* atv = 0;
1234 if (pre_press_cursor) {
1235 set_canvas_cursor (pre_press_cursor);
1236 pre_press_cursor = 0;
1239 /* no action if we're recording */
1241 if (_session && _session->actively_recording()) {
1245 /* see if we're finishing a drag */
1247 bool were_dragging = false;
1248 if (_drags->active ()) {
1249 bool const r = _drags->end_grab (event);
1251 /* grab dragged, so do nothing else */
1255 were_dragging = true;
1258 update_region_layering_order_editor ();
1260 /* edit events get handled here */
1262 if (!_drags->active () && Keyboard::is_edit_event (&event->button)) {
1263 switch (item_type) {
1265 show_region_properties ();
1268 case TempoMarkerItem:
1269 edit_tempo_marker (item);
1272 case MeterMarkerItem:
1273 edit_meter_marker (item);
1276 case RegionViewName:
1277 if (clicked_regionview->name_active()) {
1278 return mouse_rename_region (item, event);
1282 case ControlPointItem:
1283 edit_control_point (item);
1296 /* context menu events get handled here */
1298 if (Keyboard::is_context_menu_event (&event->button)) {
1300 if (!_drags->active ()) {
1302 /* no matter which button pops up the context menu, tell the menu
1303 widget to use button 1 to drive menu selection.
1306 switch (item_type) {
1308 case FadeInHandleItem:
1310 case FadeOutHandleItem:
1311 popup_fade_context_menu (1, event->button.time, item, item_type);
1315 popup_track_context_menu (1, event->button.time, item_type, false);
1319 case RegionViewNameHighlight:
1320 case LeftFrameHandle:
1321 case RightFrameHandle:
1322 case RegionViewName:
1323 popup_track_context_menu (1, event->button.time, item_type, false);
1327 popup_track_context_menu (1, event->button.time, item_type, true);
1330 case AutomationTrackItem:
1331 popup_track_context_menu (1, event->button.time, item_type, false);
1335 case RangeMarkerBarItem:
1336 case TransportMarkerBarItem:
1337 case CdMarkerBarItem:
1340 popup_ruler_menu (where, item_type);
1344 marker_context_menu (&event->button, item);
1347 case TempoMarkerItem:
1348 tempo_or_meter_marker_context_menu (&event->button, item);
1351 case MeterMarkerItem:
1352 tempo_or_meter_marker_context_menu (&event->button, item);
1355 case CrossfadeViewItem:
1356 popup_track_context_menu (1, event->button.time, item_type, false);
1359 case ControlPointItem:
1360 popup_control_point_context_menu (item, event);
1364 case ImageFrameItem:
1365 popup_imageframe_edit_menu(1, event->button.time, item, true) ;
1367 case ImageFrameTimeAxisItem:
1368 popup_imageframe_edit_menu(1, event->button.time, item, false) ;
1370 case MarkerViewItem:
1371 popup_marker_time_axis_edit_menu(1, event->button.time, item, true) ;
1373 case MarkerTimeAxisItem:
1374 popup_marker_time_axis_edit_menu(1, event->button.time, item, false) ;
1386 /* delete events get handled here */
1388 Editing::MouseMode const eff = effective_mouse_mode ();
1390 if (!_drags->active () && Keyboard::is_delete_event (&event->button)) {
1392 switch (item_type) {
1393 case TempoMarkerItem:
1394 remove_tempo_marker (item);
1397 case MeterMarkerItem:
1398 remove_meter_marker (item);
1402 remove_marker (*item, event);
1406 if (eff == MouseObject) {
1407 remove_clicked_region ();
1411 case ControlPointItem:
1412 remove_control_point (item);
1416 remove_midi_note (item, event);
1425 switch (event->button.button) {
1428 switch (item_type) {
1429 /* see comments in button_press_handler */
1430 case PlayheadCursorItem:
1433 case AutomationLineItem:
1434 case StartSelectionTrimItem:
1435 case EndSelectionTrimItem:
1439 if (!_dragging_playhead) {
1440 snap_to_with_modifier (where, event, 0, true);
1441 mouse_add_new_marker (where);
1445 case CdMarkerBarItem:
1446 if (!_dragging_playhead) {
1447 // if we get here then a dragged range wasn't done
1448 snap_to_with_modifier (where, event, 0, true);
1449 mouse_add_new_marker (where, true);
1454 if (!_dragging_playhead) {
1455 snap_to_with_modifier (where, event);
1456 mouse_add_new_tempo_event (where);
1461 if (!_dragging_playhead) {
1462 mouse_add_new_meter_event (pixel_to_frame (event->button.x));
1473 switch (item_type) {
1474 case AutomationTrackItem:
1475 atv = dynamic_cast<AutomationTimeAxisView*>(clicked_axisview);
1477 atv->add_automation_event (item, event, where, event->button.y);
1488 switch (item_type) {
1491 /* check that we didn't drag before releasing, since
1492 its really annoying to create new control
1493 points when doing this.
1495 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (clicked_regionview);
1496 if (were_dragging && arv) {
1497 arv->add_gain_point_event (item, event);
1503 case AutomationTrackItem:
1504 dynamic_cast<AutomationTimeAxisView*>(clicked_axisview)->
1505 add_automation_event (item, event, where, event->button.y);
1514 set_canvas_cursor (current_canvas_cursor);
1515 if (scrubbing_direction == 0) {
1516 /* no drag, just a click */
1517 switch (item_type) {
1519 play_selected_region ();
1525 /* make sure we stop */
1526 _session->request_transport_speed (0.0);
1535 /* do any (de)selection operations that should occur on button release */
1536 button_selection (item, event, item_type);
1545 switch (item_type) {
1547 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1549 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier|Keyboard::SecondaryModifier))) {
1552 // Button2 click is unused
1565 // x_style_paste (where, 1.0);
1586 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1593 last_item_entered = item;
1595 switch (item_type) {
1596 case ControlPointItem:
1597 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1598 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1599 cp->set_visible (true);
1603 at_y = cp->get_y ();
1604 cp->i2w (at_x, at_y);
1608 fraction = 1.0 - (cp->get_y() / cp->line().height());
1610 if (is_drawable() && !_drags->active ()) {
1611 set_canvas_cursor (_cursors->fader);
1614 _verbose_cursor->set (cp->line().get_verbose_cursor_string (fraction), at_x, at_y);
1615 _verbose_cursor->show ();
1620 if (mouse_mode == MouseGain) {
1621 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1623 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredGainLine.get();
1624 if (is_drawable()) {
1625 set_canvas_cursor (_cursors->fader);
1630 case AutomationLineItem:
1631 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1633 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1635 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredAutomationLine.get();
1637 if (is_drawable()) {
1638 set_canvas_cursor (_cursors->fader);
1643 case RegionViewNameHighlight:
1644 if (is_drawable() && mouse_mode == MouseObject && entered_regionview) {
1645 set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1646 _over_region_trim_target = true;
1650 case LeftFrameHandle:
1651 case RightFrameHandle:
1652 if (is_drawable() && mouse_mode == MouseObject && !internal_editing() && entered_regionview) {
1653 set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1657 case StartSelectionTrimItem:
1658 case EndSelectionTrimItem:
1661 case ImageFrameHandleStartItem:
1662 case ImageFrameHandleEndItem:
1663 case MarkerViewHandleStartItem:
1664 case MarkerViewHandleEndItem:
1667 if (is_drawable()) {
1668 set_canvas_cursor (_cursors->trimmer);
1672 case PlayheadCursorItem:
1673 if (is_drawable()) {
1674 switch (_edit_point) {
1676 set_canvas_cursor (_cursors->grabber_edit_point);
1679 set_canvas_cursor (_cursors->grabber);
1685 case RegionViewName:
1687 /* when the name is not an active item, the entire name highlight is for trimming */
1689 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1690 if (mouse_mode == MouseObject && is_drawable()) {
1691 set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1692 _over_region_trim_target = true;
1698 case AutomationTrackItem:
1699 if (is_drawable()) {
1700 Gdk::Cursor *cursor;
1701 switch (mouse_mode) {
1703 cursor = _cursors->selector;
1706 cursor = _cursors->zoom_in;
1709 cursor = _cursors->cross_hair;
1713 set_canvas_cursor (cursor);
1715 AutomationTimeAxisView* atv;
1716 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1717 clear_entered_track = false;
1718 set_entered_track (atv);
1724 case RangeMarkerBarItem:
1725 case TransportMarkerBarItem:
1726 case CdMarkerBarItem:
1729 if (is_drawable()) {
1730 set_canvas_cursor (_cursors->timebar);
1735 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1738 entered_marker = marker;
1739 marker->set_color_rgba (ARDOUR_UI::config()->canvasvar_EnteredMarker.get());
1741 case MeterMarkerItem:
1742 case TempoMarkerItem:
1743 if (is_drawable()) {
1744 set_canvas_cursor (_cursors->timebar);
1748 case FadeInHandleItem:
1749 if (mouse_mode == MouseObject && !internal_editing()) {
1750 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1752 rect->property_fill_color_rgba() = 0xBBBBBBAA;
1754 set_canvas_cursor (_cursors->fade_in);
1758 case FadeOutHandleItem:
1759 if (mouse_mode == MouseObject && !internal_editing()) {
1760 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1762 rect->property_fill_color_rgba() = 0xBBBBBBAA;
1764 set_canvas_cursor (_cursors->fade_out);
1767 case FeatureLineItem:
1769 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1770 line->property_fill_color_rgba() = 0xFF0000FF;
1774 if (join_object_range_button.get_active()) {
1775 set_canvas_cursor ();
1783 /* second pass to handle entered track status in a comprehensible way.
1786 switch (item_type) {
1788 case AutomationLineItem:
1789 case ControlPointItem:
1790 /* these do not affect the current entered track state */
1791 clear_entered_track = false;
1794 case AutomationTrackItem:
1795 /* handled above already */
1799 set_entered_track (0);
1807 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent*, ItemType item_type)
1817 switch (item_type) {
1818 case ControlPointItem:
1819 cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
1820 if (cp->line().the_list()->interpolation() != AutomationList::Discrete) {
1821 if (cp->line().npoints() > 1 && !cp->get_selected()) {
1822 cp->set_visible (false);
1826 if (is_drawable()) {
1827 set_canvas_cursor (current_canvas_cursor);
1830 _verbose_cursor->hide ();
1833 case RegionViewNameHighlight:
1834 case LeftFrameHandle:
1835 case RightFrameHandle:
1836 case StartSelectionTrimItem:
1837 case EndSelectionTrimItem:
1838 case PlayheadCursorItem:
1841 case ImageFrameHandleStartItem:
1842 case ImageFrameHandleEndItem:
1843 case MarkerViewHandleStartItem:
1844 case MarkerViewHandleEndItem:
1847 _over_region_trim_target = false;
1849 if (is_drawable()) {
1850 set_canvas_cursor (current_canvas_cursor);
1855 case AutomationLineItem:
1856 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1858 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1860 line->property_fill_color_rgba() = al->get_line_color();
1862 if (is_drawable()) {
1863 set_canvas_cursor (current_canvas_cursor);
1867 case RegionViewName:
1868 /* see enter_handler() for notes */
1869 _over_region_trim_target = false;
1871 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1872 if (is_drawable() && mouse_mode == MouseObject) {
1873 set_canvas_cursor (current_canvas_cursor);
1878 case RangeMarkerBarItem:
1879 case TransportMarkerBarItem:
1880 case CdMarkerBarItem:
1884 if (is_drawable()) {
1885 set_canvas_cursor (current_canvas_cursor);
1890 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1894 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
1895 location_flags_changed (loc, this);
1898 case MeterMarkerItem:
1899 case TempoMarkerItem:
1901 if (is_drawable()) {
1902 set_canvas_cursor (_cursors->timebar);
1907 case FadeInHandleItem:
1908 case FadeOutHandleItem:
1909 rv = static_cast<RegionView*>(item->get_data ("regionview"));
1911 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1913 rect->property_fill_color_rgba() = rv->get_fill_color();
1914 rect->property_outline_pixels() = 0;
1917 set_canvas_cursor (current_canvas_cursor);
1920 case AutomationTrackItem:
1921 if (is_drawable()) {
1922 set_canvas_cursor (current_canvas_cursor);
1923 clear_entered_track = true;
1924 Glib::signal_idle().connect (sigc::mem_fun(*this, &Editor::left_automation_track));
1927 case FeatureLineItem:
1929 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1930 line->property_fill_color_rgba() = (guint) ARDOUR_UI::config()->canvasvar_ZeroLine.get();;
1942 Editor::left_automation_track ()
1944 if (clear_entered_track) {
1945 set_entered_track (0);
1946 clear_entered_track = false;
1952 Editor::scrub (framepos_t frame, double current_x)
1956 if (scrubbing_direction == 0) {
1958 _session->request_locate (frame, false);
1959 _session->request_transport_speed (0.1);
1960 scrubbing_direction = 1;
1964 if (last_scrub_x > current_x) {
1966 /* pointer moved to the left */
1968 if (scrubbing_direction > 0) {
1970 /* we reversed direction to go backwards */
1973 scrub_reverse_distance += (int) (last_scrub_x - current_x);
1977 /* still moving to the left (backwards) */
1979 scrub_reversals = 0;
1980 scrub_reverse_distance = 0;
1982 delta = 0.01 * (last_scrub_x - current_x);
1983 _session->request_transport_speed_nonzero (_session->transport_speed() - delta);
1987 /* pointer moved to the right */
1989 if (scrubbing_direction < 0) {
1990 /* we reversed direction to go forward */
1993 scrub_reverse_distance += (int) (current_x - last_scrub_x);
1996 /* still moving to the right */
1998 scrub_reversals = 0;
1999 scrub_reverse_distance = 0;
2001 delta = 0.01 * (current_x - last_scrub_x);
2002 _session->request_transport_speed_nonzero (_session->transport_speed() + delta);
2006 /* if there have been more than 2 opposite motion moves detected, or one that moves
2007 back more than 10 pixels, reverse direction
2010 if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
2012 if (scrubbing_direction > 0) {
2013 /* was forwards, go backwards */
2014 _session->request_transport_speed (-0.1);
2015 scrubbing_direction = -1;
2017 /* was backwards, go forwards */
2018 _session->request_transport_speed (0.1);
2019 scrubbing_direction = 1;
2022 scrub_reverse_distance = 0;
2023 scrub_reversals = 0;
2027 last_scrub_x = current_x;
2031 Editor::motion_handler (ArdourCanvas::Item* /*item*/, GdkEvent* event, bool from_autoscroll)
2033 _last_motion_y = event->motion.y;
2035 if (event->motion.is_hint) {
2038 /* We call this so that MOTION_NOTIFY events continue to be
2039 delivered to the canvas. We need to do this because we set
2040 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
2041 the density of the events, at the expense of a round-trip
2042 to the server. Given that this will mostly occur on cases
2043 where DISPLAY = :0.0, and given the cost of what the motion
2044 event might do, its a good tradeoff.
2047 track_canvas->get_pointer (x, y);
2050 if (current_stepping_trackview) {
2051 /* don't keep the persistent stepped trackview if the mouse moves */
2052 current_stepping_trackview = 0;
2053 step_timeout.disconnect ();
2056 if (_session && _session->actively_recording()) {
2057 /* Sorry. no dragging stuff around while we record */
2061 JoinObjectRangeState const old = _join_object_range_state;
2062 update_join_object_range_location (event->motion.x, event->motion.y);
2063 if (_join_object_range_state != old) {
2064 set_canvas_cursor ();
2067 if (_over_region_trim_target) {
2068 set_canvas_cursor_for_region_view (event->motion.x, entered_regionview);
2071 bool handled = false;
2072 if (_drags->active ()) {
2073 handled = _drags->motion_handler (event, from_autoscroll);
2080 track_canvas_motion (event);
2085 Editor::can_remove_control_point (ArdourCanvas::Item* item)
2087 ControlPoint* control_point;
2089 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2090 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2094 AutomationLine& line = control_point->line ();
2095 if (dynamic_cast<AudioRegionGainLine*> (&line)) {
2096 /* we shouldn't remove the first or last gain point in region gain lines */
2097 if (line.is_last_point(*control_point) || line.is_first_point(*control_point)) {
2106 Editor::remove_control_point (ArdourCanvas::Item* item)
2108 if (!can_remove_control_point (item)) {
2112 ControlPoint* control_point;
2114 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2115 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2119 control_point->line().remove_point (*control_point);
2123 Editor::edit_control_point (ArdourCanvas::Item* item)
2125 ControlPoint* p = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"));
2128 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2132 ControlPointDialog d (p);
2133 d.set_position (Gtk::WIN_POS_MOUSE);
2136 if (d.run () != RESPONSE_ACCEPT) {
2140 p->line().modify_point_y (*p, d.get_y_fraction ());
2144 Editor::edit_note (ArdourCanvas::Item* item)
2146 ArdourCanvas::CanvasNoteEvent* e = dynamic_cast<ArdourCanvas::CanvasNoteEvent*> (item);
2149 EditNoteDialog d (&e->region_view(), e);
2150 d.set_position (Gtk::WIN_POS_MOUSE);
2158 Editor::visible_order_range (int* low, int* high) const
2160 *low = TimeAxisView::max_order ();
2163 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
2165 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
2167 if (!rtv->hidden()) {
2169 if (*high < rtv->order()) {
2170 *high = rtv->order ();
2173 if (*low > rtv->order()) {
2174 *low = rtv->order ();
2181 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
2183 /* Either add to or set the set the region selection, unless
2184 this is an alignment click (control used)
2187 if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
2188 TimeAxisView* tv = &rv.get_time_axis_view();
2189 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
2191 if (rtv && rtv->is_track()) {
2192 speed = rtv->track()->speed();
2195 framepos_t where = get_preferred_edit_position();
2199 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
2201 align_region (rv.region(), SyncPoint, (framepos_t) (where * speed));
2203 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
2205 align_region (rv.region(), End, (framepos_t) (where * speed));
2209 align_region (rv.region(), Start, (framepos_t) (where * speed));
2216 Editor::collect_new_region_view (RegionView* rv)
2218 latest_regionviews.push_back (rv);
2222 Editor::collect_and_select_new_region_view (RegionView* rv)
2225 latest_regionviews.push_back (rv);
2229 Editor::cancel_selection ()
2231 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2232 (*i)->hide_selection ();
2235 selection->clear ();
2236 clicked_selection = 0;
2241 Editor::point_trim (GdkEvent* event, framepos_t new_bound)
2243 RegionView* rv = clicked_regionview;
2245 /* Choose action dependant on which button was pressed */
2246 switch (event->button.button) {
2248 begin_reversible_command (_("start point trim"));
2250 if (selection->selected (rv)) {
2251 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
2252 i != selection->regions.by_layer().end(); ++i)
2255 cerr << "region view contains null region" << endl;
2258 if (!(*i)->region()->locked()) {
2259 (*i)->region()->clear_changes ();
2260 (*i)->region()->trim_front (new_bound);
2261 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2266 if (!rv->region()->locked()) {
2267 rv->region()->clear_changes ();
2268 rv->region()->trim_front (new_bound);
2269 _session->add_command(new StatefulDiffCommand (rv->region()));
2273 commit_reversible_command();
2277 begin_reversible_command (_("End point trim"));
2279 if (selection->selected (rv)) {
2281 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
2283 if (!(*i)->region()->locked()) {
2284 (*i)->region()->clear_changes();
2285 (*i)->region()->trim_end (new_bound);
2286 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2292 if (!rv->region()->locked()) {
2293 rv->region()->clear_changes ();
2294 rv->region()->trim_end (new_bound);
2295 _session->add_command (new StatefulDiffCommand (rv->region()));
2299 commit_reversible_command();
2308 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* /*event*/)
2313 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2314 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2318 Location* location = find_location_from_marker (marker, is_start);
2319 location->set_hidden (true, this);
2324 Editor::reposition_zoom_rect (framepos_t start, framepos_t end)
2326 double x1 = frame_to_pixel (start);
2327 double x2 = frame_to_pixel (end);
2328 double y2 = full_canvas_height - 1.0;
2330 zoom_rect->property_x1() = x1;
2331 zoom_rect->property_y1() = 1.0;
2332 zoom_rect->property_x2() = x2;
2333 zoom_rect->property_y2() = y2;
2338 Editor::mouse_rename_region (ArdourCanvas::Item* /*item*/, GdkEvent* /*event*/)
2340 using namespace Gtkmm2ext;
2342 ArdourPrompter prompter (false);
2344 prompter.set_prompt (_("Name for region:"));
2345 prompter.set_initial_text (clicked_regionview->region()->name());
2346 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
2347 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
2348 prompter.show_all ();
2349 switch (prompter.run ()) {
2350 case Gtk::RESPONSE_ACCEPT:
2352 prompter.get_result(str);
2354 clicked_regionview->region()->set_name (str);
2363 Editor::mouse_brush_insert_region (RegionView* rv, framepos_t pos)
2365 /* no brushing without a useful snap setting */
2367 switch (_snap_mode) {
2369 return; /* can't work because it allows region to be placed anywhere */
2374 switch (_snap_type) {
2382 /* don't brush a copy over the original */
2384 if (pos == rv->region()->position()) {
2388 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
2390 if (rtv == 0 || !rtv->is_track()) {
2394 boost::shared_ptr<Playlist> playlist = rtv->playlist();
2395 double speed = rtv->track()->speed();
2397 playlist->clear_changes ();
2398 boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region(), true));
2399 playlist->add_region (new_region, (framepos_t) (pos * speed));
2400 _session->add_command (new StatefulDiffCommand (playlist));
2402 // playlist is frozen, so we have to update manually XXX this is disgusting
2404 playlist->RegionAdded (new_region); /* EMIT SIGNAL */
2408 Editor::track_height_step_timeout ()
2410 if (get_microseconds() - last_track_height_step_timestamp < 250000) {
2411 current_stepping_trackview = 0;
2418 Editor::add_region_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2420 assert (region_view);
2422 if (!region_view->region()->playlist()) {
2426 _region_motion_group->raise_to_top ();
2428 if (Config->get_edit_mode() == Splice) {
2429 _drags->add (new RegionSpliceDrag (this, item, region_view, selection->regions.by_layer()));
2431 RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id);
2432 _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), false, false));
2435 /* sync the canvas to what we think is its current state */
2436 update_canvas_now();
2440 Editor::add_region_copy_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2442 assert (region_view);
2444 if (!region_view->region()->playlist()) {
2448 _region_motion_group->raise_to_top ();
2450 RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id);
2451 _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), false, true));
2455 Editor::add_region_brush_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2457 assert (region_view);
2459 if (!region_view->region()->playlist()) {
2463 if (Config->get_edit_mode() == Splice) {
2467 RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id);
2468 _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), true, false));
2470 begin_reversible_command (Operations::drag_region_brush);
2473 /** Start a grab where a time range is selected, track(s) are selected, and the
2474 * user clicks and drags a region with a modifier in order to create a new region containing
2475 * the section of the clicked region that lies within the time range.
2478 Editor::start_selection_grab (ArdourCanvas::Item* /*item*/, GdkEvent* event)
2480 if (clicked_regionview == 0) {
2484 /* lets try to create new Region for the selection */
2486 vector<boost::shared_ptr<Region> > new_regions;
2487 create_region_from_selection (new_regions);
2489 if (new_regions.empty()) {
2493 /* XXX fix me one day to use all new regions */
2495 boost::shared_ptr<Region> region (new_regions.front());
2497 /* add it to the current stream/playlist.
2499 tricky: the streamview for the track will add a new regionview. we will
2500 catch the signal it sends when it creates the regionview to
2501 set the regionview we want to then drag.
2504 latest_regionviews.clear();
2505 sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
2507 /* A selection grab currently creates two undo/redo operations, one for
2508 creating the new region and another for moving it.
2511 begin_reversible_command (Operations::selection_grab);
2513 boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
2515 playlist->clear_changes ();
2516 clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
2517 _session->add_command(new StatefulDiffCommand (playlist));
2519 commit_reversible_command ();
2523 if (latest_regionviews.empty()) {
2524 /* something went wrong */
2528 /* we need to deselect all other regionviews, and select this one
2529 i'm ignoring undo stuff, because the region creation will take care of it
2531 selection->set (latest_regionviews);
2533 _drags->set (new RegionMoveDrag (this, latest_regionviews.front()->get_canvas_group(), latest_regionviews.front(), latest_regionviews, false, false), event);
2539 if (_drags->active ()) {
2542 selection->clear ();
2547 Editor::set_internal_edit (bool yn)
2549 _internal_editing = yn;
2552 mouse_select_button.set_image (*(manage (new Image (::get_icon("midi_tool_pencil")))));
2553 mouse_select_button.get_image ()->show ();
2554 ARDOUR_UI::instance()->set_tip (mouse_select_button, _("Draw/Edit MIDI Notes"));
2555 mouse_mode_toggled (mouse_mode);
2557 pre_internal_mouse_mode = mouse_mode;
2559 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2560 (*i)->enter_internal_edit_mode ();
2565 mouse_select_button.set_image (*(manage (new Image (::get_icon("tool_range")))));
2566 mouse_select_button.get_image ()->show ();
2567 ARDOUR_UI::instance()->set_tip (mouse_select_button, _("Select/Move Ranges"));
2568 mouse_mode_toggled (mouse_mode); // sets cursor
2570 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2571 (*i)->leave_internal_edit_mode ();
2574 if (mouse_mode == MouseRange && pre_internal_mouse_mode != MouseRange) {
2575 /* we were drawing .. flip back to something sensible */
2576 set_mouse_mode (pre_internal_mouse_mode);
2581 /** Update _join_object_range_state which indicate whether we are over the top or bottom half of a region view,
2582 * used by the `join object/range' tool mode.
2585 Editor::update_join_object_range_location (double /*x*/, double y)
2587 /* XXX: actually, this decides based on whether the mouse is in the top or bottom half of a RouteTimeAxisView;
2588 entered_{track,regionview} is not always setup (e.g. if the mouse is over a TimeSelection), and to get a Region
2589 that we're over requires searching the playlist.
2592 if (join_object_range_button.get_active() == false || (mouse_mode != MouseRange && mouse_mode != MouseObject)) {
2593 _join_object_range_state = JOIN_OBJECT_RANGE_NONE;
2597 if (mouse_mode == MouseObject) {
2598 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2599 } else if (mouse_mode == MouseRange) {
2600 _join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
2603 /* XXX: maybe we should make entered_track work in all cases, rather than resorting to this */
2604 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y + vertical_adjustment.get_value() - canvas_timebars_vsize);
2608 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
2613 rtv->canvas_display()->w2i (cx, cy);
2615 double const c = cy / rtv->view()->child_height();
2617 double const f = modf (c, &d);
2619 _join_object_range_state = f < 0.5 ? JOIN_OBJECT_RANGE_RANGE : JOIN_OBJECT_RANGE_OBJECT;
2625 Editor::effective_mouse_mode () const
2627 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
2629 } else if (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE) {
2637 Editor::remove_midi_note (ArdourCanvas::Item* item, GdkEvent *)
2639 ArdourCanvas::CanvasNoteEvent* e = dynamic_cast<ArdourCanvas::CanvasNoteEvent*> (item);
2642 e->region_view().delete_note (e->note ());
2646 Editor::set_canvas_cursor_for_region_view (double x, RegionView* rv)
2650 ArdourCanvas::Group* g = rv->get_canvas_group ();
2651 ArdourCanvas::Group* p = g->get_parent_group ();
2653 /* Compute x in region view parent coordinates */
2657 double x1, x2, y1, y2;
2658 g->get_bounds (x1, y1, x2, y2);
2660 /* Halfway across the region */
2661 double const h = (x1 + x2) / 2;
2663 Trimmable::CanTrim ct = rv->region()->can_trim ();
2665 if (ct & Trimmable::FrontTrimEarlier) {
2666 set_canvas_cursor (_cursors->left_side_trim);
2668 set_canvas_cursor (_cursors->left_side_trim_right_only);
2671 if (ct & Trimmable::EndTrimLater) {
2672 set_canvas_cursor (_cursors->right_side_trim);
2674 set_canvas_cursor (_cursors->right_side_trim_left_only);
2679 /** Obtain the pointer position in world coordinates */
2681 Editor::get_pointer_position (double& x, double& y) const
2684 track_canvas->get_pointer (px, py);
2685 track_canvas->window_to_world (px, py, x, y);