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;
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
432 if (((mouse_mode != MouseObject) &&
433 (_join_object_range_state != JOIN_OBJECT_RANGE_OBJECT) &&
434 (mouse_mode != MouseAudition || item_type != RegionItem) &&
435 (mouse_mode != MouseTimeFX || item_type != RegionItem) &&
436 (mouse_mode != MouseGain) &&
437 (mouse_mode != MouseRange)) ||
439 ((event->type != GDK_BUTTON_PRESS && event->type != GDK_BUTTON_RELEASE) || event->button.button > 3)) {
444 if (event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) {
446 if ((event->button.state & Keyboard::RelevantModifierKeyMask) && event->button.button != 1) {
448 /* almost no selection action on modified button-2 or button-3 events */
450 if (item_type != RegionItem && event->button.button != 2) {
456 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
457 bool press = (event->type == GDK_BUTTON_PRESS);
459 // begin_reversible_command (_("select on click"));
463 if (mouse_mode != MouseRange || internal_editing() || _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
464 set_selected_regionview_from_click (press, op, true);
465 } else if (event->type == GDK_BUTTON_PRESS) {
466 selection->clear_tracks ();
467 set_selected_track_as_side_effect (true);
469 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT && !selection->regions.empty()) {
470 clicked_selection = select_range_around_region (selection->regions.front());
475 case RegionViewNameHighlight:
477 case LeftFrameHandle:
478 case RightFrameHandle:
479 if (mouse_mode != MouseRange || internal_editing() || _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
480 set_selected_regionview_from_click (press, op, true);
481 } else if (event->type == GDK_BUTTON_PRESS) {
482 set_selected_track_as_side_effect ();
487 case FadeInHandleItem:
489 case FadeOutHandleItem:
491 if (mouse_mode != MouseRange || _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
492 set_selected_regionview_from_click (press, op, true);
493 } else if (event->type == GDK_BUTTON_PRESS) {
494 set_selected_track_as_side_effect ();
498 case ControlPointItem:
499 set_selected_track_as_side_effect ();
500 if (mouse_mode != MouseRange || _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
501 set_selected_control_point_from_click (op, false);
506 /* for context click, select track */
507 if (event->button.button == 3) {
508 selection->clear_tracks ();
509 set_selected_track_as_side_effect (true);
513 case AutomationTrackItem:
514 set_selected_track_as_side_effect (true);
523 Editor::button_press_handler_1 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
525 if (_drags->active ()) {
529 /* single mouse clicks on any of these item types operate
530 independent of mouse mode, mostly because they are
531 not on the main track canvas or because we want
536 case PlayheadCursorItem:
537 _drags->set (new CursorDrag (this, item, true), event);
541 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
542 hide_marker (item, event);
544 _drags->set (new MarkerDrag (this, item), event);
548 case TempoMarkerItem:
550 new TempoMarkerDrag (
553 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
559 case MeterMarkerItem:
561 new MeterMarkerDrag (
564 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
573 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
574 _drags->set (new CursorDrag (this, &playhead_cursor->canvas_item, false), event);
580 case RangeMarkerBarItem:
581 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
582 _drags->set (new CursorDrag (this, &playhead_cursor->canvas_item, false), event);
584 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateRangeMarker), event);
589 case CdMarkerBarItem:
590 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
591 _drags->set (new CursorDrag (this, &playhead_cursor->canvas_item, false), event);
593 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateCDMarker), event);
598 case TransportMarkerBarItem:
599 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
600 _drags->set (new CursorDrag (this, &playhead_cursor->canvas_item, false), event);
602 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateTransportMarker), event);
611 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
612 /* special case: allow trim of range selections in joined object mode;
613 in theory eff should equal MouseRange in this case, but it doesn't
614 because entering the range selection canvas item results in entered_regionview
615 being set to 0, so update_join_object_range_location acts as if we aren't
618 if (item_type == StartSelectionTrimItem) {
619 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
620 } else if (item_type == EndSelectionTrimItem) {
621 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
625 Editing::MouseMode eff = effective_mouse_mode ();
627 /* special case: allow drag of region fade in/out in object mode with join object/range enabled */
628 if (item_type == FadeInHandleItem || item_type == FadeOutHandleItem) {
635 case StartSelectionTrimItem:
636 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
639 case EndSelectionTrimItem:
640 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
644 if (Keyboard::modifier_state_contains
645 (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier))) {
646 // contains and not equals because I can't use alt as a modifier alone.
647 start_selection_grab (item, event);
648 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
649 /* grab selection for moving */
650 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionMove), event);
652 double const y = event->button.y + vertical_adjustment.get_value() - canvas_timebars_vsize;
653 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
655 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
656 if (join_object_range_button.get_active() && atv) {
657 /* smart "join" mode: drag automation */
658 _drags->set (new AutomationRangeDrag (this, atv->base_item(), selection->time), event);
660 /* this was debated, but decided the more common action was to
661 make a new selection */
662 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
669 if (internal_editing()) {
670 /* trim notes if we're in internal edit mode and near the ends of the note */
671 _drags->set (new NoteResizeDrag (this, item), event);
676 if (internal_editing()) {
677 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
680 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
685 case RegionViewNameHighlight:
686 case LeftFrameHandle:
687 case RightFrameHandle:
689 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
690 _drags->set (new TrimDrag (this, item, clicked_regionview, s.by_layer()), event);
696 if (!internal_editing()) {
697 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
706 if (internal_editing()) {
707 _drags->set (new NoteDrag (this, item), event);
716 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)) &&
717 event->type == GDK_BUTTON_PRESS) {
719 _drags->set (new RubberbandSelectDrag (this, item), event);
721 } else if (event->type == GDK_BUTTON_PRESS) {
724 case FadeInHandleItem:
726 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
727 _drags->set (new FadeInDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), s), event);
731 case FadeOutHandleItem:
733 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
734 _drags->set (new FadeOutDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), s), event);
738 case FeatureLineItem:
740 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier)) {
741 remove_transient(item);
745 _drags->set (new FeatureLineDrag (this, item), event);
751 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
752 add_region_copy_drag (item, event, clicked_regionview);
754 else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
755 add_region_brush_drag (item, event, clicked_regionview);
757 add_region_drag (item, event, clicked_regionview);
760 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT && !selection->regions.empty()) {
761 _drags->add (new SelectionDrag (this, clicked_axisview->get_selection_rect (clicked_selection)->rect, SelectionDrag::SelectionMove));
764 _drags->start_grab (event);
767 case RegionViewNameHighlight:
768 case LeftFrameHandle:
769 case RightFrameHandle:
771 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
772 _drags->set (new TrimDrag (this, item, clicked_regionview, s.by_layer()), event);
779 /* rename happens on edit clicks */
780 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
781 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, s.by_layer()), event);
786 case ControlPointItem:
787 _drags->set (new ControlPointDrag (this, item), event);
791 case AutomationLineItem:
792 _drags->set (new LineDrag (this, item), event);
797 if (internal_editing()) {
798 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
801 _drags->set (new RubberbandSelectDrag (this, item), event);
805 case AutomationTrackItem:
806 /* rubberband drag to select automation points */
807 _drags->set (new RubberbandSelectDrag (this, item), event);
812 if (join_object_range_button.get_active()) {
813 /* we're in "smart" joined mode, and we've clicked on a Selection */
814 double const y = event->button.y + vertical_adjustment.get_value() - canvas_timebars_vsize;
815 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
817 /* if we're over an automation track, start a drag of its data */
818 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
820 _drags->set (new AutomationRangeDrag (this, atv->base_item(), selection->time), event);
823 /* if we're over a track and a region, and in the `object' part of a region,
824 put a selection around the region and drag both
826 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
827 if (rtv && _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
828 boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track> (rtv->route ());
830 boost::shared_ptr<Playlist> pl = t->playlist ();
833 boost::shared_ptr<Region> r = pl->top_region_at (event_frame (event));
835 RegionView* rv = rtv->view()->find_view (r);
836 clicked_selection = select_range_around_region (rv);
837 _drags->add (new SelectionDrag (this, item, SelectionDrag::SelectionMove));
838 list<RegionView*> rvs;
840 _drags->add (new RegionMoveDrag (this, item, rv, rvs, false, false));
841 _drags->start_grab (event);
852 case ImageFrameHandleStartItem:
853 imageframe_start_handle_op(item, event) ;
856 case ImageFrameHandleEndItem:
857 imageframe_end_handle_op(item, event) ;
860 case MarkerViewHandleStartItem:
861 markerview_item_start_handle_op(item, event) ;
864 case MarkerViewHandleEndItem:
865 markerview_item_end_handle_op(item, event) ;
869 start_markerview_grab(item, event) ;
872 start_imageframe_grab(item, event) ;
890 /* start a grab so that if we finish after moving
891 we can tell what happened.
893 _drags->set (new RegionGainDrag (this, item), event, current_canvas_cursor);
897 _drags->set (new LineDrag (this, item), event);
900 case ControlPointItem:
901 _drags->set (new ControlPointDrag (this, item), event);
912 case ControlPointItem:
913 _drags->set (new ControlPointDrag (this, item), event);
916 case AutomationLineItem:
917 _drags->set (new LineDrag (this, item), event);
921 // XXX need automation mode to identify which
923 // start_line_grab_from_regionview (item, event);
933 if (event->type == GDK_BUTTON_PRESS) {
934 _drags->set (new MouseZoomDrag (this, item), event);
941 if (internal_editing() && item_type == NoteItem) {
942 /* drag notes if we're in internal edit mode */
943 _drags->set (new NoteResizeDrag (this, item), event);
945 } else if ((!internal_editing() || dynamic_cast<AudioRegionView*> (clicked_regionview)) && clicked_regionview) {
946 /* do time-FX if we're not in internal edit mode, or we are but we clicked on an audio region */
947 _drags->set (new TimeFXDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
953 _drags->set (new ScrubDrag (this, item), event);
955 scrub_reverse_distance = 0;
956 last_scrub_x = event->button.x;
957 scrubbing_direction = 0;
958 track_canvas->get_window()->set_cursor (*transparent_cursor);
970 Editor::button_press_handler_2 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
972 Editing::MouseMode const eff = effective_mouse_mode ();
977 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
978 add_region_copy_drag (item, event, clicked_regionview);
980 add_region_drag (item, event, clicked_regionview);
982 _drags->start_grab (event);
985 case ControlPointItem:
986 _drags->set (new ControlPointDrag (this, item), event);
995 case RegionViewNameHighlight:
996 case LeftFrameHandle:
997 case RightFrameHandle:
998 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1002 case RegionViewName:
1003 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer()), event);
1014 /* relax till release */
1020 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1021 temporal_zoom_session();
1023 temporal_zoom_to_frame (true, event_frame(event));
1036 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1038 if (event->type != GDK_BUTTON_PRESS) {
1042 Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->track_canvas->get_window();
1044 if (canvas_window) {
1045 Glib::RefPtr<const Gdk::Window> pointer_window;
1048 Gdk::ModifierType mask;
1050 pointer_window = canvas_window->get_pointer (x, y, mask);
1052 if (pointer_window == track_canvas->get_bin_window()) {
1053 track_canvas->window_to_world (x, y, wx, wy);
1057 track_canvas->grab_focus();
1059 if (_session && _session->actively_recording()) {
1063 button_selection (item, event, item_type);
1065 if (!_drags->active () &&
1066 (Keyboard::is_delete_event (&event->button) ||
1067 Keyboard::is_context_menu_event (&event->button) ||
1068 Keyboard::is_edit_event (&event->button))) {
1070 /* handled by button release */
1074 switch (event->button.button) {
1076 return button_press_handler_1 (item, event, item_type);
1080 return button_press_handler_2 (item, event, item_type);
1095 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1097 nframes64_t where = event_frame (event, 0, 0);
1098 AutomationTimeAxisView* atv = 0;
1100 /* no action if we're recording */
1102 if (_session && _session->actively_recording()) {
1106 /* first, see if we're finishing a drag ... */
1108 bool were_dragging = false;
1109 if (_drags->active ()) {
1110 bool const r = _drags->end_grab (event);
1112 /* grab dragged, so do nothing else */
1116 were_dragging = true;
1119 button_selection (item, event, item_type);
1121 /* edit events get handled here */
1123 if (!_drags->active () && Keyboard::is_edit_event (&event->button)) {
1124 switch (item_type) {
1129 case TempoMarkerItem:
1130 edit_tempo_marker (item);
1133 case MeterMarkerItem:
1134 edit_meter_marker (item);
1137 case RegionViewName:
1138 if (clicked_regionview->name_active()) {
1139 return mouse_rename_region (item, event);
1143 case ControlPointItem:
1144 edit_control_point (item);
1153 /* context menu events get handled here */
1155 if (Keyboard::is_context_menu_event (&event->button)) {
1157 if (!_drags->active ()) {
1159 /* no matter which button pops up the context menu, tell the menu
1160 widget to use button 1 to drive menu selection.
1163 switch (item_type) {
1165 case FadeInHandleItem:
1167 case FadeOutHandleItem:
1168 popup_fade_context_menu (1, event->button.time, item, item_type);
1172 popup_track_context_menu (1, event->button.time, item_type, false, where);
1176 case RegionViewNameHighlight:
1177 case LeftFrameHandle:
1178 case RightFrameHandle:
1179 case RegionViewName:
1180 popup_track_context_menu (1, event->button.time, item_type, false, where);
1184 popup_track_context_menu (1, event->button.time, item_type, true, where);
1187 case AutomationTrackItem:
1188 popup_track_context_menu (1, event->button.time, item_type, false, where);
1192 case RangeMarkerBarItem:
1193 case TransportMarkerBarItem:
1194 case CdMarkerBarItem:
1197 popup_ruler_menu (where, item_type);
1201 marker_context_menu (&event->button, item);
1204 case TempoMarkerItem:
1205 tempo_or_meter_marker_context_menu (&event->button, item);
1208 case MeterMarkerItem:
1209 tempo_or_meter_marker_context_menu (&event->button, item);
1212 case CrossfadeViewItem:
1213 popup_track_context_menu (1, event->button.time, item_type, false, where);
1217 case ImageFrameItem:
1218 popup_imageframe_edit_menu(1, event->button.time, item, true) ;
1220 case ImageFrameTimeAxisItem:
1221 popup_imageframe_edit_menu(1, event->button.time, item, false) ;
1223 case MarkerViewItem:
1224 popup_marker_time_axis_edit_menu(1, event->button.time, item, true) ;
1226 case MarkerTimeAxisItem:
1227 popup_marker_time_axis_edit_menu(1, event->button.time, item, false) ;
1239 /* delete events get handled here */
1241 Editing::MouseMode const eff = effective_mouse_mode ();
1243 if (!_drags->active () && Keyboard::is_delete_event (&event->button)) {
1245 switch (item_type) {
1246 case TempoMarkerItem:
1247 remove_tempo_marker (item);
1250 case MeterMarkerItem:
1251 remove_meter_marker (item);
1255 remove_marker (*item, event);
1259 if (eff == MouseObject) {
1260 remove_clicked_region ();
1264 case ControlPointItem:
1265 if (eff == MouseGain) {
1266 remove_gain_control_point (item, event);
1268 remove_control_point (item, event);
1273 remove_midi_note (item, event);
1282 switch (event->button.button) {
1285 switch (item_type) {
1286 /* see comments in button_press_handler */
1287 case PlayheadCursorItem:
1290 case AutomationLineItem:
1291 case StartSelectionTrimItem:
1292 case EndSelectionTrimItem:
1296 if (!_dragging_playhead) {
1297 snap_to_with_modifier (where, event, 0, true);
1298 mouse_add_new_marker (where);
1302 case CdMarkerBarItem:
1303 if (!_dragging_playhead) {
1304 // if we get here then a dragged range wasn't done
1305 snap_to_with_modifier (where, event, 0, true);
1306 mouse_add_new_marker (where, true);
1311 if (!_dragging_playhead) {
1312 snap_to_with_modifier (where, event);
1313 mouse_add_new_tempo_event (where);
1318 if (!_dragging_playhead) {
1319 mouse_add_new_meter_event (pixel_to_frame (event->button.x));
1330 switch (item_type) {
1331 case AutomationTrackItem:
1332 atv = dynamic_cast<AutomationTimeAxisView*>(clicked_axisview);
1334 atv->add_automation_event (item, event, where, event->button.y);
1345 switch (item_type) {
1348 /* check that we didn't drag before releasing, since
1349 its really annoying to create new control
1350 points when doing this.
1352 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (clicked_regionview);
1353 if (were_dragging && arv) {
1354 arv->add_gain_point_event (item, event);
1360 case AutomationTrackItem:
1361 dynamic_cast<AutomationTimeAxisView*>(clicked_axisview)->
1362 add_automation_event (item, event, where, event->button.y);
1371 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1372 if (scrubbing_direction == 0) {
1373 /* no drag, just a click */
1374 switch (item_type) {
1376 play_selected_region ();
1382 /* make sure we stop */
1383 _session->request_transport_speed (0.0);
1400 switch (item_type) {
1402 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1404 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier|Keyboard::SecondaryModifier))) {
1407 // Button2 click is unused
1420 // x_style_paste (where, 1.0);
1440 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1447 if (last_item_entered != item) {
1448 last_item_entered = item;
1449 last_item_entered_n = 0;
1452 switch (item_type) {
1453 case ControlPointItem:
1454 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1455 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1456 cp->set_visible (true);
1460 at_y = cp->get_y ();
1461 cp->i2w (at_x, at_y);
1465 fraction = 1.0 - (cp->get_y() / cp->line().height());
1467 if (is_drawable() && !_drags->active ()) {
1468 track_canvas->get_window()->set_cursor (*fader_cursor);
1471 last_item_entered_n++;
1472 set_verbose_canvas_cursor (cp->line().get_verbose_cursor_string (fraction), at_x, at_y);
1473 if (last_item_entered_n < 10) {
1474 show_verbose_canvas_cursor ();
1480 if (mouse_mode == MouseGain) {
1481 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1483 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredGainLine.get();
1484 if (is_drawable()) {
1485 track_canvas->get_window()->set_cursor (*fader_cursor);
1490 case AutomationLineItem:
1491 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1493 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1495 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredAutomationLine.get();
1497 if (is_drawable()) {
1498 track_canvas->get_window()->set_cursor (*fader_cursor);
1503 case RegionViewNameHighlight:
1504 if (is_drawable() && (mouse_mode == MouseObject || (internal_editing() && mouse_mode == MouseRange))) {
1505 track_canvas->get_window()->set_cursor (*trimmer_cursor);
1509 case LeftFrameHandle:
1510 if (is_drawable() && (mouse_mode == MouseObject || (internal_editing() && mouse_mode == MouseRange))) {
1511 track_canvas->get_window()->set_cursor (*left_side_trim_cursor);
1515 case RightFrameHandle:
1516 if (is_drawable() && (mouse_mode == MouseObject || (internal_editing() && mouse_mode == MouseRange))) {
1517 track_canvas->get_window()->set_cursor (*right_side_trim_cursor);
1521 case StartSelectionTrimItem:
1522 case EndSelectionTrimItem:
1525 case ImageFrameHandleStartItem:
1526 case ImageFrameHandleEndItem:
1527 case MarkerViewHandleStartItem:
1528 case MarkerViewHandleEndItem:
1531 if (is_drawable()) {
1532 track_canvas->get_window()->set_cursor (*trimmer_cursor);
1536 case PlayheadCursorItem:
1537 if (is_drawable()) {
1538 switch (_edit_point) {
1540 track_canvas->get_window()->set_cursor (*grabber_edit_point_cursor);
1543 track_canvas->get_window()->set_cursor (*grabber_cursor);
1549 case RegionViewName:
1551 /* when the name is not an active item, the entire name highlight is for trimming */
1553 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1554 if (mouse_mode == MouseObject && is_drawable()) {
1555 track_canvas->get_window()->set_cursor (*trimmer_cursor);
1561 case AutomationTrackItem:
1562 if (is_drawable()) {
1563 Gdk::Cursor *cursor;
1564 switch (mouse_mode) {
1566 cursor = selector_cursor;
1569 cursor = zoom_cursor;
1572 cursor = cross_hair_cursor;
1576 track_canvas->get_window()->set_cursor (*cursor);
1578 AutomationTimeAxisView* atv;
1579 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1580 clear_entered_track = false;
1581 set_entered_track (atv);
1587 case RangeMarkerBarItem:
1588 case TransportMarkerBarItem:
1589 case CdMarkerBarItem:
1592 if (is_drawable()) {
1593 track_canvas->get_window()->set_cursor (*timebar_cursor);
1598 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1601 entered_marker = marker;
1602 marker->set_color_rgba (ARDOUR_UI::config()->canvasvar_EnteredMarker.get());
1604 case MeterMarkerItem:
1605 case TempoMarkerItem:
1606 if (is_drawable()) {
1607 track_canvas->get_window()->set_cursor (*timebar_cursor);
1611 case FadeInHandleItem:
1612 if (mouse_mode == MouseObject) {
1613 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1615 rect->property_fill_color_rgba() = 0;
1616 rect->property_outline_pixels() = 1;
1618 track_canvas->get_window()->set_cursor (*fade_in_cursor);
1622 case FadeOutHandleItem:
1623 if (mouse_mode == MouseObject) {
1624 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1626 rect->property_fill_color_rgba() = 0;
1627 rect->property_outline_pixels() = 1;
1629 track_canvas->get_window()->set_cursor (*fade_out_cursor);
1632 case FeatureLineItem:
1634 ArdourCanvas::SimpleLine *line = dynamic_cast<ArdourCanvas::SimpleLine *> (item);
1635 line->property_color_rgba() = 0xFF0000FF;
1642 /* second pass to handle entered track status in a comprehensible way.
1645 switch (item_type) {
1647 case AutomationLineItem:
1648 case ControlPointItem:
1649 /* these do not affect the current entered track state */
1650 clear_entered_track = false;
1653 case AutomationTrackItem:
1654 /* handled above already */
1658 set_entered_track (0);
1666 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1676 switch (item_type) {
1677 case ControlPointItem:
1678 cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
1679 if (cp->line().the_list()->interpolation() != AutomationList::Discrete) {
1680 if (cp->line().npoints() > 1 && !cp->selected()) {
1681 cp->set_visible (false);
1685 if (is_drawable()) {
1686 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1689 hide_verbose_canvas_cursor ();
1692 case RegionViewNameHighlight:
1693 case LeftFrameHandle:
1694 case RightFrameHandle:
1695 case StartSelectionTrimItem:
1696 case EndSelectionTrimItem:
1697 case PlayheadCursorItem:
1700 case ImageFrameHandleStartItem:
1701 case ImageFrameHandleEndItem:
1702 case MarkerViewHandleStartItem:
1703 case MarkerViewHandleEndItem:
1706 if (is_drawable()) {
1707 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1712 case AutomationLineItem:
1713 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1715 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1717 line->property_fill_color_rgba() = al->get_line_color();
1719 if (is_drawable()) {
1720 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1724 case RegionViewName:
1725 /* see enter_handler() for notes */
1726 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1727 if (is_drawable() && mouse_mode == MouseObject) {
1728 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1733 case RangeMarkerBarItem:
1734 case TransportMarkerBarItem:
1735 case CdMarkerBarItem:
1739 if (is_drawable()) {
1740 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1745 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1749 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
1750 location_flags_changed (loc, this);
1753 case MeterMarkerItem:
1754 case TempoMarkerItem:
1756 if (is_drawable()) {
1757 track_canvas->get_window()->set_cursor (*timebar_cursor);
1762 case FadeInHandleItem:
1763 case FadeOutHandleItem:
1764 rv = static_cast<RegionView*>(item->get_data ("regionview"));
1766 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1768 rect->property_fill_color_rgba() = rv->get_fill_color();
1769 rect->property_outline_pixels() = 0;
1772 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1775 case AutomationTrackItem:
1776 if (is_drawable()) {
1777 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1778 clear_entered_track = true;
1779 Glib::signal_idle().connect (sigc::mem_fun(*this, &Editor::left_automation_track));
1782 case FeatureLineItem:
1784 ArdourCanvas::SimpleLine *line = dynamic_cast<ArdourCanvas::SimpleLine *> (item);
1785 line->property_color_rgba() = (guint) ARDOUR_UI::config()->canvasvar_ZeroLine.get();;
1797 Editor::left_automation_track ()
1799 if (clear_entered_track) {
1800 set_entered_track (0);
1801 clear_entered_track = false;
1807 Editor::scrub (nframes64_t frame, double current_x)
1811 if (scrubbing_direction == 0) {
1813 _session->request_locate (frame, false);
1814 _session->request_transport_speed (0.1);
1815 scrubbing_direction = 1;
1819 if (last_scrub_x > current_x) {
1821 /* pointer moved to the left */
1823 if (scrubbing_direction > 0) {
1825 /* we reversed direction to go backwards */
1828 scrub_reverse_distance += (int) (last_scrub_x - current_x);
1832 /* still moving to the left (backwards) */
1834 scrub_reversals = 0;
1835 scrub_reverse_distance = 0;
1837 delta = 0.01 * (last_scrub_x - current_x);
1838 _session->request_transport_speed (_session->transport_speed() - delta);
1842 /* pointer moved to the right */
1844 if (scrubbing_direction < 0) {
1845 /* we reversed direction to go forward */
1848 scrub_reverse_distance += (int) (current_x - last_scrub_x);
1851 /* still moving to the right */
1853 scrub_reversals = 0;
1854 scrub_reverse_distance = 0;
1856 delta = 0.01 * (current_x - last_scrub_x);
1857 _session->request_transport_speed (_session->transport_speed() + delta);
1861 /* if there have been more than 2 opposite motion moves detected, or one that moves
1862 back more than 10 pixels, reverse direction
1865 if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
1867 if (scrubbing_direction > 0) {
1868 /* was forwards, go backwards */
1869 _session->request_transport_speed (-0.1);
1870 scrubbing_direction = -1;
1872 /* was backwards, go forwards */
1873 _session->request_transport_speed (0.1);
1874 scrubbing_direction = 1;
1877 scrub_reverse_distance = 0;
1878 scrub_reversals = 0;
1882 last_scrub_x = current_x;
1886 Editor::motion_handler (ArdourCanvas::Item* /*item*/, GdkEvent* event, bool from_autoscroll)
1888 if (event->motion.is_hint) {
1891 /* We call this so that MOTION_NOTIFY events continue to be
1892 delivered to the canvas. We need to do this because we set
1893 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
1894 the density of the events, at the expense of a round-trip
1895 to the server. Given that this will mostly occur on cases
1896 where DISPLAY = :0.0, and given the cost of what the motion
1897 event might do, its a good tradeoff.
1900 track_canvas->get_pointer (x, y);
1903 if (current_stepping_trackview) {
1904 /* don't keep the persistent stepped trackview if the mouse moves */
1905 current_stepping_trackview = 0;
1906 step_timeout.disconnect ();
1909 if (_session && _session->actively_recording()) {
1910 /* Sorry. no dragging stuff around while we record */
1914 JoinObjectRangeState const old = _join_object_range_state;
1915 update_join_object_range_location (event->motion.x, event->motion.y);
1916 if (_join_object_range_state != old) {
1917 set_canvas_cursor ();
1920 bool handled = false;
1921 if (_drags->active ()) {
1922 handled = _drags->motion_handler (event, from_autoscroll);
1928 track_canvas_motion (event);
1933 Editor::remove_gain_control_point (ArdourCanvas::Item*item, GdkEvent* /*event*/)
1935 ControlPoint* control_point;
1937 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
1938 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
1942 // We shouldn't remove the first or last gain point
1943 if (control_point->line().is_last_point(*control_point) ||
1944 control_point->line().is_first_point(*control_point)) {
1948 control_point->line().remove_point (*control_point);
1952 Editor::remove_control_point (ArdourCanvas::Item* item, GdkEvent* /*event*/)
1954 ControlPoint* control_point;
1956 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
1957 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
1961 control_point->line().remove_point (*control_point);
1965 Editor::edit_control_point (ArdourCanvas::Item* item)
1967 ControlPoint* p = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"));
1970 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
1974 ControlPointDialog d (p);
1975 d.set_position (Gtk::WIN_POS_MOUSE);
1978 if (d.run () != RESPONSE_ACCEPT) {
1982 p->line().modify_point_y (*p, d.get_y_fraction ());
1987 Editor::visible_order_range (int* low, int* high) const
1989 *low = TimeAxisView::max_order ();
1992 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
1994 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
1996 if (!rtv->hidden()) {
1998 if (*high < rtv->order()) {
1999 *high = rtv->order ();
2002 if (*low > rtv->order()) {
2003 *low = rtv->order ();
2010 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
2012 /* Either add to or set the set the region selection, unless
2013 this is an alignment click (control used)
2016 if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
2017 TimeAxisView* tv = &rv.get_time_axis_view();
2018 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
2020 if (rtv && rtv->is_track()) {
2021 speed = rtv->track()->speed();
2024 nframes64_t where = get_preferred_edit_position();
2028 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
2030 align_region (rv.region(), SyncPoint, (nframes64_t) (where * speed));
2032 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
2034 align_region (rv.region(), End, (nframes64_t) (where * speed));
2038 align_region (rv.region(), Start, (nframes64_t) (where * speed));
2045 Editor::show_verbose_time_cursor (nframes64_t frame, double offset, double xpos, double ypos)
2048 Timecode::Time timecode;
2051 nframes64_t frame_rate;
2054 if (_session == 0) {
2060 if (Profile->get_sae() || Profile->get_small_screen()) {
2061 m = ARDOUR_UI::instance()->primary_clock.mode();
2063 m = ARDOUR_UI::instance()->secondary_clock.mode();
2067 case AudioClock::BBT:
2068 _session->bbt_time (frame, bbt);
2069 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, bbt.bars, bbt.beats, bbt.ticks);
2072 case AudioClock::Timecode:
2073 _session->timecode_time (frame, timecode);
2074 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
2077 case AudioClock::MinSec:
2078 /* XXX this is copied from show_verbose_duration_cursor() */
2079 frame_rate = _session->frame_rate();
2080 hours = frame / (frame_rate * 3600);
2081 frame = frame % (frame_rate * 3600);
2082 mins = frame / (frame_rate * 60);
2083 frame = frame % (frame_rate * 60);
2084 secs = (float) frame / (float) frame_rate;
2085 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%07.4f", hours, mins, secs);
2089 snprintf (buf, sizeof(buf), "%" PRIi64, frame);
2093 if (xpos >= 0 && ypos >=0) {
2094 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
2096 set_verbose_canvas_cursor (buf, _drags->current_pointer_x() + offset - horizontal_position(), _drags->current_pointer_y() + offset - vertical_adjustment.get_value() + canvas_timebars_vsize);
2098 show_verbose_canvas_cursor ();
2102 Editor::show_verbose_duration_cursor (nframes64_t start, nframes64_t end, double offset, double xpos, double ypos)
2105 Timecode::Time timecode;
2109 nframes64_t distance, frame_rate;
2111 Meter meter_at_start(_session->tempo_map().meter_at(start));
2113 if (_session == 0) {
2119 if (Profile->get_sae() || Profile->get_small_screen()) {
2120 m = ARDOUR_UI::instance()->primary_clock.mode ();
2122 m = ARDOUR_UI::instance()->secondary_clock.mode ();
2126 case AudioClock::BBT:
2127 _session->bbt_time (start, sbbt);
2128 _session->bbt_time (end, ebbt);
2131 /* XXX this computation won't work well if the
2132 user makes a selection that spans any meter changes.
2135 ebbt.bars -= sbbt.bars;
2136 if (ebbt.beats >= sbbt.beats) {
2137 ebbt.beats -= sbbt.beats;
2140 ebbt.beats = int(meter_at_start.beats_per_bar()) + ebbt.beats - sbbt.beats;
2142 if (ebbt.ticks >= sbbt.ticks) {
2143 ebbt.ticks -= sbbt.ticks;
2146 ebbt.ticks = int(Meter::ticks_per_beat) + ebbt.ticks - sbbt.ticks;
2149 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, ebbt.bars, ebbt.beats, ebbt.ticks);
2152 case AudioClock::Timecode:
2153 _session->timecode_duration (end - start, timecode);
2154 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
2157 case AudioClock::MinSec:
2158 /* XXX this stuff should be elsewhere.. */
2159 distance = end - start;
2160 frame_rate = _session->frame_rate();
2161 hours = distance / (frame_rate * 3600);
2162 distance = distance % (frame_rate * 3600);
2163 mins = distance / (frame_rate * 60);
2164 distance = distance % (frame_rate * 60);
2165 secs = (float) distance / (float) frame_rate;
2166 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%07.4f", hours, mins, secs);
2170 snprintf (buf, sizeof(buf), "%" PRIi64, end - start);
2174 if (xpos >= 0 && ypos >=0) {
2175 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
2178 set_verbose_canvas_cursor (buf, _drags->current_pointer_x() + offset, _drags->current_pointer_y() + offset);
2181 show_verbose_canvas_cursor ();
2185 Editor::collect_new_region_view (RegionView* rv)
2187 latest_regionviews.push_back (rv);
2191 Editor::collect_and_select_new_region_view (RegionView* rv)
2194 latest_regionviews.push_back (rv);
2198 Editor::cancel_selection ()
2200 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2201 (*i)->hide_selection ();
2204 selection->clear ();
2205 clicked_selection = 0;
2210 Editor::single_contents_trim (RegionView& rv, nframes64_t frame_delta, bool left_direction, bool swap_direction)
2212 boost::shared_ptr<Region> region (rv.region());
2214 if (region->locked()) {
2218 nframes64_t new_bound;
2221 TimeAxisView* tvp = clicked_axisview;
2222 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2224 if (tv && tv->is_track()) {
2225 speed = tv->track()->speed();
2228 if (left_direction) {
2229 if (swap_direction) {
2230 new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
2232 new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
2235 if (swap_direction) {
2236 new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
2238 new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
2242 region->trim_start ((nframes64_t) (new_bound * speed), this);
2243 rv.region_changed (PropertyChange (ARDOUR::Properties::start));
2247 Editor::single_start_trim (RegionView& rv, nframes64_t new_bound, bool no_overlap)
2249 boost::shared_ptr<Region> region (rv.region());
2251 if (region->locked()) {
2256 TimeAxisView* tvp = clicked_axisview;
2257 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2259 if (tv && tv->is_track()) {
2260 speed = tv->track()->speed();
2263 nframes64_t pre_trim_first_frame = region->first_frame();
2265 region->trim_front ((nframes64_t) (new_bound * speed), this);
2268 //Get the next region on the left of this region and shrink/expand it.
2269 boost::shared_ptr<Playlist> playlist (region->playlist());
2270 boost::shared_ptr<Region> region_left = playlist->find_next_region (pre_trim_first_frame, End, 0);
2272 bool regions_touching = false;
2274 if (region_left != 0 && (pre_trim_first_frame == region_left->last_frame() + 1)){
2275 regions_touching = true;
2278 //Only trim region on the left if the first frame has gone beyond the left region's last frame.
2279 if (region_left != 0 &&
2280 (region_left->last_frame() > region->first_frame() || regions_touching))
2282 region_left->trim_end(region->first_frame() - 1, this);
2286 rv.region_changed (ARDOUR::bounds_change);
2290 Editor::single_end_trim (RegionView& rv, nframes64_t new_bound, bool no_overlap)
2292 boost::shared_ptr<Region> region (rv.region());
2294 if (region->locked()) {
2299 TimeAxisView* tvp = clicked_axisview;
2300 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2302 if (tv && tv->is_track()) {
2303 speed = tv->track()->speed();
2306 nframes64_t pre_trim_last_frame = region->last_frame();
2308 region->trim_end ((nframes64_t) (new_bound * speed), this);
2311 //Get the next region on the right of this region and shrink/expand it.
2312 boost::shared_ptr<Playlist> playlist (region->playlist());
2313 boost::shared_ptr<Region> region_right = playlist->find_next_region (pre_trim_last_frame, Start, 1);
2315 bool regions_touching = false;
2317 if (region_right != 0 && (pre_trim_last_frame == region_right->first_frame() - 1)) {
2318 regions_touching = true;
2321 //Only trim region on the right if the last frame has gone beyond the right region's first frame.
2322 if (region_right != 0 &&
2323 (region_right->first_frame() < region->last_frame() || regions_touching))
2325 region_right->trim_front(region->last_frame() + 1, this);
2328 rv.region_changed (ARDOUR::bounds_change);
2331 rv.region_changed (PropertyChange (ARDOUR::Properties::length));
2337 Editor::point_trim (GdkEvent* event, nframes64_t new_bound)
2339 RegionView* rv = clicked_regionview;
2341 /* Choose action dependant on which button was pressed */
2342 switch (event->button.button) {
2344 begin_reversible_command (_("Start point trim"));
2346 if (selection->selected (rv)) {
2347 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
2348 i != selection->regions.by_layer().end(); ++i)
2351 cerr << "region view contains null region" << endl;
2354 if (!(*i)->region()->locked()) {
2355 (*i)->region()->clear_history ();
2356 (*i)->region()->trim_front (new_bound, this);
2357 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2362 if (!rv->region()->locked()) {
2363 rv->region()->clear_history ();
2364 rv->region()->trim_front (new_bound, this);
2365 _session->add_command(new StatefulDiffCommand (rv->region()));
2369 commit_reversible_command();
2373 begin_reversible_command (_("End point trim"));
2375 if (selection->selected (rv)) {
2377 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
2379 if (!(*i)->region()->locked()) {
2380 (*i)->region()->clear_history();
2381 (*i)->region()->trim_end (new_bound, this);
2382 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2388 if (!rv->region()->locked()) {
2389 rv->region()->clear_history ();
2390 rv->region()->trim_end (new_bound, this);
2391 _session->add_command (new StatefulDiffCommand (rv->region()));
2395 commit_reversible_command();
2404 Editor::thaw_region_after_trim (RegionView& rv)
2406 boost::shared_ptr<Region> region (rv.region());
2408 if (region->locked()) {
2412 region->resume_property_changes ();
2414 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(&rv);
2417 arv->unhide_envelope ();
2422 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* /*event*/)
2427 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2428 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2432 Location* location = find_location_from_marker (marker, is_start);
2433 location->set_hidden (true, this);
2438 Editor::reposition_zoom_rect (nframes64_t start, nframes64_t end)
2440 double x1 = frame_to_pixel (start);
2441 double x2 = frame_to_pixel (end);
2442 double y2 = full_canvas_height - 1.0;
2444 zoom_rect->property_x1() = x1;
2445 zoom_rect->property_y1() = 1.0;
2446 zoom_rect->property_x2() = x2;
2447 zoom_rect->property_y2() = y2;
2452 Editor::mouse_rename_region (ArdourCanvas::Item* /*item*/, GdkEvent* /*event*/)
2454 using namespace Gtkmm2ext;
2456 ArdourPrompter prompter (false);
2458 prompter.set_prompt (_("Name for region:"));
2459 prompter.set_initial_text (clicked_regionview->region()->name());
2460 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
2461 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
2462 prompter.show_all ();
2463 switch (prompter.run ()) {
2464 case Gtk::RESPONSE_ACCEPT:
2466 prompter.get_result(str);
2468 clicked_regionview->region()->set_name (str);
2477 Editor::mouse_brush_insert_region (RegionView* rv, nframes64_t pos)
2479 /* no brushing without a useful snap setting */
2481 switch (_snap_mode) {
2483 return; /* can't work because it allows region to be placed anywhere */
2488 switch (_snap_type) {
2496 /* don't brush a copy over the original */
2498 if (pos == rv->region()->position()) {
2502 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
2504 if (rtv == 0 || !rtv->is_track()) {
2508 boost::shared_ptr<Playlist> playlist = rtv->playlist();
2509 double speed = rtv->track()->speed();
2511 playlist->clear_history ();
2512 boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region()));
2513 playlist->add_region (new_region, (nframes64_t) (pos * speed));
2514 _session->add_command (new StatefulDiffCommand (playlist));
2516 // playlist is frozen, so we have to update manually XXX this is disgusting
2518 playlist->RegionAdded (new_region); /* EMIT SIGNAL */
2522 Editor::track_height_step_timeout ()
2524 if (get_microseconds() - last_track_height_step_timestamp < 250000) {
2525 current_stepping_trackview = 0;
2532 Editor::add_region_drag (ArdourCanvas::Item* item, GdkEvent* event, RegionView* region_view)
2534 assert (region_view);
2536 _region_motion_group->raise_to_top ();
2538 if (Config->get_edit_mode() == Splice) {
2539 _drags->add (new RegionSpliceDrag (this, item, region_view, selection->regions.by_layer()));
2541 RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id);
2542 _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), false, false));
2545 /* sync the canvas to what we think is its current state */
2546 update_canvas_now();
2550 Editor::add_region_copy_drag (ArdourCanvas::Item* item, GdkEvent* event, RegionView* region_view)
2552 assert (region_view);
2554 _region_motion_group->raise_to_top ();
2556 RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id);
2557 _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), false, true));
2561 Editor::add_region_brush_drag (ArdourCanvas::Item* item, GdkEvent* event, RegionView* region_view)
2563 assert (region_view);
2565 if (Config->get_edit_mode() == Splice) {
2569 RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id);
2570 _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), true, false));
2572 begin_reversible_command (_("Drag region brush"));
2575 /** Start a grab where a time range is selected, track(s) are selected, and the
2576 * user clicks and drags a region with a modifier in order to create a new region containing
2577 * the section of the clicked region that lies within the time range.
2580 Editor::start_selection_grab (ArdourCanvas::Item* /*item*/, GdkEvent* event)
2582 if (clicked_regionview == 0) {
2586 /* lets try to create new Region for the selection */
2588 vector<boost::shared_ptr<Region> > new_regions;
2589 create_region_from_selection (new_regions);
2591 if (new_regions.empty()) {
2595 /* XXX fix me one day to use all new regions */
2597 boost::shared_ptr<Region> region (new_regions.front());
2599 /* add it to the current stream/playlist.
2601 tricky: the streamview for the track will add a new regionview. we will
2602 catch the signal it sends when it creates the regionview to
2603 set the regionview we want to then drag.
2606 latest_regionviews.clear();
2607 sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
2609 /* A selection grab currently creates two undo/redo operations, one for
2610 creating the new region and another for moving it.
2613 begin_reversible_command (_("selection grab"));
2615 boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
2617 playlist->clear_history ();
2618 clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
2619 _session->add_command(new StatefulDiffCommand (playlist));
2621 commit_reversible_command ();
2625 if (latest_regionviews.empty()) {
2626 /* something went wrong */
2630 /* we need to deselect all other regionviews, and select this one
2631 i'm ignoring undo stuff, because the region creation will take care of it
2633 selection->set (latest_regionviews);
2635 _drags->set (new RegionMoveDrag (this, latest_regionviews.front()->get_canvas_group(), latest_regionviews.front(), latest_regionviews, false, false), event);
2641 if (_drags->active ()) {
2644 selection->clear ();
2649 Editor::set_internal_edit (bool yn)
2651 _internal_editing = yn;
2654 mouse_select_button.set_image (*(manage (new Image (::get_icon("midi_tool_pencil")))));
2655 mouse_select_button.get_image ()->show ();
2656 ARDOUR_UI::instance()->tooltips().set_tip (mouse_select_button, _("Draw/Edit MIDI Notes"));
2658 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2659 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (*i);
2661 mtv->start_step_editing ();
2665 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
2666 (*i)->hide_selection ();
2669 start_step_editing ();
2670 set_canvas_cursor ();
2674 mouse_select_button.set_image (*(manage (new Image (::get_icon("tool_range")))));
2675 mouse_select_button.get_image ()->show ();
2676 ARDOUR_UI::instance()->tooltips().set_tip (mouse_select_button, _("Select/Move Ranges"));
2677 stop_step_editing ();
2679 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2680 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (*i);
2682 mtv->stop_step_editing ();
2686 mouse_mode_toggled (mouse_mode);
2690 /** Update _join_object_range_state which indicate whether we are over the top or bottom half of a region view,
2691 * used by the `join object/range' tool mode.
2694 Editor::update_join_object_range_location (double x, double y)
2696 /* XXX: actually, this decides based on whether the mouse is in the top or bottom half of a RouteTimeAxisView;
2697 entered_{track,regionview} is not always setup (e.g. if the mouse is over a TimeSelection), and to get a Region
2698 that we're over requires searching the playlist.
2701 if (join_object_range_button.get_active() == false || (mouse_mode != MouseRange && mouse_mode != MouseObject)) {
2702 _join_object_range_state = JOIN_OBJECT_RANGE_NONE;
2706 if (mouse_mode == MouseObject) {
2707 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2708 } else if (mouse_mode == MouseRange) {
2709 _join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
2712 /* XXX: maybe we should make entered_track work in all cases, rather than resorting to this */
2713 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y + vertical_adjustment.get_value() - canvas_timebars_vsize);
2717 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
2722 rtv->canvas_display()->w2i (cx, cy);
2724 bool const top_half = cy < rtv->current_height () / 2;
2726 _join_object_range_state = top_half ? JOIN_OBJECT_RANGE_RANGE : JOIN_OBJECT_RANGE_OBJECT;
2732 Editor::effective_mouse_mode () const
2734 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
2736 } else if (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE) {
2744 Editor::remove_midi_note (ArdourCanvas::Item* item, GdkEvent *)
2746 ArdourCanvas::CanvasNoteEvent* e = dynamic_cast<ArdourCanvas::CanvasNoteEvent*> (item);
2749 e->region_view().delete_note (e->note ());