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 <gtkmm2ext/utils.h>
31 #include <gtkmm2ext/tearoff.h>
32 #include "pbd/memento_command.h"
33 #include "pbd/basename.h"
34 #include "pbd/stateful_diff_command.h"
36 #include "ardour_ui.h"
39 #include "time_axis_view.h"
40 #include "audio_time_axis.h"
41 #include "audio_region_view.h"
42 #include "midi_region_view.h"
44 #include "streamview.h"
45 #include "region_gain_line.h"
46 #include "automation_time_axis.h"
47 #include "control_point.h"
50 #include "selection.h"
53 #include "rgb_macros.h"
54 #include "control_point_dialog.h"
55 #include "editor_drag.h"
57 #include "ardour/types.h"
58 #include "ardour/profile.h"
59 #include "ardour/route.h"
60 #include "ardour/audio_track.h"
61 #include "ardour/audio_diskstream.h"
62 #include "ardour/midi_diskstream.h"
63 #include "ardour/playlist.h"
64 #include "ardour/audioplaylist.h"
65 #include "ardour/audioregion.h"
66 #include "ardour/midi_region.h"
67 #include "ardour/dB.h"
68 #include "ardour/utils.h"
69 #include "ardour/region_factory.h"
70 #include "ardour/source_factory.h"
71 #include "ardour/session.h"
78 using namespace ARDOUR;
81 using namespace Editing;
82 using Gtkmm2ext::Keyboard;
85 Editor::mouse_frame (nframes64_t& where, bool& in_track_canvas) const
89 Gdk::ModifierType mask;
90 Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->track_canvas->get_window();
91 Glib::RefPtr<const Gdk::Window> pointer_window;
97 pointer_window = canvas_window->get_pointer (x, y, mask);
99 if (pointer_window == track_canvas->get_bin_window()) {
102 in_track_canvas = true;
105 in_track_canvas = false;
110 event.type = GDK_BUTTON_RELEASE;
114 where = event_frame (&event, 0, 0);
119 Editor::event_frame (GdkEvent const * event, double* pcx, double* pcy) const
133 switch (event->type) {
134 case GDK_BUTTON_RELEASE:
135 case GDK_BUTTON_PRESS:
136 case GDK_2BUTTON_PRESS:
137 case GDK_3BUTTON_PRESS:
138 *pcx = event->button.x;
139 *pcy = event->button.y;
140 _trackview_group->w2i(*pcx, *pcy);
142 case GDK_MOTION_NOTIFY:
143 *pcx = event->motion.x;
144 *pcy = event->motion.y;
145 _trackview_group->w2i(*pcx, *pcy);
147 case GDK_ENTER_NOTIFY:
148 case GDK_LEAVE_NOTIFY:
149 track_canvas->w2c(event->crossing.x, event->crossing.y, *pcx, *pcy);
152 case GDK_KEY_RELEASE:
153 // track_canvas->w2c(event->key.x, event->key.y, *pcx, *pcy);
156 warning << string_compose (_("Editor::event_frame() used on unhandled event type %1"), event->type) << endmsg;
160 /* note that pixel_to_frame() never returns less than zero, so even if the pixel
161 position is negative (as can be the case with motion events in particular),
162 the frame location is always positive.
165 return pixel_to_frame (*pcx);
169 Editor::which_grabber_cursor ()
171 Gdk::Cursor* c = grabber_cursor;
173 if (_internal_editing) {
174 switch (mouse_mode) {
176 c = midi_pencil_cursor;
180 c = grabber_note_cursor;
184 c = midi_resize_cursor;
193 switch (_edit_point) {
195 c = grabber_edit_point_cursor;
206 Editor::set_canvas_cursor ()
208 if (_internal_editing) {
210 switch (mouse_mode) {
212 current_canvas_cursor = midi_pencil_cursor;
216 current_canvas_cursor = which_grabber_cursor();
220 current_canvas_cursor = midi_resize_cursor;
229 switch (mouse_mode) {
231 current_canvas_cursor = selector_cursor;
235 current_canvas_cursor = which_grabber_cursor();
239 current_canvas_cursor = cross_hair_cursor;
243 current_canvas_cursor = zoom_cursor;
247 current_canvas_cursor = time_fx_cursor; // just use playhead
251 current_canvas_cursor = speaker_cursor;
256 switch (_join_object_range_state) {
257 case JOIN_OBJECT_RANGE_NONE:
259 case JOIN_OBJECT_RANGE_OBJECT:
260 current_canvas_cursor = which_grabber_cursor ();
262 case JOIN_OBJECT_RANGE_RANGE:
263 current_canvas_cursor = selector_cursor;
268 track_canvas->get_window()->set_cursor(*current_canvas_cursor);
273 Editor::set_mouse_mode (MouseMode m, bool force)
275 if (_drags->active ()) {
279 if (!force && m == mouse_mode) {
283 Glib::RefPtr<Action> act;
287 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-range"));
291 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-object"));
295 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-gain"));
299 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-zoom"));
303 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-timefx"));
307 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-audition"));
313 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
316 /* go there and back to ensure that the toggled handler is called to set up mouse_mode */
317 tact->set_active (false);
318 tact->set_active (true);
322 Editor::mouse_mode_toggled (MouseMode m)
328 cerr << "Mouse mode toggled to " << m << endl;
330 if (!internal_editing()) {
331 if (mouse_mode != MouseRange && _join_object_range_state == JOIN_OBJECT_RANGE_NONE) {
333 /* in all modes except range and joined object/range, hide the range selection,
334 show the object (region) selection.
337 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
338 (*i)->set_should_show_selection (true);
340 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
341 (*i)->hide_selection ();
347 in range or object/range mode, show the range selection.
350 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
351 (*i)->show_selection (selection->time);
356 set_canvas_cursor ();
360 Editor::step_mouse_mode (bool next)
362 switch (current_mouse_mode()) {
365 if (Profile->get_sae()) {
366 set_mouse_mode (MouseZoom);
368 set_mouse_mode (MouseRange);
371 set_mouse_mode (MouseTimeFX);
376 if (next) set_mouse_mode (MouseZoom);
377 else set_mouse_mode (MouseObject);
382 if (Profile->get_sae()) {
383 set_mouse_mode (MouseTimeFX);
385 set_mouse_mode (MouseGain);
388 if (Profile->get_sae()) {
389 set_mouse_mode (MouseObject);
391 set_mouse_mode (MouseRange);
397 if (next) set_mouse_mode (MouseTimeFX);
398 else set_mouse_mode (MouseZoom);
403 set_mouse_mode (MouseAudition);
405 if (Profile->get_sae()) {
406 set_mouse_mode (MouseZoom);
408 set_mouse_mode (MouseGain);
414 if (next) set_mouse_mode (MouseObject);
415 else set_mouse_mode (MouseTimeFX);
421 Editor::button_selection (ArdourCanvas::Item* /*item*/, GdkEvent* event, ItemType item_type)
423 /* in object/audition/timefx/gain-automation mode,
424 any button press sets the selection if the object
425 can be selected. this is a bit of hack, because
426 we want to avoid this if the mouse operation is a
429 note: not dbl-click or triple-click
431 Also note that there is no region selection in internal edit mode, otherwise
432 for operations operating on the selection (e.g. cut) it is not obvious whether
433 to cut notes or regions.
436 if (((mouse_mode != MouseObject) &&
437 (_join_object_range_state != JOIN_OBJECT_RANGE_OBJECT) &&
438 (mouse_mode != MouseAudition || item_type != RegionItem) &&
439 (mouse_mode != MouseTimeFX || item_type != RegionItem) &&
440 (mouse_mode != MouseGain) &&
441 (mouse_mode != MouseRange)) ||
442 ((event->type != GDK_BUTTON_PRESS && event->type != GDK_BUTTON_RELEASE) || event->button.button > 3) ||
443 internal_editing()) {
448 if (event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) {
450 if ((event->button.state & Keyboard::RelevantModifierKeyMask) && event->button.button != 1) {
452 /* almost no selection action on modified button-2 or button-3 events */
454 if (item_type != RegionItem && event->button.button != 2) {
460 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
461 bool press = (event->type == GDK_BUTTON_PRESS);
463 // begin_reversible_command (_("select on click"));
467 if (mouse_mode != MouseRange || _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
468 set_selected_regionview_from_click (press, op, true);
469 } else if (event->type == GDK_BUTTON_PRESS) {
470 selection->clear_tracks ();
471 set_selected_track_as_side_effect (true);
473 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT && !selection->regions.empty()) {
474 clicked_selection = select_range_around_region (selection->regions.front());
478 case RegionViewNameHighlight:
480 case LeftFrameHandle:
481 case RightFrameHandle:
482 if (mouse_mode != MouseRange || _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
483 set_selected_regionview_from_click (press, op, true);
484 } else if (event->type == GDK_BUTTON_PRESS) {
485 set_selected_track_as_side_effect ();
490 case FadeInHandleItem:
492 case FadeOutHandleItem:
494 if (mouse_mode != MouseRange || _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
495 set_selected_regionview_from_click (press, op, true);
496 } else if (event->type == GDK_BUTTON_PRESS) {
497 set_selected_track_as_side_effect ();
501 case ControlPointItem:
502 set_selected_track_as_side_effect ();
503 if (mouse_mode != MouseRange || _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
504 set_selected_control_point_from_click (op, false);
509 /* for context click, select track */
510 if (event->button.button == 3) {
511 selection->clear_tracks ();
512 set_selected_track_as_side_effect (true);
516 case AutomationTrackItem:
517 set_selected_track_as_side_effect (true);
526 Editor::button_press_handler_1 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
528 if (_drags->active ()) {
532 /* single mouse clicks on any of these item types operate
533 independent of mouse mode, mostly because they are
534 not on the main track canvas or because we want
539 case PlayheadCursorItem:
540 _drags->set (new CursorDrag (this, item, true), event);
544 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
545 hide_marker (item, event);
547 _drags->set (new MarkerDrag (this, item), event);
551 case TempoMarkerItem:
553 new TempoMarkerDrag (
556 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
562 case MeterMarkerItem:
564 new MeterMarkerDrag (
567 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
576 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
577 _drags->set (new CursorDrag (this, &playhead_cursor->canvas_item, false), event);
583 case RangeMarkerBarItem:
584 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
585 _drags->set (new CursorDrag (this, &playhead_cursor->canvas_item, false), event);
587 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateRangeMarker), event);
592 case CdMarkerBarItem:
593 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
594 _drags->set (new CursorDrag (this, &playhead_cursor->canvas_item, false), event);
596 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateCDMarker), event);
601 case TransportMarkerBarItem:
602 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
603 _drags->set (new CursorDrag (this, &playhead_cursor->canvas_item, false), event);
605 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateTransportMarker), event);
614 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
615 /* special case: allow trim of range selections in joined object mode;
616 in theory eff should equal MouseRange in this case, but it doesn't
617 because entering the range selection canvas item results in entered_regionview
618 being set to 0, so update_join_object_range_location acts as if we aren't
621 if (item_type == StartSelectionTrimItem) {
622 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
623 } else if (item_type == EndSelectionTrimItem) {
624 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
628 Editing::MouseMode eff = effective_mouse_mode ();
630 /* special case: allow drag of region fade in/out in object mode with join object/range enabled */
631 if (item_type == FadeInHandleItem || item_type == FadeOutHandleItem) {
638 case StartSelectionTrimItem:
639 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
642 case EndSelectionTrimItem:
643 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
647 if (Keyboard::modifier_state_contains
648 (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier))) {
649 // contains and not equals because I can't use alt as a modifier alone.
650 start_selection_grab (item, event);
651 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
652 /* grab selection for moving */
653 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionMove), event);
655 double const y = event->button.y + vertical_adjustment.get_value() - canvas_timebars_vsize;
656 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
658 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
659 if (join_object_range_button.get_active() && atv) {
660 /* smart "join" mode: drag automation */
661 _drags->set (new AutomationRangeDrag (this, atv->base_item(), selection->time), event);
663 /* this was debated, but decided the more common action was to
664 make a new selection */
665 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
672 if (internal_editing()) {
673 /* trim notes if we're in internal edit mode and near the ends of the note */
674 _drags->set (new NoteResizeDrag (this, item), event);
679 if (internal_editing()) {
680 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
683 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
688 case RegionViewNameHighlight:
689 case LeftFrameHandle:
690 case RightFrameHandle:
692 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
693 _drags->set (new TrimDrag (this, item, clicked_regionview, s.by_layer()), event);
699 if (!internal_editing()) {
700 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
709 if (internal_editing()) {
710 _drags->set (new NoteDrag (this, item), event);
719 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)) &&
720 event->type == GDK_BUTTON_PRESS) {
722 _drags->set (new RubberbandSelectDrag (this, item), event);
724 } else if (event->type == GDK_BUTTON_PRESS) {
727 case FadeInHandleItem:
729 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
730 _drags->set (new FadeInDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), s), event);
734 case FadeOutHandleItem:
736 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
737 _drags->set (new FadeOutDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), s), event);
741 case FeatureLineItem:
743 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier)) {
744 remove_transient(item);
748 _drags->set (new FeatureLineDrag (this, item), event);
754 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
755 add_region_copy_drag (item, event, clicked_regionview);
757 else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
758 add_region_brush_drag (item, event, clicked_regionview);
760 add_region_drag (item, event, clicked_regionview);
763 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT && !selection->regions.empty()) {
764 _drags->add (new SelectionDrag (this, clicked_axisview->get_selection_rect (clicked_selection)->rect, SelectionDrag::SelectionMove));
767 _drags->start_grab (event);
770 case RegionViewNameHighlight:
771 case LeftFrameHandle:
772 case RightFrameHandle:
773 if (!internal_editing ()) {
774 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
775 _drags->set (new TrimDrag (this, item, clicked_regionview, s.by_layer()), event);
782 /* rename happens on edit clicks */
783 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
784 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, s.by_layer()), event);
789 case ControlPointItem:
790 _drags->set (new ControlPointDrag (this, item), event);
794 case AutomationLineItem:
795 _drags->set (new LineDrag (this, item), event);
800 if (internal_editing()) {
801 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
804 _drags->set (new RubberbandSelectDrag (this, item), event);
808 case AutomationTrackItem:
809 /* rubberband drag to select automation points */
810 _drags->set (new RubberbandSelectDrag (this, item), event);
815 if (join_object_range_button.get_active()) {
816 /* we're in "smart" joined mode, and we've clicked on a Selection */
817 double const y = event->button.y + vertical_adjustment.get_value() - canvas_timebars_vsize;
818 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
820 /* if we're over an automation track, start a drag of its data */
821 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
823 _drags->set (new AutomationRangeDrag (this, atv->base_item(), selection->time), event);
826 /* if we're over a track and a region, and in the `object' part of a region,
827 put a selection around the region and drag both
829 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
830 if (rtv && _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
831 boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track> (rtv->route ());
833 boost::shared_ptr<Playlist> pl = t->playlist ();
836 boost::shared_ptr<Region> r = pl->top_region_at (event_frame (event));
838 RegionView* rv = rtv->view()->find_view (r);
839 clicked_selection = select_range_around_region (rv);
840 _drags->add (new SelectionDrag (this, item, SelectionDrag::SelectionMove));
841 list<RegionView*> rvs;
843 _drags->add (new RegionMoveDrag (this, item, rv, rvs, false, false));
844 _drags->start_grab (event);
855 case ImageFrameHandleStartItem:
856 imageframe_start_handle_op(item, event) ;
859 case ImageFrameHandleEndItem:
860 imageframe_end_handle_op(item, event) ;
863 case MarkerViewHandleStartItem:
864 markerview_item_start_handle_op(item, event) ;
867 case MarkerViewHandleEndItem:
868 markerview_item_end_handle_op(item, event) ;
872 start_markerview_grab(item, event) ;
875 start_imageframe_grab(item, event) ;
893 /* start a grab so that if we finish after moving
894 we can tell what happened.
896 _drags->set (new RegionGainDrag (this, item), event, current_canvas_cursor);
900 _drags->set (new LineDrag (this, item), event);
903 case ControlPointItem:
904 _drags->set (new ControlPointDrag (this, item), event);
915 case ControlPointItem:
916 _drags->set (new ControlPointDrag (this, item), event);
919 case AutomationLineItem:
920 _drags->set (new LineDrag (this, item), event);
924 // XXX need automation mode to identify which
926 // start_line_grab_from_regionview (item, event);
936 if (event->type == GDK_BUTTON_PRESS) {
937 _drags->set (new MouseZoomDrag (this, item), event);
944 if (internal_editing() && item_type == NoteItem) {
945 /* drag notes if we're in internal edit mode */
946 _drags->set (new NoteResizeDrag (this, item), event);
948 } else if ((!internal_editing() || dynamic_cast<AudioRegionView*> (clicked_regionview)) && clicked_regionview) {
949 /* do time-FX if we're not in internal edit mode, or we are but we clicked on an audio region */
950 _drags->set (new TimeFXDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
956 _drags->set (new ScrubDrag (this, item), event);
958 scrub_reverse_distance = 0;
959 last_scrub_x = event->button.x;
960 scrubbing_direction = 0;
961 track_canvas->get_window()->set_cursor (*transparent_cursor);
973 Editor::button_press_handler_2 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
975 Editing::MouseMode const eff = effective_mouse_mode ();
980 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
981 add_region_copy_drag (item, event, clicked_regionview);
983 add_region_drag (item, event, clicked_regionview);
985 _drags->start_grab (event);
988 case ControlPointItem:
989 _drags->set (new ControlPointDrag (this, item), event);
998 case RegionViewNameHighlight:
999 case LeftFrameHandle:
1000 case RightFrameHandle:
1001 if (!internal_editing ()) {
1002 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1007 case RegionViewName:
1008 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer()), event);
1019 /* relax till release */
1025 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1026 temporal_zoom_session();
1028 temporal_zoom_to_frame (true, event_frame(event));
1041 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1043 if (event->type != GDK_BUTTON_PRESS) {
1047 Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->track_canvas->get_window();
1049 if (canvas_window) {
1050 Glib::RefPtr<const Gdk::Window> pointer_window;
1053 Gdk::ModifierType mask;
1055 pointer_window = canvas_window->get_pointer (x, y, mask);
1057 if (pointer_window == track_canvas->get_bin_window()) {
1058 track_canvas->window_to_world (x, y, wx, wy);
1062 track_canvas->grab_focus();
1064 if (_session && _session->actively_recording()) {
1068 button_selection (item, event, item_type);
1070 if (!_drags->active () &&
1071 (Keyboard::is_delete_event (&event->button) ||
1072 Keyboard::is_context_menu_event (&event->button) ||
1073 Keyboard::is_edit_event (&event->button))) {
1075 /* handled by button release */
1079 switch (event->button.button) {
1081 return button_press_handler_1 (item, event, item_type);
1085 return button_press_handler_2 (item, event, item_type);
1100 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1102 nframes64_t where = event_frame (event, 0, 0);
1103 AutomationTimeAxisView* atv = 0;
1105 /* no action if we're recording */
1107 if (_session && _session->actively_recording()) {
1111 /* first, see if we're finishing a drag ... */
1113 bool were_dragging = false;
1114 if (_drags->active ()) {
1115 bool const r = _drags->end_grab (event);
1117 /* grab dragged, so do nothing else */
1121 were_dragging = true;
1124 button_selection (item, event, item_type);
1126 /* edit events get handled here */
1128 if (!_drags->active () && Keyboard::is_edit_event (&event->button)) {
1129 switch (item_type) {
1134 case TempoMarkerItem:
1135 edit_tempo_marker (item);
1138 case MeterMarkerItem:
1139 edit_meter_marker (item);
1142 case RegionViewName:
1143 if (clicked_regionview->name_active()) {
1144 return mouse_rename_region (item, event);
1148 case ControlPointItem:
1149 edit_control_point (item);
1158 /* context menu events get handled here */
1160 if (Keyboard::is_context_menu_event (&event->button)) {
1162 if (!_drags->active ()) {
1164 /* no matter which button pops up the context menu, tell the menu
1165 widget to use button 1 to drive menu selection.
1168 switch (item_type) {
1170 case FadeInHandleItem:
1172 case FadeOutHandleItem:
1173 popup_fade_context_menu (1, event->button.time, item, item_type);
1177 popup_track_context_menu (1, event->button.time, item_type, false, where);
1181 case RegionViewNameHighlight:
1182 case LeftFrameHandle:
1183 case RightFrameHandle:
1184 case RegionViewName:
1185 popup_track_context_menu (1, event->button.time, item_type, false, where);
1189 popup_track_context_menu (1, event->button.time, item_type, true, where);
1192 case AutomationTrackItem:
1193 popup_track_context_menu (1, event->button.time, item_type, false, where);
1197 case RangeMarkerBarItem:
1198 case TransportMarkerBarItem:
1199 case CdMarkerBarItem:
1202 popup_ruler_menu (where, item_type);
1206 marker_context_menu (&event->button, item);
1209 case TempoMarkerItem:
1210 tempo_or_meter_marker_context_menu (&event->button, item);
1213 case MeterMarkerItem:
1214 tempo_or_meter_marker_context_menu (&event->button, item);
1217 case CrossfadeViewItem:
1218 popup_track_context_menu (1, event->button.time, item_type, false, where);
1222 case ImageFrameItem:
1223 popup_imageframe_edit_menu(1, event->button.time, item, true) ;
1225 case ImageFrameTimeAxisItem:
1226 popup_imageframe_edit_menu(1, event->button.time, item, false) ;
1228 case MarkerViewItem:
1229 popup_marker_time_axis_edit_menu(1, event->button.time, item, true) ;
1231 case MarkerTimeAxisItem:
1232 popup_marker_time_axis_edit_menu(1, event->button.time, item, false) ;
1244 /* delete events get handled here */
1246 Editing::MouseMode const eff = effective_mouse_mode ();
1248 if (!_drags->active () && Keyboard::is_delete_event (&event->button)) {
1250 switch (item_type) {
1251 case TempoMarkerItem:
1252 remove_tempo_marker (item);
1255 case MeterMarkerItem:
1256 remove_meter_marker (item);
1260 remove_marker (*item, event);
1264 if (eff == MouseObject) {
1265 remove_clicked_region ();
1269 case ControlPointItem:
1270 if (eff == MouseGain) {
1271 remove_gain_control_point (item, event);
1273 remove_control_point (item, event);
1278 remove_midi_note (item, event);
1287 switch (event->button.button) {
1290 switch (item_type) {
1291 /* see comments in button_press_handler */
1292 case PlayheadCursorItem:
1295 case AutomationLineItem:
1296 case StartSelectionTrimItem:
1297 case EndSelectionTrimItem:
1301 if (!_dragging_playhead) {
1302 snap_to_with_modifier (where, event, 0, true);
1303 mouse_add_new_marker (where);
1307 case CdMarkerBarItem:
1308 if (!_dragging_playhead) {
1309 // if we get here then a dragged range wasn't done
1310 snap_to_with_modifier (where, event, 0, true);
1311 mouse_add_new_marker (where, true);
1316 if (!_dragging_playhead) {
1317 snap_to_with_modifier (where, event);
1318 mouse_add_new_tempo_event (where);
1323 if (!_dragging_playhead) {
1324 mouse_add_new_meter_event (pixel_to_frame (event->button.x));
1335 switch (item_type) {
1336 case AutomationTrackItem:
1337 atv = dynamic_cast<AutomationTimeAxisView*>(clicked_axisview);
1339 atv->add_automation_event (item, event, where, event->button.y);
1350 switch (item_type) {
1353 /* check that we didn't drag before releasing, since
1354 its really annoying to create new control
1355 points when doing this.
1357 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (clicked_regionview);
1358 if (were_dragging && arv) {
1359 arv->add_gain_point_event (item, event);
1365 case AutomationTrackItem:
1366 dynamic_cast<AutomationTimeAxisView*>(clicked_axisview)->
1367 add_automation_event (item, event, where, event->button.y);
1376 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1377 if (scrubbing_direction == 0) {
1378 /* no drag, just a click */
1379 switch (item_type) {
1381 play_selected_region ();
1387 /* make sure we stop */
1388 _session->request_transport_speed (0.0);
1405 switch (item_type) {
1407 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1409 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier|Keyboard::SecondaryModifier))) {
1412 // Button2 click is unused
1425 // x_style_paste (where, 1.0);
1445 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1452 if (last_item_entered != item) {
1453 last_item_entered = item;
1454 last_item_entered_n = 0;
1457 switch (item_type) {
1458 case ControlPointItem:
1459 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1460 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1461 cp->set_visible (true);
1465 at_y = cp->get_y ();
1466 cp->i2w (at_x, at_y);
1470 fraction = 1.0 - (cp->get_y() / cp->line().height());
1472 if (is_drawable() && !_drags->active ()) {
1473 track_canvas->get_window()->set_cursor (*fader_cursor);
1476 last_item_entered_n++;
1477 set_verbose_canvas_cursor (cp->line().get_verbose_cursor_string (fraction), at_x, at_y);
1478 if (last_item_entered_n < 10) {
1479 show_verbose_canvas_cursor ();
1485 if (mouse_mode == MouseGain) {
1486 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1488 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredGainLine.get();
1489 if (is_drawable()) {
1490 track_canvas->get_window()->set_cursor (*fader_cursor);
1495 case AutomationLineItem:
1496 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1498 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1500 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredAutomationLine.get();
1502 if (is_drawable()) {
1503 track_canvas->get_window()->set_cursor (*fader_cursor);
1508 case RegionViewNameHighlight:
1509 if (is_drawable() && mouse_mode == MouseObject && !internal_editing()) {
1510 track_canvas->get_window()->set_cursor (*trimmer_cursor);
1514 case LeftFrameHandle:
1515 if (is_drawable() && mouse_mode == MouseObject && !internal_editing()) {
1516 track_canvas->get_window()->set_cursor (*left_side_trim_cursor);
1520 case RightFrameHandle:
1521 if (is_drawable() && mouse_mode == MouseObject && !internal_editing()) {
1522 track_canvas->get_window()->set_cursor (*right_side_trim_cursor);
1526 case StartSelectionTrimItem:
1527 case EndSelectionTrimItem:
1530 case ImageFrameHandleStartItem:
1531 case ImageFrameHandleEndItem:
1532 case MarkerViewHandleStartItem:
1533 case MarkerViewHandleEndItem:
1536 if (is_drawable()) {
1537 track_canvas->get_window()->set_cursor (*trimmer_cursor);
1541 case PlayheadCursorItem:
1542 if (is_drawable()) {
1543 switch (_edit_point) {
1545 track_canvas->get_window()->set_cursor (*grabber_edit_point_cursor);
1548 track_canvas->get_window()->set_cursor (*grabber_cursor);
1554 case RegionViewName:
1556 /* when the name is not an active item, the entire name highlight is for trimming */
1558 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1559 if (mouse_mode == MouseObject && is_drawable()) {
1560 track_canvas->get_window()->set_cursor (*trimmer_cursor);
1566 case AutomationTrackItem:
1567 if (is_drawable()) {
1568 Gdk::Cursor *cursor;
1569 switch (mouse_mode) {
1571 cursor = selector_cursor;
1574 cursor = zoom_cursor;
1577 cursor = cross_hair_cursor;
1581 track_canvas->get_window()->set_cursor (*cursor);
1583 AutomationTimeAxisView* atv;
1584 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1585 clear_entered_track = false;
1586 set_entered_track (atv);
1592 case RangeMarkerBarItem:
1593 case TransportMarkerBarItem:
1594 case CdMarkerBarItem:
1597 if (is_drawable()) {
1598 track_canvas->get_window()->set_cursor (*timebar_cursor);
1603 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1606 entered_marker = marker;
1607 marker->set_color_rgba (ARDOUR_UI::config()->canvasvar_EnteredMarker.get());
1609 case MeterMarkerItem:
1610 case TempoMarkerItem:
1611 if (is_drawable()) {
1612 track_canvas->get_window()->set_cursor (*timebar_cursor);
1616 case FadeInHandleItem:
1617 if (mouse_mode == MouseObject && !internal_editing()) {
1618 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1620 rect->property_fill_color_rgba() = 0;
1621 rect->property_outline_pixels() = 1;
1623 track_canvas->get_window()->set_cursor (*fade_in_cursor);
1627 case FadeOutHandleItem:
1628 if (mouse_mode == MouseObject && !internal_editing()) {
1629 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1631 rect->property_fill_color_rgba() = 0;
1632 rect->property_outline_pixels() = 1;
1634 track_canvas->get_window()->set_cursor (*fade_out_cursor);
1637 case FeatureLineItem:
1639 ArdourCanvas::SimpleLine *line = dynamic_cast<ArdourCanvas::SimpleLine *> (item);
1640 line->property_color_rgba() = 0xFF0000FF;
1647 /* second pass to handle entered track status in a comprehensible way.
1650 switch (item_type) {
1652 case AutomationLineItem:
1653 case ControlPointItem:
1654 /* these do not affect the current entered track state */
1655 clear_entered_track = false;
1658 case AutomationTrackItem:
1659 /* handled above already */
1663 set_entered_track (0);
1671 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1681 switch (item_type) {
1682 case ControlPointItem:
1683 cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
1684 if (cp->line().the_list()->interpolation() != AutomationList::Discrete) {
1685 if (cp->line().npoints() > 1 && !cp->selected()) {
1686 cp->set_visible (false);
1690 if (is_drawable()) {
1691 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1694 hide_verbose_canvas_cursor ();
1697 case RegionViewNameHighlight:
1698 case LeftFrameHandle:
1699 case RightFrameHandle:
1700 case StartSelectionTrimItem:
1701 case EndSelectionTrimItem:
1702 case PlayheadCursorItem:
1705 case ImageFrameHandleStartItem:
1706 case ImageFrameHandleEndItem:
1707 case MarkerViewHandleStartItem:
1708 case MarkerViewHandleEndItem:
1711 if (is_drawable()) {
1712 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1717 case AutomationLineItem:
1718 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1720 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1722 line->property_fill_color_rgba() = al->get_line_color();
1724 if (is_drawable()) {
1725 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1729 case RegionViewName:
1730 /* see enter_handler() for notes */
1731 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1732 if (is_drawable() && mouse_mode == MouseObject) {
1733 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1738 case RangeMarkerBarItem:
1739 case TransportMarkerBarItem:
1740 case CdMarkerBarItem:
1744 if (is_drawable()) {
1745 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1750 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1754 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
1755 location_flags_changed (loc, this);
1758 case MeterMarkerItem:
1759 case TempoMarkerItem:
1761 if (is_drawable()) {
1762 track_canvas->get_window()->set_cursor (*timebar_cursor);
1767 case FadeInHandleItem:
1768 case FadeOutHandleItem:
1769 rv = static_cast<RegionView*>(item->get_data ("regionview"));
1771 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1773 rect->property_fill_color_rgba() = rv->get_fill_color();
1774 rect->property_outline_pixels() = 0;
1777 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1780 case AutomationTrackItem:
1781 if (is_drawable()) {
1782 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1783 clear_entered_track = true;
1784 Glib::signal_idle().connect (sigc::mem_fun(*this, &Editor::left_automation_track));
1787 case FeatureLineItem:
1789 ArdourCanvas::SimpleLine *line = dynamic_cast<ArdourCanvas::SimpleLine *> (item);
1790 line->property_color_rgba() = (guint) ARDOUR_UI::config()->canvasvar_ZeroLine.get();;
1802 Editor::left_automation_track ()
1804 if (clear_entered_track) {
1805 set_entered_track (0);
1806 clear_entered_track = false;
1812 Editor::scrub (nframes64_t frame, double current_x)
1816 if (scrubbing_direction == 0) {
1818 _session->request_locate (frame, false);
1819 _session->request_transport_speed (0.1);
1820 scrubbing_direction = 1;
1824 if (last_scrub_x > current_x) {
1826 /* pointer moved to the left */
1828 if (scrubbing_direction > 0) {
1830 /* we reversed direction to go backwards */
1833 scrub_reverse_distance += (int) (last_scrub_x - current_x);
1837 /* still moving to the left (backwards) */
1839 scrub_reversals = 0;
1840 scrub_reverse_distance = 0;
1842 delta = 0.01 * (last_scrub_x - current_x);
1843 _session->request_transport_speed (_session->transport_speed() - delta);
1847 /* pointer moved to the right */
1849 if (scrubbing_direction < 0) {
1850 /* we reversed direction to go forward */
1853 scrub_reverse_distance += (int) (current_x - last_scrub_x);
1856 /* still moving to the right */
1858 scrub_reversals = 0;
1859 scrub_reverse_distance = 0;
1861 delta = 0.01 * (current_x - last_scrub_x);
1862 _session->request_transport_speed (_session->transport_speed() + delta);
1866 /* if there have been more than 2 opposite motion moves detected, or one that moves
1867 back more than 10 pixels, reverse direction
1870 if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
1872 if (scrubbing_direction > 0) {
1873 /* was forwards, go backwards */
1874 _session->request_transport_speed (-0.1);
1875 scrubbing_direction = -1;
1877 /* was backwards, go forwards */
1878 _session->request_transport_speed (0.1);
1879 scrubbing_direction = 1;
1882 scrub_reverse_distance = 0;
1883 scrub_reversals = 0;
1887 last_scrub_x = current_x;
1891 Editor::motion_handler (ArdourCanvas::Item* /*item*/, GdkEvent* event, bool from_autoscroll)
1893 if (event->motion.is_hint) {
1896 /* We call this so that MOTION_NOTIFY events continue to be
1897 delivered to the canvas. We need to do this because we set
1898 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
1899 the density of the events, at the expense of a round-trip
1900 to the server. Given that this will mostly occur on cases
1901 where DISPLAY = :0.0, and given the cost of what the motion
1902 event might do, its a good tradeoff.
1905 track_canvas->get_pointer (x, y);
1908 if (current_stepping_trackview) {
1909 /* don't keep the persistent stepped trackview if the mouse moves */
1910 current_stepping_trackview = 0;
1911 step_timeout.disconnect ();
1914 if (_session && _session->actively_recording()) {
1915 /* Sorry. no dragging stuff around while we record */
1919 JoinObjectRangeState const old = _join_object_range_state;
1920 update_join_object_range_location (event->motion.x, event->motion.y);
1921 if (_join_object_range_state != old) {
1922 set_canvas_cursor ();
1925 bool handled = false;
1926 if (_drags->active ()) {
1927 handled = _drags->motion_handler (event, from_autoscroll);
1933 track_canvas_motion (event);
1938 Editor::remove_gain_control_point (ArdourCanvas::Item*item, GdkEvent* /*event*/)
1940 ControlPoint* control_point;
1942 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
1943 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
1947 // We shouldn't remove the first or last gain point
1948 if (control_point->line().is_last_point(*control_point) ||
1949 control_point->line().is_first_point(*control_point)) {
1953 control_point->line().remove_point (*control_point);
1957 Editor::remove_control_point (ArdourCanvas::Item* item, GdkEvent* /*event*/)
1959 ControlPoint* control_point;
1961 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
1962 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
1966 control_point->line().remove_point (*control_point);
1970 Editor::edit_control_point (ArdourCanvas::Item* item)
1972 ControlPoint* p = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"));
1975 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
1979 ControlPointDialog d (p);
1980 d.set_position (Gtk::WIN_POS_MOUSE);
1983 if (d.run () != RESPONSE_ACCEPT) {
1987 p->line().modify_point_y (*p, d.get_y_fraction ());
1992 Editor::visible_order_range (int* low, int* high) const
1994 *low = TimeAxisView::max_order ();
1997 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
1999 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
2001 if (!rtv->hidden()) {
2003 if (*high < rtv->order()) {
2004 *high = rtv->order ();
2007 if (*low > rtv->order()) {
2008 *low = rtv->order ();
2015 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
2017 /* Either add to or set the set the region selection, unless
2018 this is an alignment click (control used)
2021 if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
2022 TimeAxisView* tv = &rv.get_time_axis_view();
2023 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
2025 if (rtv && rtv->is_track()) {
2026 speed = rtv->track()->speed();
2029 nframes64_t where = get_preferred_edit_position();
2033 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
2035 align_region (rv.region(), SyncPoint, (nframes64_t) (where * speed));
2037 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
2039 align_region (rv.region(), End, (nframes64_t) (where * speed));
2043 align_region (rv.region(), Start, (nframes64_t) (where * speed));
2050 Editor::show_verbose_time_cursor (nframes64_t frame, double offset, double xpos, double ypos)
2053 Timecode::Time timecode;
2056 nframes64_t frame_rate;
2059 if (_session == 0) {
2065 if (Profile->get_sae() || Profile->get_small_screen()) {
2066 m = ARDOUR_UI::instance()->primary_clock.mode();
2068 m = ARDOUR_UI::instance()->secondary_clock.mode();
2072 case AudioClock::BBT:
2073 _session->bbt_time (frame, bbt);
2074 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, bbt.bars, bbt.beats, bbt.ticks);
2077 case AudioClock::Timecode:
2078 _session->timecode_time (frame, timecode);
2079 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
2082 case AudioClock::MinSec:
2083 /* XXX this is copied from show_verbose_duration_cursor() */
2084 frame_rate = _session->frame_rate();
2085 hours = frame / (frame_rate * 3600);
2086 frame = frame % (frame_rate * 3600);
2087 mins = frame / (frame_rate * 60);
2088 frame = frame % (frame_rate * 60);
2089 secs = (float) frame / (float) frame_rate;
2090 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%07.4f", hours, mins, secs);
2094 snprintf (buf, sizeof(buf), "%" PRIi64, frame);
2098 if (xpos >= 0 && ypos >=0) {
2099 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
2101 set_verbose_canvas_cursor (buf, _drags->current_pointer_x() + offset - horizontal_position(), _drags->current_pointer_y() + offset - vertical_adjustment.get_value() + canvas_timebars_vsize);
2103 show_verbose_canvas_cursor ();
2107 Editor::show_verbose_duration_cursor (nframes64_t start, nframes64_t end, double offset, double xpos, double ypos)
2110 Timecode::Time timecode;
2114 nframes64_t distance, frame_rate;
2116 Meter meter_at_start(_session->tempo_map().meter_at(start));
2118 if (_session == 0) {
2124 if (Profile->get_sae() || Profile->get_small_screen()) {
2125 m = ARDOUR_UI::instance()->primary_clock.mode ();
2127 m = ARDOUR_UI::instance()->secondary_clock.mode ();
2131 case AudioClock::BBT:
2132 _session->bbt_time (start, sbbt);
2133 _session->bbt_time (end, ebbt);
2136 /* XXX this computation won't work well if the
2137 user makes a selection that spans any meter changes.
2140 ebbt.bars -= sbbt.bars;
2141 if (ebbt.beats >= sbbt.beats) {
2142 ebbt.beats -= sbbt.beats;
2145 ebbt.beats = int(meter_at_start.beats_per_bar()) + ebbt.beats - sbbt.beats;
2147 if (ebbt.ticks >= sbbt.ticks) {
2148 ebbt.ticks -= sbbt.ticks;
2151 ebbt.ticks = int(Meter::ticks_per_beat) + ebbt.ticks - sbbt.ticks;
2154 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, ebbt.bars, ebbt.beats, ebbt.ticks);
2157 case AudioClock::Timecode:
2158 _session->timecode_duration (end - start, timecode);
2159 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
2162 case AudioClock::MinSec:
2163 /* XXX this stuff should be elsewhere.. */
2164 distance = end - start;
2165 frame_rate = _session->frame_rate();
2166 hours = distance / (frame_rate * 3600);
2167 distance = distance % (frame_rate * 3600);
2168 mins = distance / (frame_rate * 60);
2169 distance = distance % (frame_rate * 60);
2170 secs = (float) distance / (float) frame_rate;
2171 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%07.4f", hours, mins, secs);
2175 snprintf (buf, sizeof(buf), "%" PRIi64, end - start);
2179 if (xpos >= 0 && ypos >=0) {
2180 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
2183 set_verbose_canvas_cursor (buf, _drags->current_pointer_x() + offset, _drags->current_pointer_y() + offset);
2186 show_verbose_canvas_cursor ();
2190 Editor::collect_new_region_view (RegionView* rv)
2192 latest_regionviews.push_back (rv);
2196 Editor::collect_and_select_new_region_view (RegionView* rv)
2199 latest_regionviews.push_back (rv);
2203 Editor::cancel_selection ()
2205 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2206 (*i)->hide_selection ();
2209 selection->clear ();
2210 clicked_selection = 0;
2215 Editor::single_contents_trim (RegionView& rv, nframes64_t frame_delta, bool left_direction, bool swap_direction)
2217 boost::shared_ptr<Region> region (rv.region());
2219 if (region->locked()) {
2223 nframes64_t new_bound;
2226 TimeAxisView* tvp = clicked_axisview;
2227 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2229 if (tv && tv->is_track()) {
2230 speed = tv->track()->speed();
2233 if (left_direction) {
2234 if (swap_direction) {
2235 new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
2237 new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
2240 if (swap_direction) {
2241 new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
2243 new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
2247 region->trim_start ((nframes64_t) (new_bound * speed), this);
2248 rv.region_changed (PropertyChange (ARDOUR::Properties::start));
2252 Editor::single_start_trim (RegionView& rv, nframes64_t new_bound, bool no_overlap)
2254 boost::shared_ptr<Region> region (rv.region());
2256 if (region->locked()) {
2261 TimeAxisView* tvp = clicked_axisview;
2262 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2264 if (tv && tv->is_track()) {
2265 speed = tv->track()->speed();
2268 nframes64_t pre_trim_first_frame = region->first_frame();
2270 region->trim_front ((nframes64_t) (new_bound * speed), this);
2273 //Get the next region on the left of this region and shrink/expand it.
2274 boost::shared_ptr<Playlist> playlist (region->playlist());
2275 boost::shared_ptr<Region> region_left = playlist->find_next_region (pre_trim_first_frame, End, 0);
2277 bool regions_touching = false;
2279 if (region_left != 0 && (pre_trim_first_frame == region_left->last_frame() + 1)){
2280 regions_touching = true;
2283 //Only trim region on the left if the first frame has gone beyond the left region's last frame.
2284 if (region_left != 0 &&
2285 (region_left->last_frame() > region->first_frame() || regions_touching))
2287 region_left->trim_end(region->first_frame() - 1, this);
2291 rv.region_changed (ARDOUR::bounds_change);
2295 Editor::single_end_trim (RegionView& rv, nframes64_t new_bound, bool no_overlap)
2297 boost::shared_ptr<Region> region (rv.region());
2299 if (region->locked()) {
2304 TimeAxisView* tvp = clicked_axisview;
2305 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2307 if (tv && tv->is_track()) {
2308 speed = tv->track()->speed();
2311 nframes64_t pre_trim_last_frame = region->last_frame();
2313 region->trim_end ((nframes64_t) (new_bound * speed), this);
2316 //Get the next region on the right of this region and shrink/expand it.
2317 boost::shared_ptr<Playlist> playlist (region->playlist());
2318 boost::shared_ptr<Region> region_right = playlist->find_next_region (pre_trim_last_frame, Start, 1);
2320 bool regions_touching = false;
2322 if (region_right != 0 && (pre_trim_last_frame == region_right->first_frame() - 1)) {
2323 regions_touching = true;
2326 //Only trim region on the right if the last frame has gone beyond the right region's first frame.
2327 if (region_right != 0 &&
2328 (region_right->first_frame() < region->last_frame() || regions_touching))
2330 region_right->trim_front(region->last_frame() + 1, this);
2333 rv.region_changed (ARDOUR::bounds_change);
2336 rv.region_changed (PropertyChange (ARDOUR::Properties::length));
2342 Editor::point_trim (GdkEvent* event, nframes64_t new_bound)
2344 RegionView* rv = clicked_regionview;
2346 /* Choose action dependant on which button was pressed */
2347 switch (event->button.button) {
2349 begin_reversible_command (_("Start point trim"));
2351 if (selection->selected (rv)) {
2352 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
2353 i != selection->regions.by_layer().end(); ++i)
2356 cerr << "region view contains null region" << endl;
2359 if (!(*i)->region()->locked()) {
2360 (*i)->region()->clear_history ();
2361 (*i)->region()->trim_front (new_bound, this);
2362 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2367 if (!rv->region()->locked()) {
2368 rv->region()->clear_history ();
2369 rv->region()->trim_front (new_bound, this);
2370 _session->add_command(new StatefulDiffCommand (rv->region()));
2374 commit_reversible_command();
2378 begin_reversible_command (_("End point trim"));
2380 if (selection->selected (rv)) {
2382 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
2384 if (!(*i)->region()->locked()) {
2385 (*i)->region()->clear_history();
2386 (*i)->region()->trim_end (new_bound, this);
2387 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2393 if (!rv->region()->locked()) {
2394 rv->region()->clear_history ();
2395 rv->region()->trim_end (new_bound, this);
2396 _session->add_command (new StatefulDiffCommand (rv->region()));
2400 commit_reversible_command();
2409 Editor::thaw_region_after_trim (RegionView& rv)
2411 boost::shared_ptr<Region> region (rv.region());
2413 if (region->locked()) {
2417 region->resume_property_changes ();
2419 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(&rv);
2422 arv->unhide_envelope ();
2427 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* /*event*/)
2432 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2433 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2437 Location* location = find_location_from_marker (marker, is_start);
2438 location->set_hidden (true, this);
2443 Editor::reposition_zoom_rect (nframes64_t start, nframes64_t end)
2445 double x1 = frame_to_pixel (start);
2446 double x2 = frame_to_pixel (end);
2447 double y2 = full_canvas_height - 1.0;
2449 zoom_rect->property_x1() = x1;
2450 zoom_rect->property_y1() = 1.0;
2451 zoom_rect->property_x2() = x2;
2452 zoom_rect->property_y2() = y2;
2457 Editor::mouse_rename_region (ArdourCanvas::Item* /*item*/, GdkEvent* /*event*/)
2459 using namespace Gtkmm2ext;
2461 ArdourPrompter prompter (false);
2463 prompter.set_prompt (_("Name for region:"));
2464 prompter.set_initial_text (clicked_regionview->region()->name());
2465 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
2466 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
2467 prompter.show_all ();
2468 switch (prompter.run ()) {
2469 case Gtk::RESPONSE_ACCEPT:
2471 prompter.get_result(str);
2473 clicked_regionview->region()->set_name (str);
2482 Editor::mouse_brush_insert_region (RegionView* rv, nframes64_t pos)
2484 /* no brushing without a useful snap setting */
2486 switch (_snap_mode) {
2488 return; /* can't work because it allows region to be placed anywhere */
2493 switch (_snap_type) {
2501 /* don't brush a copy over the original */
2503 if (pos == rv->region()->position()) {
2507 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
2509 if (rtv == 0 || !rtv->is_track()) {
2513 boost::shared_ptr<Playlist> playlist = rtv->playlist();
2514 double speed = rtv->track()->speed();
2516 playlist->clear_history ();
2517 boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region()));
2518 playlist->add_region (new_region, (nframes64_t) (pos * speed));
2519 _session->add_command (new StatefulDiffCommand (playlist));
2521 // playlist is frozen, so we have to update manually XXX this is disgusting
2523 playlist->RegionAdded (new_region); /* EMIT SIGNAL */
2527 Editor::track_height_step_timeout ()
2529 if (get_microseconds() - last_track_height_step_timestamp < 250000) {
2530 current_stepping_trackview = 0;
2537 Editor::add_region_drag (ArdourCanvas::Item* item, GdkEvent* event, RegionView* region_view)
2539 assert (region_view);
2541 _region_motion_group->raise_to_top ();
2543 if (Config->get_edit_mode() == Splice) {
2544 _drags->add (new RegionSpliceDrag (this, item, region_view, selection->regions.by_layer()));
2546 RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id);
2547 _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), false, false));
2550 /* sync the canvas to what we think is its current state */
2551 update_canvas_now();
2555 Editor::add_region_copy_drag (ArdourCanvas::Item* item, GdkEvent* event, RegionView* region_view)
2557 assert (region_view);
2559 _region_motion_group->raise_to_top ();
2561 RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id);
2562 _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), false, true));
2566 Editor::add_region_brush_drag (ArdourCanvas::Item* item, GdkEvent* event, RegionView* region_view)
2568 assert (region_view);
2570 if (Config->get_edit_mode() == Splice) {
2574 RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id);
2575 _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), true, false));
2577 begin_reversible_command (_("Drag region brush"));
2580 /** Start a grab where a time range is selected, track(s) are selected, and the
2581 * user clicks and drags a region with a modifier in order to create a new region containing
2582 * the section of the clicked region that lies within the time range.
2585 Editor::start_selection_grab (ArdourCanvas::Item* /*item*/, GdkEvent* event)
2587 if (clicked_regionview == 0) {
2591 /* lets try to create new Region for the selection */
2593 vector<boost::shared_ptr<Region> > new_regions;
2594 create_region_from_selection (new_regions);
2596 if (new_regions.empty()) {
2600 /* XXX fix me one day to use all new regions */
2602 boost::shared_ptr<Region> region (new_regions.front());
2604 /* add it to the current stream/playlist.
2606 tricky: the streamview for the track will add a new regionview. we will
2607 catch the signal it sends when it creates the regionview to
2608 set the regionview we want to then drag.
2611 latest_regionviews.clear();
2612 sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
2614 /* A selection grab currently creates two undo/redo operations, one for
2615 creating the new region and another for moving it.
2618 begin_reversible_command (_("selection grab"));
2620 boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
2622 playlist->clear_history ();
2623 clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
2624 _session->add_command(new StatefulDiffCommand (playlist));
2626 commit_reversible_command ();
2630 if (latest_regionviews.empty()) {
2631 /* something went wrong */
2635 /* we need to deselect all other regionviews, and select this one
2636 i'm ignoring undo stuff, because the region creation will take care of it
2638 selection->set (latest_regionviews);
2640 _drags->set (new RegionMoveDrag (this, latest_regionviews.front()->get_canvas_group(), latest_regionviews.front(), latest_regionviews, false, false), event);
2646 if (_drags->active ()) {
2649 selection->clear ();
2654 Editor::set_internal_edit (bool yn)
2656 _internal_editing = yn;
2659 mouse_select_button.set_image (*(manage (new Image (::get_icon("midi_tool_pencil")))));
2660 mouse_select_button.get_image ()->show ();
2661 ARDOUR_UI::instance()->tooltips().set_tip (mouse_select_button, _("Draw/Edit MIDI Notes"));
2662 set_canvas_cursor ();
2664 /* deselect everything to avoid confusion when e.g. we can't now cut a previously selected
2665 region because cut means "cut note" rather than "cut region".
2667 selection->clear ();
2671 mouse_select_button.set_image (*(manage (new Image (::get_icon("tool_range")))));
2672 mouse_select_button.get_image ()->show ();
2673 ARDOUR_UI::instance()->tooltips().set_tip (mouse_select_button, _("Select/Move Ranges"));
2674 mouse_mode_toggled (mouse_mode); // sets cursor
2678 /** Update _join_object_range_state which indicate whether we are over the top or bottom half of a region view,
2679 * used by the `join object/range' tool mode.
2682 Editor::update_join_object_range_location (double x, double y)
2684 /* XXX: actually, this decides based on whether the mouse is in the top or bottom half of a RouteTimeAxisView;
2685 entered_{track,regionview} is not always setup (e.g. if the mouse is over a TimeSelection), and to get a Region
2686 that we're over requires searching the playlist.
2689 if (join_object_range_button.get_active() == false || (mouse_mode != MouseRange && mouse_mode != MouseObject)) {
2690 _join_object_range_state = JOIN_OBJECT_RANGE_NONE;
2694 if (mouse_mode == MouseObject) {
2695 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2696 } else if (mouse_mode == MouseRange) {
2697 _join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
2700 /* XXX: maybe we should make entered_track work in all cases, rather than resorting to this */
2701 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y + vertical_adjustment.get_value() - canvas_timebars_vsize);
2705 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
2710 rtv->canvas_display()->w2i (cx, cy);
2712 bool const top_half = cy < rtv->current_height () / 2;
2714 _join_object_range_state = top_half ? JOIN_OBJECT_RANGE_RANGE : JOIN_OBJECT_RANGE_OBJECT;
2720 Editor::effective_mouse_mode () const
2722 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
2724 } else if (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE) {
2732 Editor::remove_midi_note (ArdourCanvas::Item* item, GdkEvent *)
2734 ArdourCanvas::CanvasNoteEvent* e = dynamic_cast<ArdourCanvas::CanvasNoteEvent*> (item);
2737 e->region_view().delete_note (e->note ());