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);
1556 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1563 last_item_entered = item;
1565 switch (item_type) {
1566 case ControlPointItem:
1567 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1568 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1569 cp->set_visible (true);
1573 at_y = cp->get_y ();
1574 cp->i2w (at_x, at_y);
1578 fraction = 1.0 - (cp->get_y() / cp->line().height());
1580 if (is_drawable() && !_drags->active ()) {
1581 set_canvas_cursor (_cursors->fader);
1584 set_verbose_canvas_cursor (cp->line().get_verbose_cursor_string (fraction), at_x, at_y);
1585 show_verbose_canvas_cursor ();
1590 if (mouse_mode == MouseGain) {
1591 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1593 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredGainLine.get();
1594 if (is_drawable()) {
1595 set_canvas_cursor (_cursors->fader);
1600 case AutomationLineItem:
1601 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1603 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1605 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredAutomationLine.get();
1607 if (is_drawable()) {
1608 set_canvas_cursor (_cursors->fader);
1613 case RegionViewNameHighlight:
1614 if (is_drawable() && mouse_mode == MouseObject && entered_regionview) {
1615 set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1616 _over_region_trim_target = true;
1620 case LeftFrameHandle:
1621 case RightFrameHandle:
1622 if (is_drawable() && mouse_mode == MouseObject && !internal_editing() && entered_regionview) {
1623 set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1627 case StartSelectionTrimItem:
1628 case EndSelectionTrimItem:
1631 case ImageFrameHandleStartItem:
1632 case ImageFrameHandleEndItem:
1633 case MarkerViewHandleStartItem:
1634 case MarkerViewHandleEndItem:
1637 if (is_drawable()) {
1638 set_canvas_cursor (_cursors->trimmer);
1642 case PlayheadCursorItem:
1643 if (is_drawable()) {
1644 switch (_edit_point) {
1646 set_canvas_cursor (_cursors->grabber_edit_point);
1649 set_canvas_cursor (_cursors->grabber);
1655 case RegionViewName:
1657 /* when the name is not an active item, the entire name highlight is for trimming */
1659 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1660 if (mouse_mode == MouseObject && is_drawable()) {
1661 set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1662 _over_region_trim_target = true;
1668 case AutomationTrackItem:
1669 if (is_drawable()) {
1670 Gdk::Cursor *cursor;
1671 switch (mouse_mode) {
1673 cursor = _cursors->selector;
1676 cursor = _cursors->zoom_in;
1679 cursor = _cursors->cross_hair;
1683 set_canvas_cursor (cursor);
1685 AutomationTimeAxisView* atv;
1686 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1687 clear_entered_track = false;
1688 set_entered_track (atv);
1694 case RangeMarkerBarItem:
1695 case TransportMarkerBarItem:
1696 case CdMarkerBarItem:
1699 if (is_drawable()) {
1700 set_canvas_cursor (_cursors->timebar);
1705 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1708 entered_marker = marker;
1709 marker->set_color_rgba (ARDOUR_UI::config()->canvasvar_EnteredMarker.get());
1711 case MeterMarkerItem:
1712 case TempoMarkerItem:
1713 if (is_drawable()) {
1714 set_canvas_cursor (_cursors->timebar);
1718 case FadeInHandleItem:
1719 if (mouse_mode == MouseObject && !internal_editing()) {
1720 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1722 rect->property_fill_color_rgba() = 0xBBBBBBAA;
1724 set_canvas_cursor (_cursors->fade_in);
1728 case FadeOutHandleItem:
1729 if (mouse_mode == MouseObject && !internal_editing()) {
1730 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1732 rect->property_fill_color_rgba() = 0xBBBBBBAA;
1734 set_canvas_cursor (_cursors->fade_out);
1737 case FeatureLineItem:
1739 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1740 line->property_fill_color_rgba() = 0xFF0000FF;
1744 if (join_object_range_button.get_active()) {
1745 set_canvas_cursor ();
1753 /* second pass to handle entered track status in a comprehensible way.
1756 switch (item_type) {
1758 case AutomationLineItem:
1759 case ControlPointItem:
1760 /* these do not affect the current entered track state */
1761 clear_entered_track = false;
1764 case AutomationTrackItem:
1765 /* handled above already */
1769 set_entered_track (0);
1777 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1787 switch (item_type) {
1788 case ControlPointItem:
1789 cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
1790 if (cp->line().the_list()->interpolation() != AutomationList::Discrete) {
1791 if (cp->line().npoints() > 1 && !cp->get_selected()) {
1792 cp->set_visible (false);
1796 if (is_drawable()) {
1797 set_canvas_cursor (current_canvas_cursor);
1800 hide_verbose_canvas_cursor ();
1803 case RegionViewNameHighlight:
1804 case LeftFrameHandle:
1805 case RightFrameHandle:
1806 case StartSelectionTrimItem:
1807 case EndSelectionTrimItem:
1808 case PlayheadCursorItem:
1811 case ImageFrameHandleStartItem:
1812 case ImageFrameHandleEndItem:
1813 case MarkerViewHandleStartItem:
1814 case MarkerViewHandleEndItem:
1817 _over_region_trim_target = false;
1819 if (is_drawable()) {
1820 set_canvas_cursor (current_canvas_cursor);
1825 case AutomationLineItem:
1826 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1828 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1830 line->property_fill_color_rgba() = al->get_line_color();
1832 if (is_drawable()) {
1833 set_canvas_cursor (current_canvas_cursor);
1837 case RegionViewName:
1838 /* see enter_handler() for notes */
1839 _over_region_trim_target = false;
1841 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1842 if (is_drawable() && mouse_mode == MouseObject) {
1843 set_canvas_cursor (current_canvas_cursor);
1848 case RangeMarkerBarItem:
1849 case TransportMarkerBarItem:
1850 case CdMarkerBarItem:
1854 if (is_drawable()) {
1855 set_canvas_cursor (current_canvas_cursor);
1860 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1864 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
1865 location_flags_changed (loc, this);
1868 case MeterMarkerItem:
1869 case TempoMarkerItem:
1871 if (is_drawable()) {
1872 set_canvas_cursor (_cursors->timebar);
1877 case FadeInHandleItem:
1878 case FadeOutHandleItem:
1879 rv = static_cast<RegionView*>(item->get_data ("regionview"));
1881 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1883 rect->property_fill_color_rgba() = rv->get_fill_color();
1884 rect->property_outline_pixels() = 0;
1887 set_canvas_cursor (current_canvas_cursor);
1890 case AutomationTrackItem:
1891 if (is_drawable()) {
1892 set_canvas_cursor (current_canvas_cursor);
1893 clear_entered_track = true;
1894 Glib::signal_idle().connect (sigc::mem_fun(*this, &Editor::left_automation_track));
1897 case FeatureLineItem:
1899 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1900 line->property_fill_color_rgba() = (guint) ARDOUR_UI::config()->canvasvar_ZeroLine.get();;
1912 Editor::left_automation_track ()
1914 if (clear_entered_track) {
1915 set_entered_track (0);
1916 clear_entered_track = false;
1922 Editor::scrub (framepos_t frame, double current_x)
1926 if (scrubbing_direction == 0) {
1928 _session->request_locate (frame, false);
1929 _session->request_transport_speed (0.1);
1930 scrubbing_direction = 1;
1934 if (last_scrub_x > current_x) {
1936 /* pointer moved to the left */
1938 if (scrubbing_direction > 0) {
1940 /* we reversed direction to go backwards */
1943 scrub_reverse_distance += (int) (last_scrub_x - current_x);
1947 /* still moving to the left (backwards) */
1949 scrub_reversals = 0;
1950 scrub_reverse_distance = 0;
1952 delta = 0.01 * (last_scrub_x - current_x);
1953 _session->request_transport_speed_nonzero (_session->transport_speed() - delta);
1957 /* pointer moved to the right */
1959 if (scrubbing_direction < 0) {
1960 /* we reversed direction to go forward */
1963 scrub_reverse_distance += (int) (current_x - last_scrub_x);
1966 /* still moving to the right */
1968 scrub_reversals = 0;
1969 scrub_reverse_distance = 0;
1971 delta = 0.01 * (current_x - last_scrub_x);
1972 _session->request_transport_speed_nonzero (_session->transport_speed() + delta);
1976 /* if there have been more than 2 opposite motion moves detected, or one that moves
1977 back more than 10 pixels, reverse direction
1980 if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
1982 if (scrubbing_direction > 0) {
1983 /* was forwards, go backwards */
1984 _session->request_transport_speed (-0.1);
1985 scrubbing_direction = -1;
1987 /* was backwards, go forwards */
1988 _session->request_transport_speed (0.1);
1989 scrubbing_direction = 1;
1992 scrub_reverse_distance = 0;
1993 scrub_reversals = 0;
1997 last_scrub_x = current_x;
2001 Editor::motion_handler (ArdourCanvas::Item* /*item*/, GdkEvent* event, bool from_autoscroll)
2003 _last_motion_y = event->motion.y;
2005 if (event->motion.is_hint) {
2008 /* We call this so that MOTION_NOTIFY events continue to be
2009 delivered to the canvas. We need to do this because we set
2010 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
2011 the density of the events, at the expense of a round-trip
2012 to the server. Given that this will mostly occur on cases
2013 where DISPLAY = :0.0, and given the cost of what the motion
2014 event might do, its a good tradeoff.
2017 track_canvas->get_pointer (x, y);
2020 if (current_stepping_trackview) {
2021 /* don't keep the persistent stepped trackview if the mouse moves */
2022 current_stepping_trackview = 0;
2023 step_timeout.disconnect ();
2026 if (_session && _session->actively_recording()) {
2027 /* Sorry. no dragging stuff around while we record */
2031 JoinObjectRangeState const old = _join_object_range_state;
2032 update_join_object_range_location (event->motion.x, event->motion.y);
2033 if (_join_object_range_state != old) {
2034 set_canvas_cursor ();
2037 if (_over_region_trim_target) {
2038 set_canvas_cursor_for_region_view (event->motion.x, entered_regionview);
2041 bool handled = false;
2042 if (_drags->active ()) {
2043 handled = _drags->motion_handler (event, from_autoscroll);
2050 track_canvas_motion (event);
2055 Editor::remove_gain_control_point (ArdourCanvas::Item*item, GdkEvent* /*event*/)
2057 ControlPoint* control_point;
2059 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2060 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2064 // We shouldn't remove the first or last gain point
2065 if (control_point->line().is_last_point(*control_point) ||
2066 control_point->line().is_first_point(*control_point)) {
2070 control_point->line().remove_point (*control_point);
2074 Editor::remove_control_point (ArdourCanvas::Item* item, GdkEvent* /*event*/)
2076 ControlPoint* control_point;
2078 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2079 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2083 control_point->line().remove_point (*control_point);
2087 Editor::edit_control_point (ArdourCanvas::Item* item)
2089 ControlPoint* p = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"));
2092 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2096 ControlPointDialog d (p);
2097 d.set_position (Gtk::WIN_POS_MOUSE);
2100 if (d.run () != RESPONSE_ACCEPT) {
2104 p->line().modify_point_y (*p, d.get_y_fraction ());
2108 Editor::edit_note (ArdourCanvas::Item* item)
2110 ArdourCanvas::CanvasNoteEvent* e = dynamic_cast<ArdourCanvas::CanvasNoteEvent*> (item);
2113 EditNoteDialog d (&e->region_view(), e);
2114 d.set_position (Gtk::WIN_POS_MOUSE);
2122 Editor::visible_order_range (int* low, int* high) const
2124 *low = TimeAxisView::max_order ();
2127 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
2129 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
2131 if (!rtv->hidden()) {
2133 if (*high < rtv->order()) {
2134 *high = rtv->order ();
2137 if (*low > rtv->order()) {
2138 *low = rtv->order ();
2145 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
2147 /* Either add to or set the set the region selection, unless
2148 this is an alignment click (control used)
2151 if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
2152 TimeAxisView* tv = &rv.get_time_axis_view();
2153 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
2155 if (rtv && rtv->is_track()) {
2156 speed = rtv->track()->speed();
2159 framepos_t where = get_preferred_edit_position();
2163 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
2165 align_region (rv.region(), SyncPoint, (framepos_t) (where * speed));
2167 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
2169 align_region (rv.region(), End, (framepos_t) (where * speed));
2173 align_region (rv.region(), Start, (framepos_t) (where * speed));
2180 Editor::show_verbose_time_cursor (framepos_t frame, double offset, double xpos, double ypos)
2183 Timecode::Time timecode;
2184 Timecode::BBT_Time bbt;
2186 framepos_t frame_rate;
2189 if (_session == 0) {
2195 if (Profile->get_sae() || Profile->get_small_screen()) {
2196 m = ARDOUR_UI::instance()->primary_clock.mode();
2198 m = ARDOUR_UI::instance()->secondary_clock.mode();
2202 case AudioClock::BBT:
2203 _session->bbt_time (frame, bbt);
2204 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, bbt.bars, bbt.beats, bbt.ticks);
2207 case AudioClock::Timecode:
2208 _session->timecode_time (frame, timecode);
2209 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
2212 case AudioClock::MinSec:
2213 /* XXX this is copied from show_verbose_duration_cursor() */
2214 frame_rate = _session->frame_rate();
2215 hours = frame / (frame_rate * 3600);
2216 frame = frame % (frame_rate * 3600);
2217 mins = frame / (frame_rate * 60);
2218 frame = frame % (frame_rate * 60);
2219 secs = (float) frame / (float) frame_rate;
2220 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%07.4f", hours, mins, secs);
2224 snprintf (buf, sizeof(buf), "%" PRIi64, frame);
2228 if (xpos >= 0 && ypos >=0) {
2229 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
2231 set_verbose_canvas_cursor (buf, _drags->current_pointer_x() + offset - horizontal_position(), _drags->current_pointer_y() + offset - vertical_adjustment.get_value() + canvas_timebars_vsize);
2233 show_verbose_canvas_cursor ();
2237 Editor::show_verbose_duration_cursor (framepos_t start, framepos_t end, double offset, double xpos, double ypos)
2240 Timecode::Time timecode;
2241 Timecode::BBT_Time sbbt;
2242 Timecode::BBT_Time ebbt;
2244 framepos_t distance, frame_rate;
2246 Meter meter_at_start(_session->tempo_map().meter_at(start));
2248 if (_session == 0) {
2254 if (Profile->get_sae() || Profile->get_small_screen()) {
2255 m = ARDOUR_UI::instance()->primary_clock.mode ();
2257 m = ARDOUR_UI::instance()->secondary_clock.mode ();
2261 case AudioClock::BBT:
2262 _session->bbt_time (start, sbbt);
2263 _session->bbt_time (end, ebbt);
2266 /* XXX this computation won't work well if the
2267 user makes a selection that spans any meter changes.
2270 ebbt.bars -= sbbt.bars;
2271 if (ebbt.beats >= sbbt.beats) {
2272 ebbt.beats -= sbbt.beats;
2275 ebbt.beats = int(meter_at_start.beats_per_bar()) + ebbt.beats - sbbt.beats;
2277 if (ebbt.ticks >= sbbt.ticks) {
2278 ebbt.ticks -= sbbt.ticks;
2281 ebbt.ticks = int(Timecode::BBT_Time::ticks_per_beat) + ebbt.ticks - sbbt.ticks;
2284 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, ebbt.bars, ebbt.beats, ebbt.ticks);
2287 case AudioClock::Timecode:
2288 _session->timecode_duration (end - start, timecode);
2289 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
2292 case AudioClock::MinSec:
2293 /* XXX this stuff should be elsewhere.. */
2294 distance = end - start;
2295 frame_rate = _session->frame_rate();
2296 hours = distance / (frame_rate * 3600);
2297 distance = distance % (frame_rate * 3600);
2298 mins = distance / (frame_rate * 60);
2299 distance = distance % (frame_rate * 60);
2300 secs = (float) distance / (float) frame_rate;
2301 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%07.4f", hours, mins, secs);
2305 snprintf (buf, sizeof(buf), "%" PRIi64, end - start);
2309 if (xpos >= 0 && ypos >=0) {
2310 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
2313 set_verbose_canvas_cursor (buf, _drags->current_pointer_x() + offset, _drags->current_pointer_y() + offset);
2316 show_verbose_canvas_cursor ();
2320 Editor::collect_new_region_view (RegionView* rv)
2322 latest_regionviews.push_back (rv);
2326 Editor::collect_and_select_new_region_view (RegionView* rv)
2329 latest_regionviews.push_back (rv);
2333 Editor::cancel_selection ()
2335 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2336 (*i)->hide_selection ();
2339 selection->clear ();
2340 clicked_selection = 0;
2345 Editor::point_trim (GdkEvent* event, framepos_t new_bound)
2347 RegionView* rv = clicked_regionview;
2349 /* Choose action dependant on which button was pressed */
2350 switch (event->button.button) {
2352 begin_reversible_command (_("start point trim"));
2354 if (selection->selected (rv)) {
2355 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
2356 i != selection->regions.by_layer().end(); ++i)
2359 cerr << "region view contains null region" << endl;
2362 if (!(*i)->region()->locked()) {
2363 (*i)->region()->clear_changes ();
2364 (*i)->region()->trim_front (new_bound, this);
2365 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2370 if (!rv->region()->locked()) {
2371 rv->region()->clear_changes ();
2372 rv->region()->trim_front (new_bound, this);
2373 _session->add_command(new StatefulDiffCommand (rv->region()));
2377 commit_reversible_command();
2381 begin_reversible_command (_("End point trim"));
2383 if (selection->selected (rv)) {
2385 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
2387 if (!(*i)->region()->locked()) {
2388 (*i)->region()->clear_changes();
2389 (*i)->region()->trim_end (new_bound, this);
2390 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2396 if (!rv->region()->locked()) {
2397 rv->region()->clear_changes ();
2398 rv->region()->trim_end (new_bound, this);
2399 _session->add_command (new StatefulDiffCommand (rv->region()));
2403 commit_reversible_command();
2412 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* /*event*/)
2417 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2418 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2422 Location* location = find_location_from_marker (marker, is_start);
2423 location->set_hidden (true, this);
2428 Editor::reposition_zoom_rect (framepos_t start, framepos_t end)
2430 double x1 = frame_to_pixel (start);
2431 double x2 = frame_to_pixel (end);
2432 double y2 = full_canvas_height - 1.0;
2434 zoom_rect->property_x1() = x1;
2435 zoom_rect->property_y1() = 1.0;
2436 zoom_rect->property_x2() = x2;
2437 zoom_rect->property_y2() = y2;
2442 Editor::mouse_rename_region (ArdourCanvas::Item* /*item*/, GdkEvent* /*event*/)
2444 using namespace Gtkmm2ext;
2446 ArdourPrompter prompter (false);
2448 prompter.set_prompt (_("Name for region:"));
2449 prompter.set_initial_text (clicked_regionview->region()->name());
2450 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
2451 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
2452 prompter.show_all ();
2453 switch (prompter.run ()) {
2454 case Gtk::RESPONSE_ACCEPT:
2456 prompter.get_result(str);
2458 clicked_regionview->region()->set_name (str);
2467 Editor::mouse_brush_insert_region (RegionView* rv, framepos_t pos)
2469 /* no brushing without a useful snap setting */
2471 switch (_snap_mode) {
2473 return; /* can't work because it allows region to be placed anywhere */
2478 switch (_snap_type) {
2486 /* don't brush a copy over the original */
2488 if (pos == rv->region()->position()) {
2492 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
2494 if (rtv == 0 || !rtv->is_track()) {
2498 boost::shared_ptr<Playlist> playlist = rtv->playlist();
2499 double speed = rtv->track()->speed();
2501 playlist->clear_changes ();
2502 boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region(), true));
2503 playlist->add_region (new_region, (framepos_t) (pos * speed));
2504 _session->add_command (new StatefulDiffCommand (playlist));
2506 // playlist is frozen, so we have to update manually XXX this is disgusting
2508 playlist->RegionAdded (new_region); /* EMIT SIGNAL */
2512 Editor::track_height_step_timeout ()
2514 if (get_microseconds() - last_track_height_step_timestamp < 250000) {
2515 current_stepping_trackview = 0;
2522 Editor::add_region_drag (ArdourCanvas::Item* item, GdkEvent* event, RegionView* region_view)
2524 assert (region_view);
2526 if (!region_view->region()->playlist()) {
2530 _region_motion_group->raise_to_top ();
2532 if (Config->get_edit_mode() == Splice) {
2533 _drags->add (new RegionSpliceDrag (this, item, region_view, selection->regions.by_layer()));
2535 RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id);
2536 _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), false, false));
2539 /* sync the canvas to what we think is its current state */
2540 update_canvas_now();
2544 Editor::add_region_copy_drag (ArdourCanvas::Item* item, GdkEvent* event, RegionView* region_view)
2546 assert (region_view);
2548 if (!region_view->region()->playlist()) {
2552 _region_motion_group->raise_to_top ();
2554 RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id);
2555 _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), false, true));
2559 Editor::add_region_brush_drag (ArdourCanvas::Item* item, GdkEvent* event, RegionView* region_view)
2561 assert (region_view);
2563 if (!region_view->region()->playlist()) {
2567 if (Config->get_edit_mode() == Splice) {
2571 RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id);
2572 _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), true, false));
2574 begin_reversible_command (Operations::drag_region_brush);
2577 /** Start a grab where a time range is selected, track(s) are selected, and the
2578 * user clicks and drags a region with a modifier in order to create a new region containing
2579 * the section of the clicked region that lies within the time range.
2582 Editor::start_selection_grab (ArdourCanvas::Item* /*item*/, GdkEvent* event)
2584 if (clicked_regionview == 0) {
2588 /* lets try to create new Region for the selection */
2590 vector<boost::shared_ptr<Region> > new_regions;
2591 create_region_from_selection (new_regions);
2593 if (new_regions.empty()) {
2597 /* XXX fix me one day to use all new regions */
2599 boost::shared_ptr<Region> region (new_regions.front());
2601 /* add it to the current stream/playlist.
2603 tricky: the streamview for the track will add a new regionview. we will
2604 catch the signal it sends when it creates the regionview to
2605 set the regionview we want to then drag.
2608 latest_regionviews.clear();
2609 sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
2611 /* A selection grab currently creates two undo/redo operations, one for
2612 creating the new region and another for moving it.
2615 begin_reversible_command (Operations::selection_grab);
2617 boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
2619 playlist->clear_changes ();
2620 clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
2621 _session->add_command(new StatefulDiffCommand (playlist));
2623 commit_reversible_command ();
2627 if (latest_regionviews.empty()) {
2628 /* something went wrong */
2632 /* we need to deselect all other regionviews, and select this one
2633 i'm ignoring undo stuff, because the region creation will take care of it
2635 selection->set (latest_regionviews);
2637 _drags->set (new RegionMoveDrag (this, latest_regionviews.front()->get_canvas_group(), latest_regionviews.front(), latest_regionviews, false, false), event);
2643 if (_drags->active ()) {
2646 selection->clear ();
2651 Editor::set_internal_edit (bool yn)
2653 _internal_editing = yn;
2656 mouse_select_button.set_image (*(manage (new Image (::get_icon("midi_tool_pencil")))));
2657 mouse_select_button.get_image ()->show ();
2658 ARDOUR_UI::instance()->set_tip (mouse_select_button, _("Draw/Edit MIDI Notes"));
2659 mouse_mode_toggled (mouse_mode);
2661 pre_internal_mouse_mode = mouse_mode;
2663 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2664 (*i)->enter_internal_edit_mode ();
2669 mouse_select_button.set_image (*(manage (new Image (::get_icon("tool_range")))));
2670 mouse_select_button.get_image ()->show ();
2671 ARDOUR_UI::instance()->set_tip (mouse_select_button, _("Select/Move Ranges"));
2672 mouse_mode_toggled (mouse_mode); // sets cursor
2674 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2675 (*i)->leave_internal_edit_mode ();
2678 if (mouse_mode == MouseRange && pre_internal_mouse_mode != MouseRange) {
2679 /* we were drawing .. flip back to something sensible */
2680 set_mouse_mode (pre_internal_mouse_mode);
2685 /** Update _join_object_range_state which indicate whether we are over the top or bottom half of a region view,
2686 * used by the `join object/range' tool mode.
2689 Editor::update_join_object_range_location (double x, double y)
2691 /* XXX: actually, this decides based on whether the mouse is in the top or bottom half of a RouteTimeAxisView;
2692 entered_{track,regionview} is not always setup (e.g. if the mouse is over a TimeSelection), and to get a Region
2693 that we're over requires searching the playlist.
2696 if (join_object_range_button.get_active() == false || (mouse_mode != MouseRange && mouse_mode != MouseObject)) {
2697 _join_object_range_state = JOIN_OBJECT_RANGE_NONE;
2701 if (mouse_mode == MouseObject) {
2702 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2703 } else if (mouse_mode == MouseRange) {
2704 _join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
2707 /* XXX: maybe we should make entered_track work in all cases, rather than resorting to this */
2708 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y + vertical_adjustment.get_value() - canvas_timebars_vsize);
2712 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
2717 rtv->canvas_display()->w2i (cx, cy);
2719 double const c = cy / rtv->view()->child_height();
2721 double const f = modf (c, &d);
2723 _join_object_range_state = f < 0.5 ? JOIN_OBJECT_RANGE_RANGE : JOIN_OBJECT_RANGE_OBJECT;
2729 Editor::effective_mouse_mode () const
2731 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
2733 } else if (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE) {
2741 Editor::remove_midi_note (ArdourCanvas::Item* item, GdkEvent *)
2743 ArdourCanvas::CanvasNoteEvent* e = dynamic_cast<ArdourCanvas::CanvasNoteEvent*> (item);
2746 e->region_view().delete_note (e->note ());
2750 Editor::set_canvas_cursor_for_region_view (double x, RegionView* rv)
2752 ArdourCanvas::Group* g = rv->get_canvas_group ();
2753 ArdourCanvas::Group* p = g->get_parent_group ();
2755 /* Compute x in region view parent coordinates */
2759 double x1, x2, y1, y2;
2760 g->get_bounds (x1, y1, x2, y2);
2762 /* Halfway across the region */
2763 double const h = (x1 + x2) / 2;
2765 Trimmable::CanTrim ct = rv->region()->can_trim ();
2767 if (ct & Trimmable::FrontTrimEarlier) {
2768 set_canvas_cursor (_cursors->left_side_trim);
2770 set_canvas_cursor (_cursors->left_side_trim_right_only);
2773 if (ct & Trimmable::EndTrimLater) {
2774 set_canvas_cursor (_cursors->right_side_trim);
2776 set_canvas_cursor (_cursors->right_side_trim_left_only);