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 (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
1053 add_region_copy_drag (item, event, clicked_regionview);
1055 add_region_drag (item, event, clicked_regionview);
1057 _drags->start_grab (event);
1060 case ControlPointItem:
1061 _drags->set (new ControlPointDrag (this, item), event);
1069 switch (item_type) {
1070 case RegionViewNameHighlight:
1071 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1075 case LeftFrameHandle:
1076 case RightFrameHandle:
1077 if (!internal_editing ()) {
1078 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1083 case RegionViewName:
1084 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer()), event);
1095 /* relax till release */
1101 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1102 temporal_zoom_to_frame (false, event_frame (event));
1104 temporal_zoom_to_frame (true, event_frame(event));
1117 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1119 if (event->type != GDK_BUTTON_PRESS) {
1123 Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->track_canvas->get_window();
1125 if (canvas_window) {
1126 Glib::RefPtr<const Gdk::Window> pointer_window;
1129 Gdk::ModifierType mask;
1131 pointer_window = canvas_window->get_pointer (x, y, mask);
1133 if (pointer_window == track_canvas->get_bin_window()) {
1134 track_canvas->window_to_world (x, y, wx, wy);
1138 pre_press_cursor = current_canvas_cursor;
1140 track_canvas->grab_focus();
1142 if (_session && _session->actively_recording()) {
1146 button_selection (item, event, item_type);
1148 if (!_drags->active () &&
1149 (Keyboard::is_delete_event (&event->button) ||
1150 Keyboard::is_context_menu_event (&event->button) ||
1151 Keyboard::is_edit_event (&event->button))) {
1153 /* handled by button release */
1157 switch (event->button.button) {
1159 return button_press_handler_1 (item, event, item_type);
1163 return button_press_handler_2 (item, event, item_type);
1170 return button_press_dispatch (&event->button);
1179 Editor::button_press_dispatch (GdkEventButton* ev)
1181 /* this function is intended only for buttons 4 and above.
1184 Gtkmm2ext::MouseButton b (ev->state, ev->button);
1185 return button_bindings->activate (b, Gtkmm2ext::Bindings::Press);
1189 Editor::button_release_dispatch (GdkEventButton* ev)
1191 /* this function is intended only for buttons 4 and above.
1194 Gtkmm2ext::MouseButton b (ev->state, ev->button);
1195 return button_bindings->activate (b, Gtkmm2ext::Bindings::Release);
1199 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1201 framepos_t where = event_frame (event, 0, 0);
1202 AutomationTimeAxisView* atv = 0;
1204 if (pre_press_cursor) {
1205 set_canvas_cursor (pre_press_cursor);
1206 pre_press_cursor = 0;
1209 /* no action if we're recording */
1211 if (_session && _session->actively_recording()) {
1215 /* see if we're finishing a drag */
1217 bool were_dragging = false;
1218 if (_drags->active ()) {
1219 bool const r = _drags->end_grab (event);
1221 /* grab dragged, so do nothing else */
1225 were_dragging = true;
1228 update_region_layering_order_editor ();
1230 /* edit events get handled here */
1232 if (!_drags->active () && Keyboard::is_edit_event (&event->button)) {
1233 switch (item_type) {
1235 show_region_properties ();
1238 case TempoMarkerItem:
1239 edit_tempo_marker (item);
1242 case MeterMarkerItem:
1243 edit_meter_marker (item);
1246 case RegionViewName:
1247 if (clicked_regionview->name_active()) {
1248 return mouse_rename_region (item, event);
1252 case ControlPointItem:
1253 edit_control_point (item);
1266 /* context menu events get handled here */
1268 if (Keyboard::is_context_menu_event (&event->button)) {
1270 if (!_drags->active ()) {
1272 /* no matter which button pops up the context menu, tell the menu
1273 widget to use button 1 to drive menu selection.
1276 switch (item_type) {
1278 case FadeInHandleItem:
1280 case FadeOutHandleItem:
1281 popup_fade_context_menu (1, event->button.time, item, item_type);
1285 popup_track_context_menu (1, event->button.time, item_type, false);
1289 case RegionViewNameHighlight:
1290 case LeftFrameHandle:
1291 case RightFrameHandle:
1292 case RegionViewName:
1293 popup_track_context_menu (1, event->button.time, item_type, false);
1297 popup_track_context_menu (1, event->button.time, item_type, true);
1300 case AutomationTrackItem:
1301 popup_track_context_menu (1, event->button.time, item_type, false);
1305 case RangeMarkerBarItem:
1306 case TransportMarkerBarItem:
1307 case CdMarkerBarItem:
1310 popup_ruler_menu (where, item_type);
1314 marker_context_menu (&event->button, item);
1317 case TempoMarkerItem:
1318 tempo_or_meter_marker_context_menu (&event->button, item);
1321 case MeterMarkerItem:
1322 tempo_or_meter_marker_context_menu (&event->button, item);
1325 case CrossfadeViewItem:
1326 popup_track_context_menu (1, event->button.time, item_type, false);
1330 case ImageFrameItem:
1331 popup_imageframe_edit_menu(1, event->button.time, item, true) ;
1333 case ImageFrameTimeAxisItem:
1334 popup_imageframe_edit_menu(1, event->button.time, item, false) ;
1336 case MarkerViewItem:
1337 popup_marker_time_axis_edit_menu(1, event->button.time, item, true) ;
1339 case MarkerTimeAxisItem:
1340 popup_marker_time_axis_edit_menu(1, event->button.time, item, false) ;
1352 /* delete events get handled here */
1354 Editing::MouseMode const eff = effective_mouse_mode ();
1356 if (!_drags->active () && Keyboard::is_delete_event (&event->button)) {
1358 switch (item_type) {
1359 case TempoMarkerItem:
1360 remove_tempo_marker (item);
1363 case MeterMarkerItem:
1364 remove_meter_marker (item);
1368 remove_marker (*item, event);
1372 if (eff == MouseObject) {
1373 remove_clicked_region ();
1377 case ControlPointItem:
1378 if (eff == MouseGain) {
1379 remove_gain_control_point (item, event);
1381 remove_control_point (item, event);
1386 remove_midi_note (item, event);
1395 switch (event->button.button) {
1398 switch (item_type) {
1399 /* see comments in button_press_handler */
1400 case PlayheadCursorItem:
1403 case AutomationLineItem:
1404 case StartSelectionTrimItem:
1405 case EndSelectionTrimItem:
1409 if (!_dragging_playhead) {
1410 snap_to_with_modifier (where, event, 0, true);
1411 mouse_add_new_marker (where);
1415 case CdMarkerBarItem:
1416 if (!_dragging_playhead) {
1417 // if we get here then a dragged range wasn't done
1418 snap_to_with_modifier (where, event, 0, true);
1419 mouse_add_new_marker (where, true);
1424 if (!_dragging_playhead) {
1425 snap_to_with_modifier (where, event);
1426 mouse_add_new_tempo_event (where);
1431 if (!_dragging_playhead) {
1432 mouse_add_new_meter_event (pixel_to_frame (event->button.x));
1443 switch (item_type) {
1444 case AutomationTrackItem:
1445 atv = dynamic_cast<AutomationTimeAxisView*>(clicked_axisview);
1447 atv->add_automation_event (item, event, where, event->button.y);
1458 switch (item_type) {
1461 /* check that we didn't drag before releasing, since
1462 its really annoying to create new control
1463 points when doing this.
1465 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (clicked_regionview);
1466 if (were_dragging && arv) {
1467 arv->add_gain_point_event (item, event);
1473 case AutomationTrackItem:
1474 dynamic_cast<AutomationTimeAxisView*>(clicked_axisview)->
1475 add_automation_event (item, event, where, event->button.y);
1484 set_canvas_cursor (current_canvas_cursor);
1485 if (scrubbing_direction == 0) {
1486 /* no drag, just a click */
1487 switch (item_type) {
1489 play_selected_region ();
1495 /* make sure we stop */
1496 _session->request_transport_speed (0.0);
1505 /* do any (de)selection operations that should occur on button release */
1506 button_selection (item, event, item_type);
1515 switch (item_type) {
1517 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1519 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier|Keyboard::SecondaryModifier))) {
1522 // Button2 click is unused
1535 // x_style_paste (where, 1.0);
1552 cerr << "button release hits bottom\n";
1558 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1565 last_item_entered = item;
1567 switch (item_type) {
1568 case ControlPointItem:
1569 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1570 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1571 cp->set_visible (true);
1575 at_y = cp->get_y ();
1576 cp->i2w (at_x, at_y);
1580 fraction = 1.0 - (cp->get_y() / cp->line().height());
1582 if (is_drawable() && !_drags->active ()) {
1583 set_canvas_cursor (_cursors->fader);
1586 set_verbose_canvas_cursor (cp->line().get_verbose_cursor_string (fraction), at_x, at_y);
1587 show_verbose_canvas_cursor ();
1592 if (mouse_mode == MouseGain) {
1593 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1595 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredGainLine.get();
1596 if (is_drawable()) {
1597 set_canvas_cursor (_cursors->fader);
1602 case AutomationLineItem:
1603 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1605 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1607 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredAutomationLine.get();
1609 if (is_drawable()) {
1610 set_canvas_cursor (_cursors->fader);
1615 case RegionViewNameHighlight:
1616 if (is_drawable() && mouse_mode == MouseObject && entered_regionview) {
1617 set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1618 _over_region_trim_target = true;
1622 case LeftFrameHandle:
1623 case RightFrameHandle:
1624 if (is_drawable() && mouse_mode == MouseObject && !internal_editing() && entered_regionview) {
1625 set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1629 case StartSelectionTrimItem:
1630 case EndSelectionTrimItem:
1633 case ImageFrameHandleStartItem:
1634 case ImageFrameHandleEndItem:
1635 case MarkerViewHandleStartItem:
1636 case MarkerViewHandleEndItem:
1639 if (is_drawable()) {
1640 set_canvas_cursor (_cursors->trimmer);
1644 case PlayheadCursorItem:
1645 if (is_drawable()) {
1646 switch (_edit_point) {
1648 set_canvas_cursor (_cursors->grabber_edit_point);
1651 set_canvas_cursor (_cursors->grabber);
1657 case RegionViewName:
1659 /* when the name is not an active item, the entire name highlight is for trimming */
1661 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1662 if (mouse_mode == MouseObject && is_drawable()) {
1663 set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1664 _over_region_trim_target = true;
1670 case AutomationTrackItem:
1671 if (is_drawable()) {
1672 Gdk::Cursor *cursor;
1673 switch (mouse_mode) {
1675 cursor = _cursors->selector;
1678 cursor = _cursors->zoom_in;
1681 cursor = _cursors->cross_hair;
1685 set_canvas_cursor (cursor);
1687 AutomationTimeAxisView* atv;
1688 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1689 clear_entered_track = false;
1690 set_entered_track (atv);
1696 case RangeMarkerBarItem:
1697 case TransportMarkerBarItem:
1698 case CdMarkerBarItem:
1701 if (is_drawable()) {
1702 set_canvas_cursor (_cursors->timebar);
1707 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1710 entered_marker = marker;
1711 marker->set_color_rgba (ARDOUR_UI::config()->canvasvar_EnteredMarker.get());
1713 case MeterMarkerItem:
1714 case TempoMarkerItem:
1715 if (is_drawable()) {
1716 set_canvas_cursor (_cursors->timebar);
1720 case FadeInHandleItem:
1721 if (mouse_mode == MouseObject && !internal_editing()) {
1722 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1724 rect->property_fill_color_rgba() = 0xBBBBBBAA;
1726 set_canvas_cursor (_cursors->fade_in);
1730 case FadeOutHandleItem:
1731 if (mouse_mode == MouseObject && !internal_editing()) {
1732 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1734 rect->property_fill_color_rgba() = 0xBBBBBBAA;
1736 set_canvas_cursor (_cursors->fade_out);
1739 case FeatureLineItem:
1741 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1742 line->property_fill_color_rgba() = 0xFF0000FF;
1746 if (join_object_range_button.get_active()) {
1747 set_canvas_cursor ();
1755 /* second pass to handle entered track status in a comprehensible way.
1758 switch (item_type) {
1760 case AutomationLineItem:
1761 case ControlPointItem:
1762 /* these do not affect the current entered track state */
1763 clear_entered_track = false;
1766 case AutomationTrackItem:
1767 /* handled above already */
1771 set_entered_track (0);
1779 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1789 switch (item_type) {
1790 case ControlPointItem:
1791 cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
1792 if (cp->line().the_list()->interpolation() != AutomationList::Discrete) {
1793 if (cp->line().npoints() > 1 && !cp->get_selected()) {
1794 cp->set_visible (false);
1798 if (is_drawable()) {
1799 set_canvas_cursor (current_canvas_cursor);
1802 hide_verbose_canvas_cursor ();
1805 case RegionViewNameHighlight:
1806 case LeftFrameHandle:
1807 case RightFrameHandle:
1808 case StartSelectionTrimItem:
1809 case EndSelectionTrimItem:
1810 case PlayheadCursorItem:
1813 case ImageFrameHandleStartItem:
1814 case ImageFrameHandleEndItem:
1815 case MarkerViewHandleStartItem:
1816 case MarkerViewHandleEndItem:
1819 _over_region_trim_target = false;
1821 if (is_drawable()) {
1822 set_canvas_cursor (current_canvas_cursor);
1827 case AutomationLineItem:
1828 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1830 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1832 line->property_fill_color_rgba() = al->get_line_color();
1834 if (is_drawable()) {
1835 set_canvas_cursor (current_canvas_cursor);
1839 case RegionViewName:
1840 /* see enter_handler() for notes */
1841 _over_region_trim_target = false;
1843 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1844 if (is_drawable() && mouse_mode == MouseObject) {
1845 set_canvas_cursor (current_canvas_cursor);
1850 case RangeMarkerBarItem:
1851 case TransportMarkerBarItem:
1852 case CdMarkerBarItem:
1856 if (is_drawable()) {
1857 set_canvas_cursor (current_canvas_cursor);
1862 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1866 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
1867 location_flags_changed (loc, this);
1870 case MeterMarkerItem:
1871 case TempoMarkerItem:
1873 if (is_drawable()) {
1874 set_canvas_cursor (_cursors->timebar);
1879 case FadeInHandleItem:
1880 case FadeOutHandleItem:
1881 rv = static_cast<RegionView*>(item->get_data ("regionview"));
1883 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1885 rect->property_fill_color_rgba() = rv->get_fill_color();
1886 rect->property_outline_pixels() = 0;
1889 set_canvas_cursor (current_canvas_cursor);
1892 case AutomationTrackItem:
1893 if (is_drawable()) {
1894 set_canvas_cursor (current_canvas_cursor);
1895 clear_entered_track = true;
1896 Glib::signal_idle().connect (sigc::mem_fun(*this, &Editor::left_automation_track));
1899 case FeatureLineItem:
1901 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1902 line->property_fill_color_rgba() = (guint) ARDOUR_UI::config()->canvasvar_ZeroLine.get();;
1914 Editor::left_automation_track ()
1916 if (clear_entered_track) {
1917 set_entered_track (0);
1918 clear_entered_track = false;
1924 Editor::scrub (framepos_t frame, double current_x)
1928 if (scrubbing_direction == 0) {
1930 _session->request_locate (frame, false);
1931 _session->request_transport_speed (0.1);
1932 scrubbing_direction = 1;
1936 if (last_scrub_x > current_x) {
1938 /* pointer moved to the left */
1940 if (scrubbing_direction > 0) {
1942 /* we reversed direction to go backwards */
1945 scrub_reverse_distance += (int) (last_scrub_x - current_x);
1949 /* still moving to the left (backwards) */
1951 scrub_reversals = 0;
1952 scrub_reverse_distance = 0;
1954 delta = 0.01 * (last_scrub_x - current_x);
1955 _session->request_transport_speed_nonzero (_session->transport_speed() - delta);
1959 /* pointer moved to the right */
1961 if (scrubbing_direction < 0) {
1962 /* we reversed direction to go forward */
1965 scrub_reverse_distance += (int) (current_x - last_scrub_x);
1968 /* still moving to the right */
1970 scrub_reversals = 0;
1971 scrub_reverse_distance = 0;
1973 delta = 0.01 * (current_x - last_scrub_x);
1974 _session->request_transport_speed_nonzero (_session->transport_speed() + delta);
1978 /* if there have been more than 2 opposite motion moves detected, or one that moves
1979 back more than 10 pixels, reverse direction
1982 if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
1984 if (scrubbing_direction > 0) {
1985 /* was forwards, go backwards */
1986 _session->request_transport_speed (-0.1);
1987 scrubbing_direction = -1;
1989 /* was backwards, go forwards */
1990 _session->request_transport_speed (0.1);
1991 scrubbing_direction = 1;
1994 scrub_reverse_distance = 0;
1995 scrub_reversals = 0;
1999 last_scrub_x = current_x;
2003 Editor::motion_handler (ArdourCanvas::Item* /*item*/, GdkEvent* event, bool from_autoscroll)
2005 _last_motion_y = event->motion.y;
2007 if (event->motion.is_hint) {
2010 /* We call this so that MOTION_NOTIFY events continue to be
2011 delivered to the canvas. We need to do this because we set
2012 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
2013 the density of the events, at the expense of a round-trip
2014 to the server. Given that this will mostly occur on cases
2015 where DISPLAY = :0.0, and given the cost of what the motion
2016 event might do, its a good tradeoff.
2019 track_canvas->get_pointer (x, y);
2022 if (current_stepping_trackview) {
2023 /* don't keep the persistent stepped trackview if the mouse moves */
2024 current_stepping_trackview = 0;
2025 step_timeout.disconnect ();
2028 if (_session && _session->actively_recording()) {
2029 /* Sorry. no dragging stuff around while we record */
2033 JoinObjectRangeState const old = _join_object_range_state;
2034 update_join_object_range_location (event->motion.x, event->motion.y);
2035 if (_join_object_range_state != old) {
2036 set_canvas_cursor ();
2039 if (_over_region_trim_target) {
2040 set_canvas_cursor_for_region_view (event->motion.x, entered_regionview);
2043 bool handled = false;
2044 if (_drags->active ()) {
2045 handled = _drags->motion_handler (event, from_autoscroll);
2052 track_canvas_motion (event);
2057 Editor::remove_gain_control_point (ArdourCanvas::Item*item, GdkEvent* /*event*/)
2059 ControlPoint* control_point;
2061 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2062 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2066 // We shouldn't remove the first or last gain point
2067 if (control_point->line().is_last_point(*control_point) ||
2068 control_point->line().is_first_point(*control_point)) {
2072 control_point->line().remove_point (*control_point);
2076 Editor::remove_control_point (ArdourCanvas::Item* item, GdkEvent* /*event*/)
2078 ControlPoint* control_point;
2080 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2081 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2085 control_point->line().remove_point (*control_point);
2089 Editor::edit_control_point (ArdourCanvas::Item* item)
2091 ControlPoint* p = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"));
2094 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2098 ControlPointDialog d (p);
2099 d.set_position (Gtk::WIN_POS_MOUSE);
2102 if (d.run () != RESPONSE_ACCEPT) {
2106 p->line().modify_point_y (*p, d.get_y_fraction ());
2110 Editor::edit_note (ArdourCanvas::Item* item)
2112 ArdourCanvas::CanvasNoteEvent* e = dynamic_cast<ArdourCanvas::CanvasNoteEvent*> (item);
2115 EditNoteDialog d (&e->region_view(), e);
2116 d.set_position (Gtk::WIN_POS_MOUSE);
2124 Editor::visible_order_range (int* low, int* high) const
2126 *low = TimeAxisView::max_order ();
2129 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
2131 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
2133 if (!rtv->hidden()) {
2135 if (*high < rtv->order()) {
2136 *high = rtv->order ();
2139 if (*low > rtv->order()) {
2140 *low = rtv->order ();
2147 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
2149 /* Either add to or set the set the region selection, unless
2150 this is an alignment click (control used)
2153 if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
2154 TimeAxisView* tv = &rv.get_time_axis_view();
2155 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
2157 if (rtv && rtv->is_track()) {
2158 speed = rtv->track()->speed();
2161 framepos_t where = get_preferred_edit_position();
2165 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
2167 align_region (rv.region(), SyncPoint, (framepos_t) (where * speed));
2169 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
2171 align_region (rv.region(), End, (framepos_t) (where * speed));
2175 align_region (rv.region(), Start, (framepos_t) (where * speed));
2182 Editor::show_verbose_time_cursor (framepos_t frame, double offset, double xpos, double ypos)
2185 Timecode::Time timecode;
2186 Timecode::BBT_Time bbt;
2188 framepos_t frame_rate;
2191 if (_session == 0) {
2197 if (Profile->get_sae() || Profile->get_small_screen()) {
2198 m = ARDOUR_UI::instance()->primary_clock.mode();
2200 m = ARDOUR_UI::instance()->secondary_clock.mode();
2204 case AudioClock::BBT:
2205 _session->bbt_time (frame, bbt);
2206 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, bbt.bars, bbt.beats, bbt.ticks);
2209 case AudioClock::Timecode:
2210 _session->timecode_time (frame, timecode);
2211 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
2214 case AudioClock::MinSec:
2215 /* XXX this is copied from show_verbose_duration_cursor() */
2216 frame_rate = _session->frame_rate();
2217 hours = frame / (frame_rate * 3600);
2218 frame = frame % (frame_rate * 3600);
2219 mins = frame / (frame_rate * 60);
2220 frame = frame % (frame_rate * 60);
2221 secs = (float) frame / (float) frame_rate;
2222 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%07.4f", hours, mins, secs);
2226 snprintf (buf, sizeof(buf), "%" PRIi64, frame);
2230 if (xpos >= 0 && ypos >=0) {
2231 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
2233 set_verbose_canvas_cursor (buf, _drags->current_pointer_x() + offset - horizontal_position(), _drags->current_pointer_y() + offset - vertical_adjustment.get_value() + canvas_timebars_vsize);
2235 show_verbose_canvas_cursor ();
2239 Editor::show_verbose_duration_cursor (framepos_t start, framepos_t end, double offset, double xpos, double ypos)
2242 Timecode::Time timecode;
2243 Timecode::BBT_Time sbbt;
2244 Timecode::BBT_Time ebbt;
2246 framepos_t distance, frame_rate;
2248 Meter meter_at_start(_session->tempo_map().meter_at(start));
2250 if (_session == 0) {
2256 if (Profile->get_sae() || Profile->get_small_screen()) {
2257 m = ARDOUR_UI::instance()->primary_clock.mode ();
2259 m = ARDOUR_UI::instance()->secondary_clock.mode ();
2263 case AudioClock::BBT:
2264 _session->bbt_time (start, sbbt);
2265 _session->bbt_time (end, ebbt);
2268 /* XXX this computation won't work well if the
2269 user makes a selection that spans any meter changes.
2272 ebbt.bars -= sbbt.bars;
2273 if (ebbt.beats >= sbbt.beats) {
2274 ebbt.beats -= sbbt.beats;
2277 ebbt.beats = int(meter_at_start.beats_per_bar()) + ebbt.beats - sbbt.beats;
2279 if (ebbt.ticks >= sbbt.ticks) {
2280 ebbt.ticks -= sbbt.ticks;
2283 ebbt.ticks = int(Timecode::BBT_Time::ticks_per_beat) + ebbt.ticks - sbbt.ticks;
2286 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, ebbt.bars, ebbt.beats, ebbt.ticks);
2289 case AudioClock::Timecode:
2290 _session->timecode_duration (end - start, timecode);
2291 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
2294 case AudioClock::MinSec:
2295 /* XXX this stuff should be elsewhere.. */
2296 distance = end - start;
2297 frame_rate = _session->frame_rate();
2298 hours = distance / (frame_rate * 3600);
2299 distance = distance % (frame_rate * 3600);
2300 mins = distance / (frame_rate * 60);
2301 distance = distance % (frame_rate * 60);
2302 secs = (float) distance / (float) frame_rate;
2303 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%07.4f", hours, mins, secs);
2307 snprintf (buf, sizeof(buf), "%" PRIi64, end - start);
2311 if (xpos >= 0 && ypos >=0) {
2312 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
2315 set_verbose_canvas_cursor (buf, _drags->current_pointer_x() + offset, _drags->current_pointer_y() + offset);
2318 show_verbose_canvas_cursor ();
2322 Editor::collect_new_region_view (RegionView* rv)
2324 latest_regionviews.push_back (rv);
2328 Editor::collect_and_select_new_region_view (RegionView* rv)
2331 latest_regionviews.push_back (rv);
2335 Editor::cancel_selection ()
2337 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2338 (*i)->hide_selection ();
2341 selection->clear ();
2342 clicked_selection = 0;
2347 Editor::point_trim (GdkEvent* event, framepos_t new_bound)
2349 RegionView* rv = clicked_regionview;
2351 /* Choose action dependant on which button was pressed */
2352 switch (event->button.button) {
2354 begin_reversible_command (_("start point trim"));
2356 if (selection->selected (rv)) {
2357 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
2358 i != selection->regions.by_layer().end(); ++i)
2361 cerr << "region view contains null region" << endl;
2364 if (!(*i)->region()->locked()) {
2365 (*i)->region()->clear_changes ();
2366 (*i)->region()->trim_front (new_bound, this);
2367 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2372 if (!rv->region()->locked()) {
2373 rv->region()->clear_changes ();
2374 rv->region()->trim_front (new_bound, this);
2375 _session->add_command(new StatefulDiffCommand (rv->region()));
2379 commit_reversible_command();
2383 begin_reversible_command (_("End point trim"));
2385 if (selection->selected (rv)) {
2387 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
2389 if (!(*i)->region()->locked()) {
2390 (*i)->region()->clear_changes();
2391 (*i)->region()->trim_end (new_bound, this);
2392 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2398 if (!rv->region()->locked()) {
2399 rv->region()->clear_changes ();
2400 rv->region()->trim_end (new_bound, this);
2401 _session->add_command (new StatefulDiffCommand (rv->region()));
2405 commit_reversible_command();
2414 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* /*event*/)
2419 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2420 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2424 Location* location = find_location_from_marker (marker, is_start);
2425 location->set_hidden (true, this);
2430 Editor::reposition_zoom_rect (framepos_t start, framepos_t end)
2432 double x1 = frame_to_pixel (start);
2433 double x2 = frame_to_pixel (end);
2434 double y2 = full_canvas_height - 1.0;
2436 zoom_rect->property_x1() = x1;
2437 zoom_rect->property_y1() = 1.0;
2438 zoom_rect->property_x2() = x2;
2439 zoom_rect->property_y2() = y2;
2444 Editor::mouse_rename_region (ArdourCanvas::Item* /*item*/, GdkEvent* /*event*/)
2446 using namespace Gtkmm2ext;
2448 ArdourPrompter prompter (false);
2450 prompter.set_prompt (_("Name for region:"));
2451 prompter.set_initial_text (clicked_regionview->region()->name());
2452 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
2453 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
2454 prompter.show_all ();
2455 switch (prompter.run ()) {
2456 case Gtk::RESPONSE_ACCEPT:
2458 prompter.get_result(str);
2460 clicked_regionview->region()->set_name (str);
2469 Editor::mouse_brush_insert_region (RegionView* rv, framepos_t pos)
2471 /* no brushing without a useful snap setting */
2473 switch (_snap_mode) {
2475 return; /* can't work because it allows region to be placed anywhere */
2480 switch (_snap_type) {
2488 /* don't brush a copy over the original */
2490 if (pos == rv->region()->position()) {
2494 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
2496 if (rtv == 0 || !rtv->is_track()) {
2500 boost::shared_ptr<Playlist> playlist = rtv->playlist();
2501 double speed = rtv->track()->speed();
2503 playlist->clear_changes ();
2504 boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region(), true));
2505 playlist->add_region (new_region, (framepos_t) (pos * speed));
2506 _session->add_command (new StatefulDiffCommand (playlist));
2508 // playlist is frozen, so we have to update manually XXX this is disgusting
2510 playlist->RegionAdded (new_region); /* EMIT SIGNAL */
2514 Editor::track_height_step_timeout ()
2516 if (get_microseconds() - last_track_height_step_timestamp < 250000) {
2517 current_stepping_trackview = 0;
2524 Editor::add_region_drag (ArdourCanvas::Item* item, GdkEvent* event, RegionView* region_view)
2526 assert (region_view);
2528 if (!region_view->region()->playlist()) {
2532 _region_motion_group->raise_to_top ();
2534 if (Config->get_edit_mode() == Splice) {
2535 _drags->add (new RegionSpliceDrag (this, item, region_view, selection->regions.by_layer()));
2537 RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id);
2538 _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), false, false));
2541 /* sync the canvas to what we think is its current state */
2542 update_canvas_now();
2546 Editor::add_region_copy_drag (ArdourCanvas::Item* item, GdkEvent* event, RegionView* region_view)
2548 assert (region_view);
2550 if (!region_view->region()->playlist()) {
2554 _region_motion_group->raise_to_top ();
2556 RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id);
2557 _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), false, true));
2561 Editor::add_region_brush_drag (ArdourCanvas::Item* item, GdkEvent* event, RegionView* region_view)
2563 assert (region_view);
2565 if (!region_view->region()->playlist()) {
2569 if (Config->get_edit_mode() == Splice) {
2573 RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id);
2574 _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), true, false));
2576 begin_reversible_command (Operations::drag_region_brush);
2579 /** Start a grab where a time range is selected, track(s) are selected, and the
2580 * user clicks and drags a region with a modifier in order to create a new region containing
2581 * the section of the clicked region that lies within the time range.
2584 Editor::start_selection_grab (ArdourCanvas::Item* /*item*/, GdkEvent* event)
2586 if (clicked_regionview == 0) {
2590 /* lets try to create new Region for the selection */
2592 vector<boost::shared_ptr<Region> > new_regions;
2593 create_region_from_selection (new_regions);
2595 if (new_regions.empty()) {
2599 /* XXX fix me one day to use all new regions */
2601 boost::shared_ptr<Region> region (new_regions.front());
2603 /* add it to the current stream/playlist.
2605 tricky: the streamview for the track will add a new regionview. we will
2606 catch the signal it sends when it creates the regionview to
2607 set the regionview we want to then drag.
2610 latest_regionviews.clear();
2611 sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
2613 /* A selection grab currently creates two undo/redo operations, one for
2614 creating the new region and another for moving it.
2617 begin_reversible_command (Operations::selection_grab);
2619 boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
2621 playlist->clear_changes ();
2622 clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
2623 _session->add_command(new StatefulDiffCommand (playlist));
2625 commit_reversible_command ();
2629 if (latest_regionviews.empty()) {
2630 /* something went wrong */
2634 /* we need to deselect all other regionviews, and select this one
2635 i'm ignoring undo stuff, because the region creation will take care of it
2637 selection->set (latest_regionviews);
2639 _drags->set (new RegionMoveDrag (this, latest_regionviews.front()->get_canvas_group(), latest_regionviews.front(), latest_regionviews, false, false), event);
2645 if (_drags->active ()) {
2648 selection->clear ();
2653 Editor::set_internal_edit (bool yn)
2655 _internal_editing = yn;
2658 mouse_select_button.set_image (*(manage (new Image (::get_icon("midi_tool_pencil")))));
2659 mouse_select_button.get_image ()->show ();
2660 ARDOUR_UI::instance()->set_tip (mouse_select_button, _("Draw/Edit MIDI Notes"));
2661 mouse_mode_toggled (mouse_mode);
2663 pre_internal_mouse_mode = mouse_mode;
2665 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2666 (*i)->enter_internal_edit_mode ();
2671 mouse_select_button.set_image (*(manage (new Image (::get_icon("tool_range")))));
2672 mouse_select_button.get_image ()->show ();
2673 ARDOUR_UI::instance()->set_tip (mouse_select_button, _("Select/Move Ranges"));
2674 mouse_mode_toggled (mouse_mode); // sets cursor
2676 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2677 (*i)->leave_internal_edit_mode ();
2680 if (mouse_mode == MouseRange && pre_internal_mouse_mode != MouseRange) {
2681 /* we were drawing .. flip back to something sensible */
2682 set_mouse_mode (pre_internal_mouse_mode);
2687 /** Update _join_object_range_state which indicate whether we are over the top or bottom half of a region view,
2688 * used by the `join object/range' tool mode.
2691 Editor::update_join_object_range_location (double x, double y)
2693 /* XXX: actually, this decides based on whether the mouse is in the top or bottom half of a RouteTimeAxisView;
2694 entered_{track,regionview} is not always setup (e.g. if the mouse is over a TimeSelection), and to get a Region
2695 that we're over requires searching the playlist.
2698 if (join_object_range_button.get_active() == false || (mouse_mode != MouseRange && mouse_mode != MouseObject)) {
2699 _join_object_range_state = JOIN_OBJECT_RANGE_NONE;
2703 if (mouse_mode == MouseObject) {
2704 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2705 } else if (mouse_mode == MouseRange) {
2706 _join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
2709 /* XXX: maybe we should make entered_track work in all cases, rather than resorting to this */
2710 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y + vertical_adjustment.get_value() - canvas_timebars_vsize);
2714 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
2719 rtv->canvas_display()->w2i (cx, cy);
2721 double const c = cy / rtv->view()->child_height();
2723 double const f = modf (c, &d);
2725 _join_object_range_state = f < 0.5 ? JOIN_OBJECT_RANGE_RANGE : JOIN_OBJECT_RANGE_OBJECT;
2731 Editor::effective_mouse_mode () const
2733 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
2735 } else if (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE) {
2743 Editor::remove_midi_note (ArdourCanvas::Item* item, GdkEvent *)
2745 ArdourCanvas::CanvasNoteEvent* e = dynamic_cast<ArdourCanvas::CanvasNoteEvent*> (item);
2748 e->region_view().delete_note (e->note ());
2752 Editor::set_canvas_cursor_for_region_view (double x, RegionView* rv)
2754 ArdourCanvas::Group* g = rv->get_canvas_group ();
2755 ArdourCanvas::Group* p = g->get_parent_group ();
2757 /* Compute x in region view parent coordinates */
2761 double x1, x2, y1, y2;
2762 g->get_bounds (x1, y1, x2, y2);
2764 /* Halfway across the region */
2765 double const h = (x1 + x2) / 2;
2767 Trimmable::CanTrim ct = rv->region()->can_trim ();
2769 if (ct & Trimmable::FrontTrimEarlier) {
2770 set_canvas_cursor (_cursors->left_side_trim);
2772 set_canvas_cursor (_cursors->left_side_trim_right_only);
2775 if (ct & Trimmable::EndTrimLater) {
2776 set_canvas_cursor (_cursors->right_side_trim);
2778 set_canvas_cursor (_cursors->right_side_trim_left_only);