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"
64 #include "ardour/types.h"
65 #include "ardour/profile.h"
66 #include "ardour/route.h"
67 #include "ardour/audio_track.h"
68 #include "ardour/audio_diskstream.h"
69 #include "ardour/midi_diskstream.h"
70 #include "ardour/playlist.h"
71 #include "ardour/audioplaylist.h"
72 #include "ardour/audioregion.h"
73 #include "ardour/midi_region.h"
74 #include "ardour/dB.h"
75 #include "ardour/utils.h"
76 #include "ardour/region_factory.h"
77 #include "ardour/source_factory.h"
78 #include "ardour/session.h"
79 #include "ardour/operations.h"
86 using namespace ARDOUR;
89 using namespace Editing;
90 using Gtkmm2ext::Keyboard;
93 Editor::mouse_frame (framepos_t& where, bool& in_track_canvas) const
97 Gdk::ModifierType mask;
98 Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->track_canvas->get_window();
99 Glib::RefPtr<const Gdk::Window> pointer_window;
101 if (!canvas_window) {
105 pointer_window = canvas_window->get_pointer (x, y, mask);
107 if (pointer_window == track_canvas->get_bin_window()) {
110 in_track_canvas = true;
113 in_track_canvas = false;
118 event.type = GDK_BUTTON_RELEASE;
122 where = event_frame (&event, 0, 0);
127 Editor::event_frame (GdkEvent const * event, double* pcx, double* pcy) const
141 switch (event->type) {
142 case GDK_BUTTON_RELEASE:
143 case GDK_BUTTON_PRESS:
144 case GDK_2BUTTON_PRESS:
145 case GDK_3BUTTON_PRESS:
146 *pcx = event->button.x;
147 *pcy = event->button.y;
148 _trackview_group->w2i(*pcx, *pcy);
150 case GDK_MOTION_NOTIFY:
151 *pcx = event->motion.x;
152 *pcy = event->motion.y;
153 _trackview_group->w2i(*pcx, *pcy);
155 case GDK_ENTER_NOTIFY:
156 case GDK_LEAVE_NOTIFY:
157 track_canvas->w2c(event->crossing.x, event->crossing.y, *pcx, *pcy);
160 case GDK_KEY_RELEASE:
161 // track_canvas->w2c(event->key.x, event->key.y, *pcx, *pcy);
164 warning << string_compose (_("Editor::event_frame() used on unhandled event type %1"), event->type) << endmsg;
168 /* note that pixel_to_frame() never returns less than zero, so even if the pixel
169 position is negative (as can be the case with motion events in particular),
170 the frame location is always positive.
173 return pixel_to_frame (*pcx);
177 Editor::which_grabber_cursor ()
179 Gdk::Cursor* c = _cursors->grabber;
181 if (_internal_editing) {
182 switch (mouse_mode) {
184 c = _cursors->midi_pencil;
188 c = _cursors->grabber_note;
192 c = _cursors->midi_resize;
201 switch (_edit_point) {
203 c = _cursors->grabber_edit_point;
206 boost::shared_ptr<Movable> m = _movable.lock();
207 if (m && m->locked()) {
208 c = _cursors->speaker;
218 Editor::set_current_trimmable (boost::shared_ptr<Trimmable> t)
220 boost::shared_ptr<Trimmable> st = _trimmable.lock();
222 if (!st || st == t) {
224 set_canvas_cursor ();
229 Editor::set_current_movable (boost::shared_ptr<Movable> m)
231 boost::shared_ptr<Movable> sm = _movable.lock();
233 if (!sm || sm != m) {
235 set_canvas_cursor ();
240 Editor::set_canvas_cursor ()
242 if (_internal_editing) {
244 switch (mouse_mode) {
246 current_canvas_cursor = _cursors->midi_pencil;
250 current_canvas_cursor = which_grabber_cursor();
254 current_canvas_cursor = _cursors->midi_resize;
263 switch (mouse_mode) {
265 current_canvas_cursor = _cursors->selector;
269 current_canvas_cursor = which_grabber_cursor();
273 current_canvas_cursor = _cursors->cross_hair;
277 if (Keyboard::the_keyboard().key_is_down (GDK_Control_L)) {
278 current_canvas_cursor = _cursors->zoom_out;
280 current_canvas_cursor = _cursors->zoom_in;
285 current_canvas_cursor = _cursors->time_fx; // just use playhead
289 current_canvas_cursor = _cursors->speaker;
294 switch (_join_object_range_state) {
295 case JOIN_OBJECT_RANGE_NONE:
297 case JOIN_OBJECT_RANGE_OBJECT:
298 current_canvas_cursor = which_grabber_cursor ();
300 case JOIN_OBJECT_RANGE_RANGE:
301 current_canvas_cursor = _cursors->selector;
305 /* up-down cursor as a cue that automation can be dragged up and down when in join object/range mode */
306 if (join_object_range_button.get_active() && last_item_entered) {
307 if (last_item_entered->property_parent() && (*last_item_entered->property_parent()).get_data (X_("timeselection"))) {
308 pair<TimeAxisView*, int> tvp = trackview_by_y_position (_last_motion_y + vertical_adjustment.get_value() - canvas_timebars_vsize);
309 if (dynamic_cast<AutomationTimeAxisView*> (tvp.first)) {
310 current_canvas_cursor = _cursors->up_down;
315 set_canvas_cursor (current_canvas_cursor, true);
319 Editor::set_mouse_mode (MouseMode m, bool force)
321 if (_drags->active ()) {
325 if (!force && m == mouse_mode) {
329 Glib::RefPtr<Action> act;
333 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-range"));
337 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-object"));
341 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-gain"));
345 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-zoom"));
349 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-timefx"));
353 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-audition"));
359 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
362 /* go there and back to ensure that the toggled handler is called to set up mouse_mode */
363 tact->set_active (false);
364 tact->set_active (true);
366 MouseModeChanged (); /* EMIT SIGNAL */
370 Editor::mouse_mode_toggled (MouseMode m)
376 if (!internal_editing()) {
377 if (mouse_mode != MouseRange && _join_object_range_state == JOIN_OBJECT_RANGE_NONE) {
379 /* in all modes except range and joined object/range, hide the range selection,
380 show the object (region) selection.
383 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
384 (*i)->hide_selection ();
390 in range or object/range mode, show the range selection.
393 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
394 (*i)->show_selection (selection->time);
399 set_canvas_cursor ();
401 MouseModeChanged (); /* EMIT SIGNAL */
405 Editor::step_mouse_mode (bool next)
407 switch (current_mouse_mode()) {
410 if (Profile->get_sae()) {
411 set_mouse_mode (MouseZoom);
413 set_mouse_mode (MouseRange);
416 set_mouse_mode (MouseTimeFX);
421 if (next) set_mouse_mode (MouseZoom);
422 else set_mouse_mode (MouseObject);
427 if (Profile->get_sae()) {
428 set_mouse_mode (MouseTimeFX);
430 set_mouse_mode (MouseGain);
433 if (Profile->get_sae()) {
434 set_mouse_mode (MouseObject);
436 set_mouse_mode (MouseRange);
442 if (next) set_mouse_mode (MouseTimeFX);
443 else set_mouse_mode (MouseZoom);
448 set_mouse_mode (MouseAudition);
450 if (Profile->get_sae()) {
451 set_mouse_mode (MouseZoom);
453 set_mouse_mode (MouseGain);
459 if (next) set_mouse_mode (MouseObject);
460 else set_mouse_mode (MouseTimeFX);
466 Editor::button_selection (ArdourCanvas::Item* /*item*/, GdkEvent* event, ItemType item_type)
468 /* in object/audition/timefx/gain-automation mode,
469 any button press sets the selection if the object
470 can be selected. this is a bit of hack, because
471 we want to avoid this if the mouse operation is a
474 note: not dbl-click or triple-click
476 Also note that there is no region selection in internal edit mode, otherwise
477 for operations operating on the selection (e.g. cut) it is not obvious whether
478 to cut notes or regions.
481 if (((mouse_mode != MouseObject) &&
482 (_join_object_range_state != JOIN_OBJECT_RANGE_OBJECT) &&
483 (mouse_mode != MouseAudition || item_type != RegionItem) &&
484 (mouse_mode != MouseTimeFX || item_type != RegionItem) &&
485 (mouse_mode != MouseGain) &&
486 (mouse_mode != MouseRange)) ||
487 ((event->type != GDK_BUTTON_PRESS && event->type != GDK_BUTTON_RELEASE) || event->button.button > 3) ||
488 internal_editing()) {
493 if (event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) {
495 if ((event->button.state & Keyboard::RelevantModifierKeyMask) && event->button.button != 1) {
497 /* almost no selection action on modified button-2 or button-3 events */
499 if (item_type != RegionItem && event->button.button != 2) {
505 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
506 bool press = (event->type == GDK_BUTTON_PRESS);
510 if (mouse_mode != MouseRange || _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
511 set_selected_regionview_from_click (press, op, true);
512 } else if (event->type == GDK_BUTTON_PRESS) {
513 selection->clear_tracks ();
514 set_selected_track_as_side_effect (op, true);
516 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT && !selection->regions.empty()) {
517 clicked_selection = select_range_around_region (selection->regions.front());
521 case RegionViewNameHighlight:
523 case LeftFrameHandle:
524 case RightFrameHandle:
525 if (mouse_mode != MouseRange || _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
526 set_selected_regionview_from_click (press, op, true);
527 } else if (event->type == GDK_BUTTON_PRESS) {
528 set_selected_track_as_side_effect (op);
533 case FadeInHandleItem:
535 case FadeOutHandleItem:
537 if (mouse_mode != MouseRange || _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
538 set_selected_regionview_from_click (press, op, true);
539 } else if (event->type == GDK_BUTTON_PRESS) {
540 set_selected_track_as_side_effect (op);
544 case ControlPointItem:
545 set_selected_track_as_side_effect (op, true);
546 if (mouse_mode != MouseRange || _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
547 set_selected_control_point_from_click (op, false);
552 /* for context click, select track */
553 if (event->button.button == 3) {
554 selection->clear_tracks ();
555 set_selected_track_as_side_effect (op, true);
559 case AutomationTrackItem:
560 set_selected_track_as_side_effect (op, true);
569 Editor::button_press_handler_1 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
571 /* single mouse clicks on any of these item types operate
572 independent of mouse mode, mostly because they are
573 not on the main track canvas or because we want
578 case PlayheadCursorItem:
579 _drags->set (new CursorDrag (this, item, true), event);
583 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
584 hide_marker (item, event);
586 _drags->set (new MarkerDrag (this, item), event);
590 case TempoMarkerItem:
592 new TempoMarkerDrag (
595 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
601 case MeterMarkerItem:
603 new MeterMarkerDrag (
606 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
615 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
616 _drags->set (new CursorDrag (this, &playhead_cursor->canvas_item, false), event);
622 case RangeMarkerBarItem:
623 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
624 _drags->set (new CursorDrag (this, &playhead_cursor->canvas_item, false), event);
626 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateRangeMarker), event);
631 case CdMarkerBarItem:
632 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
633 _drags->set (new CursorDrag (this, &playhead_cursor->canvas_item, false), event);
635 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateCDMarker), event);
640 case TransportMarkerBarItem:
641 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
642 _drags->set (new CursorDrag (this, &playhead_cursor->canvas_item, false), event);
644 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateTransportMarker), event);
653 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
654 /* special case: allow trim of range selections in joined object mode;
655 in theory eff should equal MouseRange in this case, but it doesn't
656 because entering the range selection canvas item results in entered_regionview
657 being set to 0, so update_join_object_range_location acts as if we aren't
660 if (item_type == StartSelectionTrimItem) {
661 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
662 } else if (item_type == EndSelectionTrimItem) {
663 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
667 Editing::MouseMode eff = effective_mouse_mode ();
669 /* special case: allow drag of region fade in/out in object mode with join object/range enabled */
670 if (item_type == FadeInHandleItem || item_type == FadeOutHandleItem) {
677 case StartSelectionTrimItem:
678 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
681 case EndSelectionTrimItem:
682 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
686 if (Keyboard::modifier_state_contains
687 (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier))) {
688 // contains and not equals because I can't use alt as a modifier alone.
689 start_selection_grab (item, event);
690 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
691 /* grab selection for moving */
692 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionMove), event);
694 double const y = event->button.y + vertical_adjustment.get_value() - canvas_timebars_vsize;
695 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
697 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
698 if (join_object_range_button.get_active() && atv) {
699 /* smart "join" mode: drag automation */
700 _drags->set (new AutomationRangeDrag (this, atv->base_item(), selection->time), event, _cursors->up_down);
702 /* this was debated, but decided the more common action was to
703 make a new selection */
704 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
711 if (internal_editing()) {
712 /* trim notes if we're in internal edit mode and near the ends of the note */
713 ArdourCanvas::CanvasNote* cn = dynamic_cast<ArdourCanvas::CanvasNote*> (item);
714 if (cn->big_enough_to_trim() && cn->mouse_near_ends()) {
715 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
717 _drags->set (new NoteDrag (this, item), event);
723 if (internal_editing()) {
724 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
725 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
729 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
734 case RegionViewNameHighlight:
735 if (!clicked_regionview->region()->locked()) {
736 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
737 _drags->set (new TrimDrag (this, item, clicked_regionview, s.by_layer()), event);
742 case LeftFrameHandle:
743 case RightFrameHandle:
744 if (!internal_editing() && !clicked_regionview->region()->locked()) {
745 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
746 _drags->set (new TrimDrag (this, item, clicked_regionview, s.by_layer()), event);
752 if (!internal_editing()) {
753 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
762 if (internal_editing()) {
763 ArdourCanvas::CanvasNoteEvent* cn = dynamic_cast<ArdourCanvas::CanvasNoteEvent*> (item);
764 if (cn->mouse_near_ends()) {
765 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
767 _drags->set (new NoteDrag (this, item), event);
777 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)) &&
778 event->type == GDK_BUTTON_PRESS) {
780 _drags->set (new RubberbandSelectDrag (this, item), event);
782 } else if (event->type == GDK_BUTTON_PRESS) {
785 case FadeInHandleItem:
787 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
788 _drags->set (new FadeInDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), s), event, _cursors->fade_in);
792 case FadeOutHandleItem:
794 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
795 _drags->set (new FadeOutDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), s), event, _cursors->fade_out);
799 case FeatureLineItem:
801 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier)) {
802 remove_transient(item);
806 _drags->set (new FeatureLineDrag (this, item), event);
812 if (dynamic_cast<AutomationRegionView*> (clicked_regionview)) {
813 /* click on an automation region view; do nothing here and let the ARV's signal handler
819 if (internal_editing ()) {
820 /* no region drags in internal edit mode */
824 /* click on a normal region view */
825 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
826 add_region_copy_drag (item, event, clicked_regionview);
827 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
828 add_region_brush_drag (item, event, clicked_regionview);
830 add_region_drag (item, event, clicked_regionview);
833 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT && !selection->regions.empty()) {
834 _drags->add (new SelectionDrag (this, clicked_axisview->get_selection_rect (clicked_selection)->rect, SelectionDrag::SelectionMove));
837 _drags->start_grab (event);
840 case RegionViewNameHighlight:
841 case LeftFrameHandle:
842 case RightFrameHandle:
843 if (!clicked_regionview->region()->locked()) {
844 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
845 _drags->set (new TrimDrag (this, item, clicked_regionview, s.by_layer()), event);
852 /* rename happens on edit clicks */
853 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
854 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, s.by_layer()), event);
859 case ControlPointItem:
860 _drags->set (new ControlPointDrag (this, item), event);
864 case AutomationLineItem:
865 _drags->set (new LineDrag (this, item), event);
870 if (internal_editing()) {
871 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
872 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
876 _drags->set (new RubberbandSelectDrag (this, item), event);
880 case AutomationTrackItem:
881 /* rubberband drag to select automation points */
882 _drags->set (new RubberbandSelectDrag (this, item), event);
887 if (join_object_range_button.get_active()) {
888 /* we're in "smart" joined mode, and we've clicked on a Selection */
889 double const y = event->button.y + vertical_adjustment.get_value() - canvas_timebars_vsize;
890 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
892 /* if we're over an automation track, start a drag of its data */
893 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
895 _drags->set (new AutomationRangeDrag (this, atv->base_item(), selection->time), event, _cursors->up_down);
898 /* if we're over a track and a region, and in the `object' part of a region,
899 put a selection around the region and drag both
901 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
902 if (rtv && _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
903 boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track> (rtv->route ());
905 boost::shared_ptr<Playlist> pl = t->playlist ();
908 boost::shared_ptr<Region> r = pl->top_region_at (event_frame (event));
910 RegionView* rv = rtv->view()->find_view (r);
911 clicked_selection = select_range_around_region (rv);
912 _drags->add (new SelectionDrag (this, item, SelectionDrag::SelectionMove));
913 list<RegionView*> rvs;
915 _drags->add (new RegionMoveDrag (this, item, rv, rvs, false, false));
916 _drags->start_grab (event);
927 case ImageFrameHandleStartItem:
928 imageframe_start_handle_op(item, event) ;
931 case ImageFrameHandleEndItem:
932 imageframe_end_handle_op(item, event) ;
935 case MarkerViewHandleStartItem:
936 markerview_item_start_handle_op(item, event) ;
939 case MarkerViewHandleEndItem:
940 markerview_item_end_handle_op(item, event) ;
944 start_markerview_grab(item, event) ;
947 start_imageframe_grab(item, event) ;
965 /* start a grab so that if we finish after moving
966 we can tell what happened.
968 _drags->set (new RegionGainDrag (this, item), event, current_canvas_cursor);
972 _drags->set (new LineDrag (this, item), event);
975 case ControlPointItem:
976 _drags->set (new ControlPointDrag (this, item), event);
987 case ControlPointItem:
988 _drags->set (new ControlPointDrag (this, item), event);
991 case AutomationLineItem:
992 _drags->set (new LineDrag (this, item), event);
996 // XXX need automation mode to identify which
998 // start_line_grab_from_regionview (item, event);
1008 if (event->type == GDK_BUTTON_PRESS) {
1009 _drags->set (new MouseZoomDrag (this, item), event);
1016 if (internal_editing() && item_type == NoteItem) {
1017 /* drag notes if we're in internal edit mode */
1018 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
1020 } else if ((!internal_editing() || dynamic_cast<AudioRegionView*> (clicked_regionview)) && clicked_regionview) {
1021 /* do time-FX if we're not in internal edit mode, or we are but we clicked on an audio region */
1022 _drags->set (new TimeFXDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1028 _drags->set (new ScrubDrag (this, item), event);
1029 scrub_reversals = 0;
1030 scrub_reverse_distance = 0;
1031 last_scrub_x = event->button.x;
1032 scrubbing_direction = 0;
1033 set_canvas_cursor (_cursors->transparent);
1045 Editor::button_press_handler_2 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1047 Editing::MouseMode const eff = effective_mouse_mode ();
1050 switch (item_type) {
1052 if (internal_editing ()) {
1053 /* no region drags in internal edit mode */
1057 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
1058 add_region_copy_drag (item, event, clicked_regionview);
1060 add_region_drag (item, event, clicked_regionview);
1062 _drags->start_grab (event);
1065 case ControlPointItem:
1066 _drags->set (new ControlPointDrag (this, item), event);
1074 switch (item_type) {
1075 case RegionViewNameHighlight:
1076 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1080 case LeftFrameHandle:
1081 case RightFrameHandle:
1082 if (!internal_editing ()) {
1083 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1088 case RegionViewName:
1089 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer()), event);
1100 /* relax till release */
1106 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1107 temporal_zoom_to_frame (false, event_frame (event));
1109 temporal_zoom_to_frame (true, event_frame(event));
1122 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1124 if (event->type != GDK_BUTTON_PRESS) {
1128 Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->track_canvas->get_window();
1130 if (canvas_window) {
1131 Glib::RefPtr<const Gdk::Window> pointer_window;
1134 Gdk::ModifierType mask;
1136 pointer_window = canvas_window->get_pointer (x, y, mask);
1138 if (pointer_window == track_canvas->get_bin_window()) {
1139 track_canvas->window_to_world (x, y, wx, wy);
1143 pre_press_cursor = current_canvas_cursor;
1145 track_canvas->grab_focus();
1147 if (_session && _session->actively_recording()) {
1151 button_selection (item, event, item_type);
1153 if (!_drags->active () &&
1154 (Keyboard::is_delete_event (&event->button) ||
1155 Keyboard::is_context_menu_event (&event->button) ||
1156 Keyboard::is_edit_event (&event->button))) {
1158 /* handled by button release */
1162 switch (event->button.button) {
1164 return button_press_handler_1 (item, event, item_type);
1168 return button_press_handler_2 (item, event, item_type);
1175 return button_press_dispatch (&event->button);
1184 Editor::button_press_dispatch (GdkEventButton* ev)
1186 /* this function is intended only for buttons 4 and above.
1189 Gtkmm2ext::MouseButton b (ev->state, ev->button);
1190 return button_bindings->activate (b, Gtkmm2ext::Bindings::Press);
1194 Editor::button_release_dispatch (GdkEventButton* ev)
1196 /* this function is intended only for buttons 4 and above.
1199 Gtkmm2ext::MouseButton b (ev->state, ev->button);
1200 return button_bindings->activate (b, Gtkmm2ext::Bindings::Release);
1204 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1206 framepos_t where = event_frame (event, 0, 0);
1207 AutomationTimeAxisView* atv = 0;
1209 if (pre_press_cursor) {
1210 set_canvas_cursor (pre_press_cursor);
1211 pre_press_cursor = 0;
1214 /* no action if we're recording */
1216 if (_session && _session->actively_recording()) {
1220 /* see if we're finishing a drag */
1222 bool were_dragging = false;
1223 if (_drags->active ()) {
1224 bool const r = _drags->end_grab (event);
1226 /* grab dragged, so do nothing else */
1230 were_dragging = true;
1233 update_region_layering_order_editor ();
1235 /* edit events get handled here */
1237 if (!_drags->active () && Keyboard::is_edit_event (&event->button)) {
1238 switch (item_type) {
1240 show_region_properties ();
1243 case TempoMarkerItem:
1244 edit_tempo_marker (item);
1247 case MeterMarkerItem:
1248 edit_meter_marker (item);
1251 case RegionViewName:
1252 if (clicked_regionview->name_active()) {
1253 return mouse_rename_region (item, event);
1257 case ControlPointItem:
1258 edit_control_point (item);
1271 /* context menu events get handled here */
1273 if (Keyboard::is_context_menu_event (&event->button)) {
1275 if (!_drags->active ()) {
1277 /* no matter which button pops up the context menu, tell the menu
1278 widget to use button 1 to drive menu selection.
1281 switch (item_type) {
1283 case FadeInHandleItem:
1285 case FadeOutHandleItem:
1286 popup_fade_context_menu (1, event->button.time, item, item_type);
1290 popup_track_context_menu (1, event->button.time, item_type, false);
1294 case RegionViewNameHighlight:
1295 case LeftFrameHandle:
1296 case RightFrameHandle:
1297 case RegionViewName:
1298 popup_track_context_menu (1, event->button.time, item_type, false);
1302 popup_track_context_menu (1, event->button.time, item_type, true);
1305 case AutomationTrackItem:
1306 popup_track_context_menu (1, event->button.time, item_type, false);
1310 case RangeMarkerBarItem:
1311 case TransportMarkerBarItem:
1312 case CdMarkerBarItem:
1315 popup_ruler_menu (where, item_type);
1319 marker_context_menu (&event->button, item);
1322 case TempoMarkerItem:
1323 tempo_or_meter_marker_context_menu (&event->button, item);
1326 case MeterMarkerItem:
1327 tempo_or_meter_marker_context_menu (&event->button, item);
1330 case CrossfadeViewItem:
1331 popup_track_context_menu (1, event->button.time, item_type, false);
1335 case ImageFrameItem:
1336 popup_imageframe_edit_menu(1, event->button.time, item, true) ;
1338 case ImageFrameTimeAxisItem:
1339 popup_imageframe_edit_menu(1, event->button.time, item, false) ;
1341 case MarkerViewItem:
1342 popup_marker_time_axis_edit_menu(1, event->button.time, item, true) ;
1344 case MarkerTimeAxisItem:
1345 popup_marker_time_axis_edit_menu(1, event->button.time, item, false) ;
1357 /* delete events get handled here */
1359 Editing::MouseMode const eff = effective_mouse_mode ();
1361 if (!_drags->active () && Keyboard::is_delete_event (&event->button)) {
1363 switch (item_type) {
1364 case TempoMarkerItem:
1365 remove_tempo_marker (item);
1368 case MeterMarkerItem:
1369 remove_meter_marker (item);
1373 remove_marker (*item, event);
1377 if (eff == MouseObject) {
1378 remove_clicked_region ();
1382 case ControlPointItem:
1383 if (eff == MouseGain) {
1384 remove_gain_control_point (item, event);
1386 remove_control_point (item, event);
1391 remove_midi_note (item, event);
1400 switch (event->button.button) {
1403 switch (item_type) {
1404 /* see comments in button_press_handler */
1405 case PlayheadCursorItem:
1408 case AutomationLineItem:
1409 case StartSelectionTrimItem:
1410 case EndSelectionTrimItem:
1414 if (!_dragging_playhead) {
1415 snap_to_with_modifier (where, event, 0, true);
1416 mouse_add_new_marker (where);
1420 case CdMarkerBarItem:
1421 if (!_dragging_playhead) {
1422 // if we get here then a dragged range wasn't done
1423 snap_to_with_modifier (where, event, 0, true);
1424 mouse_add_new_marker (where, true);
1429 if (!_dragging_playhead) {
1430 snap_to_with_modifier (where, event);
1431 mouse_add_new_tempo_event (where);
1436 if (!_dragging_playhead) {
1437 mouse_add_new_meter_event (pixel_to_frame (event->button.x));
1448 switch (item_type) {
1449 case AutomationTrackItem:
1450 atv = dynamic_cast<AutomationTimeAxisView*>(clicked_axisview);
1452 atv->add_automation_event (item, event, where, event->button.y);
1463 switch (item_type) {
1466 /* check that we didn't drag before releasing, since
1467 its really annoying to create new control
1468 points when doing this.
1470 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (clicked_regionview);
1471 if (were_dragging && arv) {
1472 arv->add_gain_point_event (item, event);
1478 case AutomationTrackItem:
1479 dynamic_cast<AutomationTimeAxisView*>(clicked_axisview)->
1480 add_automation_event (item, event, where, event->button.y);
1489 set_canvas_cursor (current_canvas_cursor);
1490 if (scrubbing_direction == 0) {
1491 /* no drag, just a click */
1492 switch (item_type) {
1494 play_selected_region ();
1500 /* make sure we stop */
1501 _session->request_transport_speed (0.0);
1510 /* do any (de)selection operations that should occur on button release */
1511 button_selection (item, event, item_type);
1520 switch (item_type) {
1522 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1524 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier|Keyboard::SecondaryModifier))) {
1527 // Button2 click is unused
1540 // x_style_paste (where, 1.0);
1561 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1568 last_item_entered = item;
1570 switch (item_type) {
1571 case ControlPointItem:
1572 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1573 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1574 cp->set_visible (true);
1578 at_y = cp->get_y ();
1579 cp->i2w (at_x, at_y);
1583 fraction = 1.0 - (cp->get_y() / cp->line().height());
1585 if (is_drawable() && !_drags->active ()) {
1586 set_canvas_cursor (_cursors->fader);
1589 set_verbose_canvas_cursor (cp->line().get_verbose_cursor_string (fraction), at_x, at_y);
1590 show_verbose_canvas_cursor ();
1595 if (mouse_mode == MouseGain) {
1596 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1598 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredGainLine.get();
1599 if (is_drawable()) {
1600 set_canvas_cursor (_cursors->fader);
1605 case AutomationLineItem:
1606 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1608 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1610 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredAutomationLine.get();
1612 if (is_drawable()) {
1613 set_canvas_cursor (_cursors->fader);
1618 case RegionViewNameHighlight:
1619 if (is_drawable() && mouse_mode == MouseObject && entered_regionview) {
1620 set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1621 _over_region_trim_target = true;
1625 case LeftFrameHandle:
1626 case RightFrameHandle:
1627 if (is_drawable() && mouse_mode == MouseObject && !internal_editing() && entered_regionview) {
1628 set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1632 case StartSelectionTrimItem:
1633 case EndSelectionTrimItem:
1636 case ImageFrameHandleStartItem:
1637 case ImageFrameHandleEndItem:
1638 case MarkerViewHandleStartItem:
1639 case MarkerViewHandleEndItem:
1642 if (is_drawable()) {
1643 set_canvas_cursor (_cursors->trimmer);
1647 case PlayheadCursorItem:
1648 if (is_drawable()) {
1649 switch (_edit_point) {
1651 set_canvas_cursor (_cursors->grabber_edit_point);
1654 set_canvas_cursor (_cursors->grabber);
1660 case RegionViewName:
1662 /* when the name is not an active item, the entire name highlight is for trimming */
1664 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1665 if (mouse_mode == MouseObject && is_drawable()) {
1666 set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1667 _over_region_trim_target = true;
1673 case AutomationTrackItem:
1674 if (is_drawable()) {
1675 Gdk::Cursor *cursor;
1676 switch (mouse_mode) {
1678 cursor = _cursors->selector;
1681 cursor = _cursors->zoom_in;
1684 cursor = _cursors->cross_hair;
1688 set_canvas_cursor (cursor);
1690 AutomationTimeAxisView* atv;
1691 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1692 clear_entered_track = false;
1693 set_entered_track (atv);
1699 case RangeMarkerBarItem:
1700 case TransportMarkerBarItem:
1701 case CdMarkerBarItem:
1704 if (is_drawable()) {
1705 set_canvas_cursor (_cursors->timebar);
1710 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1713 entered_marker = marker;
1714 marker->set_color_rgba (ARDOUR_UI::config()->canvasvar_EnteredMarker.get());
1716 case MeterMarkerItem:
1717 case TempoMarkerItem:
1718 if (is_drawable()) {
1719 set_canvas_cursor (_cursors->timebar);
1723 case FadeInHandleItem:
1724 if (mouse_mode == MouseObject && !internal_editing()) {
1725 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1727 rect->property_fill_color_rgba() = 0xBBBBBBAA;
1729 set_canvas_cursor (_cursors->fade_in);
1733 case FadeOutHandleItem:
1734 if (mouse_mode == MouseObject && !internal_editing()) {
1735 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1737 rect->property_fill_color_rgba() = 0xBBBBBBAA;
1739 set_canvas_cursor (_cursors->fade_out);
1742 case FeatureLineItem:
1744 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1745 line->property_fill_color_rgba() = 0xFF0000FF;
1749 if (join_object_range_button.get_active()) {
1750 set_canvas_cursor ();
1758 /* second pass to handle entered track status in a comprehensible way.
1761 switch (item_type) {
1763 case AutomationLineItem:
1764 case ControlPointItem:
1765 /* these do not affect the current entered track state */
1766 clear_entered_track = false;
1769 case AutomationTrackItem:
1770 /* handled above already */
1774 set_entered_track (0);
1782 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1792 switch (item_type) {
1793 case ControlPointItem:
1794 cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
1795 if (cp->line().the_list()->interpolation() != AutomationList::Discrete) {
1796 if (cp->line().npoints() > 1 && !cp->get_selected()) {
1797 cp->set_visible (false);
1801 if (is_drawable()) {
1802 set_canvas_cursor (current_canvas_cursor);
1805 hide_verbose_canvas_cursor ();
1808 case RegionViewNameHighlight:
1809 case LeftFrameHandle:
1810 case RightFrameHandle:
1811 case StartSelectionTrimItem:
1812 case EndSelectionTrimItem:
1813 case PlayheadCursorItem:
1816 case ImageFrameHandleStartItem:
1817 case ImageFrameHandleEndItem:
1818 case MarkerViewHandleStartItem:
1819 case MarkerViewHandleEndItem:
1822 _over_region_trim_target = false;
1824 if (is_drawable()) {
1825 set_canvas_cursor (current_canvas_cursor);
1830 case AutomationLineItem:
1831 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1833 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1835 line->property_fill_color_rgba() = al->get_line_color();
1837 if (is_drawable()) {
1838 set_canvas_cursor (current_canvas_cursor);
1842 case RegionViewName:
1843 /* see enter_handler() for notes */
1844 _over_region_trim_target = false;
1846 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1847 if (is_drawable() && mouse_mode == MouseObject) {
1848 set_canvas_cursor (current_canvas_cursor);
1853 case RangeMarkerBarItem:
1854 case TransportMarkerBarItem:
1855 case CdMarkerBarItem:
1859 if (is_drawable()) {
1860 set_canvas_cursor (current_canvas_cursor);
1865 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1869 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
1870 location_flags_changed (loc, this);
1873 case MeterMarkerItem:
1874 case TempoMarkerItem:
1876 if (is_drawable()) {
1877 set_canvas_cursor (_cursors->timebar);
1882 case FadeInHandleItem:
1883 case FadeOutHandleItem:
1884 rv = static_cast<RegionView*>(item->get_data ("regionview"));
1886 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1888 rect->property_fill_color_rgba() = rv->get_fill_color();
1889 rect->property_outline_pixels() = 0;
1892 set_canvas_cursor (current_canvas_cursor);
1895 case AutomationTrackItem:
1896 if (is_drawable()) {
1897 set_canvas_cursor (current_canvas_cursor);
1898 clear_entered_track = true;
1899 Glib::signal_idle().connect (sigc::mem_fun(*this, &Editor::left_automation_track));
1902 case FeatureLineItem:
1904 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1905 line->property_fill_color_rgba() = (guint) ARDOUR_UI::config()->canvasvar_ZeroLine.get();;
1917 Editor::left_automation_track ()
1919 if (clear_entered_track) {
1920 set_entered_track (0);
1921 clear_entered_track = false;
1927 Editor::scrub (framepos_t frame, double current_x)
1931 if (scrubbing_direction == 0) {
1933 _session->request_locate (frame, false);
1934 _session->request_transport_speed (0.1);
1935 scrubbing_direction = 1;
1939 if (last_scrub_x > current_x) {
1941 /* pointer moved to the left */
1943 if (scrubbing_direction > 0) {
1945 /* we reversed direction to go backwards */
1948 scrub_reverse_distance += (int) (last_scrub_x - current_x);
1952 /* still moving to the left (backwards) */
1954 scrub_reversals = 0;
1955 scrub_reverse_distance = 0;
1957 delta = 0.01 * (last_scrub_x - current_x);
1958 _session->request_transport_speed_nonzero (_session->transport_speed() - delta);
1962 /* pointer moved to the right */
1964 if (scrubbing_direction < 0) {
1965 /* we reversed direction to go forward */
1968 scrub_reverse_distance += (int) (current_x - last_scrub_x);
1971 /* still moving to the right */
1973 scrub_reversals = 0;
1974 scrub_reverse_distance = 0;
1976 delta = 0.01 * (current_x - last_scrub_x);
1977 _session->request_transport_speed_nonzero (_session->transport_speed() + delta);
1981 /* if there have been more than 2 opposite motion moves detected, or one that moves
1982 back more than 10 pixels, reverse direction
1985 if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
1987 if (scrubbing_direction > 0) {
1988 /* was forwards, go backwards */
1989 _session->request_transport_speed (-0.1);
1990 scrubbing_direction = -1;
1992 /* was backwards, go forwards */
1993 _session->request_transport_speed (0.1);
1994 scrubbing_direction = 1;
1997 scrub_reverse_distance = 0;
1998 scrub_reversals = 0;
2002 last_scrub_x = current_x;
2006 Editor::motion_handler (ArdourCanvas::Item* /*item*/, GdkEvent* event, bool from_autoscroll)
2008 _last_motion_y = event->motion.y;
2010 if (event->motion.is_hint) {
2013 /* We call this so that MOTION_NOTIFY events continue to be
2014 delivered to the canvas. We need to do this because we set
2015 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
2016 the density of the events, at the expense of a round-trip
2017 to the server. Given that this will mostly occur on cases
2018 where DISPLAY = :0.0, and given the cost of what the motion
2019 event might do, its a good tradeoff.
2022 track_canvas->get_pointer (x, y);
2025 if (current_stepping_trackview) {
2026 /* don't keep the persistent stepped trackview if the mouse moves */
2027 current_stepping_trackview = 0;
2028 step_timeout.disconnect ();
2031 if (_session && _session->actively_recording()) {
2032 /* Sorry. no dragging stuff around while we record */
2036 JoinObjectRangeState const old = _join_object_range_state;
2037 update_join_object_range_location (event->motion.x, event->motion.y);
2038 if (_join_object_range_state != old) {
2039 set_canvas_cursor ();
2042 if (_over_region_trim_target) {
2043 set_canvas_cursor_for_region_view (event->motion.x, entered_regionview);
2046 bool handled = false;
2047 if (_drags->active ()) {
2048 handled = _drags->motion_handler (event, from_autoscroll);
2055 track_canvas_motion (event);
2060 Editor::remove_gain_control_point (ArdourCanvas::Item*item, GdkEvent* /*event*/)
2062 ControlPoint* control_point;
2064 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2065 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2069 // We shouldn't remove the first or last gain point
2070 if (control_point->line().is_last_point(*control_point) ||
2071 control_point->line().is_first_point(*control_point)) {
2075 control_point->line().remove_point (*control_point);
2079 Editor::remove_control_point (ArdourCanvas::Item* item, GdkEvent* /*event*/)
2081 ControlPoint* control_point;
2083 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2084 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2088 control_point->line().remove_point (*control_point);
2092 Editor::edit_control_point (ArdourCanvas::Item* item)
2094 ControlPoint* p = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"));
2097 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2101 ControlPointDialog d (p);
2102 d.set_position (Gtk::WIN_POS_MOUSE);
2105 if (d.run () != RESPONSE_ACCEPT) {
2109 p->line().modify_point_y (*p, d.get_y_fraction ());
2113 Editor::edit_note (ArdourCanvas::Item* item)
2115 ArdourCanvas::CanvasNoteEvent* e = dynamic_cast<ArdourCanvas::CanvasNoteEvent*> (item);
2118 EditNoteDialog d (&e->region_view(), e);
2119 d.set_position (Gtk::WIN_POS_MOUSE);
2127 Editor::visible_order_range (int* low, int* high) const
2129 *low = TimeAxisView::max_order ();
2132 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
2134 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
2136 if (!rtv->hidden()) {
2138 if (*high < rtv->order()) {
2139 *high = rtv->order ();
2142 if (*low > rtv->order()) {
2143 *low = rtv->order ();
2150 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
2152 /* Either add to or set the set the region selection, unless
2153 this is an alignment click (control used)
2156 if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
2157 TimeAxisView* tv = &rv.get_time_axis_view();
2158 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
2160 if (rtv && rtv->is_track()) {
2161 speed = rtv->track()->speed();
2164 framepos_t where = get_preferred_edit_position();
2168 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
2170 align_region (rv.region(), SyncPoint, (framepos_t) (where * speed));
2172 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
2174 align_region (rv.region(), End, (framepos_t) (where * speed));
2178 align_region (rv.region(), Start, (framepos_t) (where * speed));
2185 Editor::show_verbose_time_cursor (framepos_t frame, double offset, double xpos, double ypos)
2188 Timecode::Time timecode;
2189 Timecode::BBT_Time bbt;
2191 framepos_t frame_rate;
2194 if (_session == 0) {
2200 if (Profile->get_sae() || Profile->get_small_screen()) {
2201 m = ARDOUR_UI::instance()->primary_clock.mode();
2203 m = ARDOUR_UI::instance()->secondary_clock.mode();
2207 case AudioClock::BBT:
2208 _session->bbt_time (frame, bbt);
2209 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, bbt.bars, bbt.beats, bbt.ticks);
2212 case AudioClock::Timecode:
2213 _session->timecode_time (frame, timecode);
2214 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
2217 case AudioClock::MinSec:
2218 /* XXX this is copied from show_verbose_duration_cursor() */
2219 frame_rate = _session->frame_rate();
2220 hours = frame / (frame_rate * 3600);
2221 frame = frame % (frame_rate * 3600);
2222 mins = frame / (frame_rate * 60);
2223 frame = frame % (frame_rate * 60);
2224 secs = (float) frame / (float) frame_rate;
2225 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%07.4f", hours, mins, secs);
2229 snprintf (buf, sizeof(buf), "%" PRIi64, frame);
2233 if (xpos >= 0 && ypos >=0) {
2234 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
2236 set_verbose_canvas_cursor (buf, _drags->current_pointer_x() + offset - horizontal_position(), _drags->current_pointer_y() + offset - vertical_adjustment.get_value() + canvas_timebars_vsize);
2238 show_verbose_canvas_cursor ();
2242 Editor::show_verbose_duration_cursor (framepos_t start, framepos_t end, double offset, double xpos, double ypos)
2245 Timecode::Time timecode;
2246 Timecode::BBT_Time sbbt;
2247 Timecode::BBT_Time ebbt;
2249 framepos_t distance, frame_rate;
2251 Meter meter_at_start(_session->tempo_map().meter_at(start));
2253 if (_session == 0) {
2259 if (Profile->get_sae() || Profile->get_small_screen()) {
2260 m = ARDOUR_UI::instance()->primary_clock.mode ();
2262 m = ARDOUR_UI::instance()->secondary_clock.mode ();
2266 case AudioClock::BBT:
2268 _session->bbt_time (start, sbbt);
2269 _session->bbt_time (end, ebbt);
2272 /* XXX this computation won't work well if the
2273 user makes a selection that spans any meter changes.
2276 /* use signed integers for the working values so that
2280 int ticks = ebbt.ticks;
2281 int beats = ebbt.beats;
2282 int bars = ebbt.bars;
2284 ticks -= sbbt.ticks;
2286 ticks += int (Timecode::BBT_Time::ticks_per_beat);
2290 beats -= sbbt.beats;
2292 beats += int (meter_at_start.beats_per_bar());
2298 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, bars, beats, ticks);
2302 case AudioClock::Timecode:
2303 _session->timecode_duration (end - start, timecode);
2304 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
2307 case AudioClock::MinSec:
2308 /* XXX this stuff should be elsewhere.. */
2309 distance = end - start;
2310 frame_rate = _session->frame_rate();
2311 hours = distance / (frame_rate * 3600);
2312 distance = distance % (frame_rate * 3600);
2313 mins = distance / (frame_rate * 60);
2314 distance = distance % (frame_rate * 60);
2315 secs = (float) distance / (float) frame_rate;
2316 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%07.4f", hours, mins, secs);
2320 snprintf (buf, sizeof(buf), "%" PRIi64, end - start);
2324 if (xpos >= 0 && ypos >=0) {
2325 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
2328 set_verbose_canvas_cursor (buf, _drags->current_pointer_x() + offset, _drags->current_pointer_y() + offset);
2331 show_verbose_canvas_cursor ();
2335 Editor::collect_new_region_view (RegionView* rv)
2337 latest_regionviews.push_back (rv);
2341 Editor::collect_and_select_new_region_view (RegionView* rv)
2344 latest_regionviews.push_back (rv);
2348 Editor::cancel_selection ()
2350 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2351 (*i)->hide_selection ();
2354 selection->clear ();
2355 clicked_selection = 0;
2360 Editor::point_trim (GdkEvent* event, framepos_t new_bound)
2362 RegionView* rv = clicked_regionview;
2364 /* Choose action dependant on which button was pressed */
2365 switch (event->button.button) {
2367 begin_reversible_command (_("start point trim"));
2369 if (selection->selected (rv)) {
2370 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
2371 i != selection->regions.by_layer().end(); ++i)
2374 cerr << "region view contains null region" << endl;
2377 if (!(*i)->region()->locked()) {
2378 (*i)->region()->clear_changes ();
2379 (*i)->region()->trim_front (new_bound, this);
2380 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2385 if (!rv->region()->locked()) {
2386 rv->region()->clear_changes ();
2387 rv->region()->trim_front (new_bound, this);
2388 _session->add_command(new StatefulDiffCommand (rv->region()));
2392 commit_reversible_command();
2396 begin_reversible_command (_("End point trim"));
2398 if (selection->selected (rv)) {
2400 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
2402 if (!(*i)->region()->locked()) {
2403 (*i)->region()->clear_changes();
2404 (*i)->region()->trim_end (new_bound, this);
2405 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2411 if (!rv->region()->locked()) {
2412 rv->region()->clear_changes ();
2413 rv->region()->trim_end (new_bound, this);
2414 _session->add_command (new StatefulDiffCommand (rv->region()));
2418 commit_reversible_command();
2427 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* /*event*/)
2432 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2433 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2437 Location* location = find_location_from_marker (marker, is_start);
2438 location->set_hidden (true, this);
2443 Editor::reposition_zoom_rect (framepos_t start, framepos_t end)
2445 double x1 = frame_to_pixel (start);
2446 double x2 = frame_to_pixel (end);
2447 double y2 = full_canvas_height - 1.0;
2449 zoom_rect->property_x1() = x1;
2450 zoom_rect->property_y1() = 1.0;
2451 zoom_rect->property_x2() = x2;
2452 zoom_rect->property_y2() = y2;
2457 Editor::mouse_rename_region (ArdourCanvas::Item* /*item*/, GdkEvent* /*event*/)
2459 using namespace Gtkmm2ext;
2461 ArdourPrompter prompter (false);
2463 prompter.set_prompt (_("Name for region:"));
2464 prompter.set_initial_text (clicked_regionview->region()->name());
2465 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
2466 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
2467 prompter.show_all ();
2468 switch (prompter.run ()) {
2469 case Gtk::RESPONSE_ACCEPT:
2471 prompter.get_result(str);
2473 clicked_regionview->region()->set_name (str);
2482 Editor::mouse_brush_insert_region (RegionView* rv, framepos_t pos)
2484 /* no brushing without a useful snap setting */
2486 switch (_snap_mode) {
2488 return; /* can't work because it allows region to be placed anywhere */
2493 switch (_snap_type) {
2501 /* don't brush a copy over the original */
2503 if (pos == rv->region()->position()) {
2507 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
2509 if (rtv == 0 || !rtv->is_track()) {
2513 boost::shared_ptr<Playlist> playlist = rtv->playlist();
2514 double speed = rtv->track()->speed();
2516 playlist->clear_changes ();
2517 boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region(), true));
2518 playlist->add_region (new_region, (framepos_t) (pos * speed));
2519 _session->add_command (new StatefulDiffCommand (playlist));
2521 // playlist is frozen, so we have to update manually XXX this is disgusting
2523 playlist->RegionAdded (new_region); /* EMIT SIGNAL */
2527 Editor::track_height_step_timeout ()
2529 if (get_microseconds() - last_track_height_step_timestamp < 250000) {
2530 current_stepping_trackview = 0;
2537 Editor::add_region_drag (ArdourCanvas::Item* item, GdkEvent* event, RegionView* region_view)
2539 assert (region_view);
2541 if (!region_view->region()->playlist()) {
2545 _region_motion_group->raise_to_top ();
2547 if (Config->get_edit_mode() == Splice) {
2548 _drags->add (new RegionSpliceDrag (this, item, region_view, selection->regions.by_layer()));
2550 RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id);
2551 _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), false, false));
2554 /* sync the canvas to what we think is its current state */
2555 update_canvas_now();
2559 Editor::add_region_copy_drag (ArdourCanvas::Item* item, GdkEvent* event, RegionView* region_view)
2561 assert (region_view);
2563 if (!region_view->region()->playlist()) {
2567 _region_motion_group->raise_to_top ();
2569 RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id);
2570 _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), false, true));
2574 Editor::add_region_brush_drag (ArdourCanvas::Item* item, GdkEvent* event, RegionView* region_view)
2576 assert (region_view);
2578 if (!region_view->region()->playlist()) {
2582 if (Config->get_edit_mode() == Splice) {
2586 RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id);
2587 _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), true, false));
2589 begin_reversible_command (Operations::drag_region_brush);
2592 /** Start a grab where a time range is selected, track(s) are selected, and the
2593 * user clicks and drags a region with a modifier in order to create a new region containing
2594 * the section of the clicked region that lies within the time range.
2597 Editor::start_selection_grab (ArdourCanvas::Item* /*item*/, GdkEvent* event)
2599 if (clicked_regionview == 0) {
2603 /* lets try to create new Region for the selection */
2605 vector<boost::shared_ptr<Region> > new_regions;
2606 create_region_from_selection (new_regions);
2608 if (new_regions.empty()) {
2612 /* XXX fix me one day to use all new regions */
2614 boost::shared_ptr<Region> region (new_regions.front());
2616 /* add it to the current stream/playlist.
2618 tricky: the streamview for the track will add a new regionview. we will
2619 catch the signal it sends when it creates the regionview to
2620 set the regionview we want to then drag.
2623 latest_regionviews.clear();
2624 sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
2626 /* A selection grab currently creates two undo/redo operations, one for
2627 creating the new region and another for moving it.
2630 begin_reversible_command (Operations::selection_grab);
2632 boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
2634 playlist->clear_changes ();
2635 clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
2636 _session->add_command(new StatefulDiffCommand (playlist));
2638 commit_reversible_command ();
2642 if (latest_regionviews.empty()) {
2643 /* something went wrong */
2647 /* we need to deselect all other regionviews, and select this one
2648 i'm ignoring undo stuff, because the region creation will take care of it
2650 selection->set (latest_regionviews);
2652 _drags->set (new RegionMoveDrag (this, latest_regionviews.front()->get_canvas_group(), latest_regionviews.front(), latest_regionviews, false, false), event);
2658 if (_drags->active ()) {
2661 selection->clear ();
2666 Editor::set_internal_edit (bool yn)
2668 _internal_editing = yn;
2671 mouse_select_button.set_image (*(manage (new Image (::get_icon("midi_tool_pencil")))));
2672 mouse_select_button.get_image ()->show ();
2673 ARDOUR_UI::instance()->set_tip (mouse_select_button, _("Draw/Edit MIDI Notes"));
2674 mouse_mode_toggled (mouse_mode);
2676 pre_internal_mouse_mode = mouse_mode;
2678 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2679 (*i)->enter_internal_edit_mode ();
2684 mouse_select_button.set_image (*(manage (new Image (::get_icon("tool_range")))));
2685 mouse_select_button.get_image ()->show ();
2686 ARDOUR_UI::instance()->set_tip (mouse_select_button, _("Select/Move Ranges"));
2687 mouse_mode_toggled (mouse_mode); // sets cursor
2689 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2690 (*i)->leave_internal_edit_mode ();
2693 if (mouse_mode == MouseRange && pre_internal_mouse_mode != MouseRange) {
2694 /* we were drawing .. flip back to something sensible */
2695 set_mouse_mode (pre_internal_mouse_mode);
2700 /** Update _join_object_range_state which indicate whether we are over the top or bottom half of a region view,
2701 * used by the `join object/range' tool mode.
2704 Editor::update_join_object_range_location (double x, double y)
2706 /* XXX: actually, this decides based on whether the mouse is in the top or bottom half of a RouteTimeAxisView;
2707 entered_{track,regionview} is not always setup (e.g. if the mouse is over a TimeSelection), and to get a Region
2708 that we're over requires searching the playlist.
2711 if (join_object_range_button.get_active() == false || (mouse_mode != MouseRange && mouse_mode != MouseObject)) {
2712 _join_object_range_state = JOIN_OBJECT_RANGE_NONE;
2716 if (mouse_mode == MouseObject) {
2717 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2718 } else if (mouse_mode == MouseRange) {
2719 _join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
2722 /* XXX: maybe we should make entered_track work in all cases, rather than resorting to this */
2723 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y + vertical_adjustment.get_value() - canvas_timebars_vsize);
2727 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
2732 rtv->canvas_display()->w2i (cx, cy);
2734 double const c = cy / rtv->view()->child_height();
2736 double const f = modf (c, &d);
2738 _join_object_range_state = f < 0.5 ? JOIN_OBJECT_RANGE_RANGE : JOIN_OBJECT_RANGE_OBJECT;
2744 Editor::effective_mouse_mode () const
2746 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
2748 } else if (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE) {
2756 Editor::remove_midi_note (ArdourCanvas::Item* item, GdkEvent *)
2758 ArdourCanvas::CanvasNoteEvent* e = dynamic_cast<ArdourCanvas::CanvasNoteEvent*> (item);
2761 e->region_view().delete_note (e->note ());
2765 Editor::set_canvas_cursor_for_region_view (double x, RegionView* rv)
2769 ArdourCanvas::Group* g = rv->get_canvas_group ();
2770 ArdourCanvas::Group* p = g->get_parent_group ();
2772 /* Compute x in region view parent coordinates */
2776 double x1, x2, y1, y2;
2777 g->get_bounds (x1, y1, x2, y2);
2779 /* Halfway across the region */
2780 double const h = (x1 + x2) / 2;
2782 Trimmable::CanTrim ct = rv->region()->can_trim ();
2784 if (ct & Trimmable::FrontTrimEarlier) {
2785 set_canvas_cursor (_cursors->left_side_trim);
2787 set_canvas_cursor (_cursors->left_side_trim_right_only);
2790 if (ct & Trimmable::EndTrimLater) {
2791 set_canvas_cursor (_cursors->right_side_trim);
2793 set_canvas_cursor (_cursors->right_side_trim_left_only);