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);
739 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
740 add_region_copy_drag (item, event, clicked_regionview);
741 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
742 add_region_brush_drag (item, event, clicked_regionview);
744 add_region_drag (item, event, clicked_regionview);
747 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT && !selection->regions.empty()) {
748 _drags->add (new SelectionDrag (this, clicked_axisview->get_selection_rect (clicked_selection)->rect, SelectionDrag::SelectionMove));
751 _drags->start_grab (event);
754 case RegionViewNameHighlight:
755 case LeftFrameHandle:
756 case RightFrameHandle:
758 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
759 _drags->set (new TrimDrag (this, item, clicked_regionview, s.by_layer()), event);
766 /* rename happens on edit clicks */
767 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
768 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, s.by_layer()), event);
773 case ControlPointItem:
774 _drags->set (new ControlPointDrag (this, item), event);
778 case AutomationLineItem:
779 _drags->set (new LineDrag (this, item), event);
784 if (internal_editing()) {
785 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
788 _drags->set (new RubberbandSelectDrag (this, item), event);
792 case AutomationTrackItem:
793 /* rubberband drag to select automation points */
794 _drags->set (new RubberbandSelectDrag (this, item), event);
799 if (join_object_range_button.get_active()) {
800 /* we're in "smart" joined mode, and we've clicked on a Selection */
801 double const y = event->button.y + vertical_adjustment.get_value() - canvas_timebars_vsize;
802 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
804 /* if we're over an automation track, start a drag of its data */
805 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
807 _drags->set (new AutomationRangeDrag (this, atv->base_item(), selection->time), event);
810 /* if we're over a track and a region, and in the `object' part of a region,
811 put a selection around the region and drag both
813 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
814 if (rtv && _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
815 boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track> (rtv->route ());
817 boost::shared_ptr<Playlist> pl = t->playlist ();
820 boost::shared_ptr<Region> r = pl->top_region_at (event_frame (event));
822 RegionView* rv = rtv->view()->find_view (r);
823 clicked_selection = select_range_around_region (rv);
824 _drags->add (new SelectionDrag (this, item, SelectionDrag::SelectionMove));
825 list<RegionView*> rvs;
827 _drags->add (new RegionMoveDrag (this, item, rv, rvs, false, false));
828 _drags->start_grab (event);
839 case ImageFrameHandleStartItem:
840 imageframe_start_handle_op(item, event) ;
843 case ImageFrameHandleEndItem:
844 imageframe_end_handle_op(item, event) ;
847 case MarkerViewHandleStartItem:
848 markerview_item_start_handle_op(item, event) ;
851 case MarkerViewHandleEndItem:
852 markerview_item_end_handle_op(item, event) ;
856 start_markerview_grab(item, event) ;
859 start_imageframe_grab(item, event) ;
877 /* start a grab so that if we finish after moving
878 we can tell what happened.
880 _drags->set (new RegionGainDrag (this, item), event, current_canvas_cursor);
884 _drags->set (new LineDrag (this, item), event);
887 case ControlPointItem:
888 _drags->set (new ControlPointDrag (this, item), event);
899 case ControlPointItem:
900 _drags->set (new ControlPointDrag (this, item), event);
903 case AutomationLineItem:
904 _drags->set (new LineDrag (this, item), event);
908 // XXX need automation mode to identify which
910 // start_line_grab_from_regionview (item, event);
920 if (event->type == GDK_BUTTON_PRESS) {
921 _drags->set (new MouseZoomDrag (this, item), event);
928 if (internal_editing() && item_type == NoteItem) {
929 /* drag notes if we're in internal edit mode */
930 _drags->set (new NoteResizeDrag (this, item), event);
932 } else if ((!internal_editing() || dynamic_cast<AudioRegionView*> (clicked_regionview)) && clicked_regionview) {
933 /* do time-FX if we're not in internal edit mode, or we are but we clicked on an audio region */
934 _drags->set (new TimeFXDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
940 _drags->set (new ScrubDrag (this, item), event);
942 scrub_reverse_distance = 0;
943 last_scrub_x = event->button.x;
944 scrubbing_direction = 0;
945 track_canvas->get_window()->set_cursor (*transparent_cursor);
957 Editor::button_press_handler_2 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
959 Editing::MouseMode const eff = effective_mouse_mode ();
964 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
965 add_region_copy_drag (item, event, clicked_regionview);
967 add_region_drag (item, event, clicked_regionview);
969 _drags->start_grab (event);
972 case ControlPointItem:
973 _drags->set (new ControlPointDrag (this, item), event);
982 case RegionViewNameHighlight:
983 case LeftFrameHandle:
984 case RightFrameHandle:
985 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
990 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer()), event);
1001 /* relax till release */
1007 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1008 temporal_zoom_session();
1010 temporal_zoom_to_frame (true, event_frame(event));
1023 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1025 if (event->type != GDK_BUTTON_PRESS) {
1029 Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->track_canvas->get_window();
1031 if (canvas_window) {
1032 Glib::RefPtr<const Gdk::Window> pointer_window;
1035 Gdk::ModifierType mask;
1037 pointer_window = canvas_window->get_pointer (x, y, mask);
1039 if (pointer_window == track_canvas->get_bin_window()) {
1040 track_canvas->window_to_world (x, y, wx, wy);
1044 track_canvas->grab_focus();
1046 if (_session && _session->actively_recording()) {
1050 button_selection (item, event, item_type);
1052 if (!_drags->active () &&
1053 (Keyboard::is_delete_event (&event->button) ||
1054 Keyboard::is_context_menu_event (&event->button) ||
1055 Keyboard::is_edit_event (&event->button))) {
1057 /* handled by button release */
1061 switch (event->button.button) {
1063 return button_press_handler_1 (item, event, item_type);
1067 return button_press_handler_2 (item, event, item_type);
1082 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1084 nframes64_t where = event_frame (event, 0, 0);
1085 AutomationTimeAxisView* atv = 0;
1087 /* no action if we're recording */
1089 if (_session && _session->actively_recording()) {
1093 /* first, see if we're finishing a drag ... */
1095 bool were_dragging = false;
1096 if (_drags->active ()) {
1097 bool const r = _drags->end_grab (event);
1099 /* grab dragged, so do nothing else */
1103 were_dragging = true;
1106 button_selection (item, event, item_type);
1108 /* edit events get handled here */
1110 if (!_drags->active () && Keyboard::is_edit_event (&event->button)) {
1111 switch (item_type) {
1116 case TempoMarkerItem:
1117 edit_tempo_marker (item);
1120 case MeterMarkerItem:
1121 edit_meter_marker (item);
1124 case RegionViewName:
1125 if (clicked_regionview->name_active()) {
1126 return mouse_rename_region (item, event);
1130 case ControlPointItem:
1131 edit_control_point (item);
1140 /* context menu events get handled here */
1142 if (Keyboard::is_context_menu_event (&event->button)) {
1144 if (!_drags->active ()) {
1146 /* no matter which button pops up the context menu, tell the menu
1147 widget to use button 1 to drive menu selection.
1150 switch (item_type) {
1152 case FadeInHandleItem:
1154 case FadeOutHandleItem:
1155 popup_fade_context_menu (1, event->button.time, item, item_type);
1159 popup_track_context_menu (1, event->button.time, item_type, false, where);
1163 case RegionViewNameHighlight:
1164 case LeftFrameHandle:
1165 case RightFrameHandle:
1166 case RegionViewName:
1167 popup_track_context_menu (1, event->button.time, item_type, false, where);
1171 popup_track_context_menu (1, event->button.time, item_type, true, where);
1174 case AutomationTrackItem:
1175 popup_track_context_menu (1, event->button.time, item_type, false, where);
1179 case RangeMarkerBarItem:
1180 case TransportMarkerBarItem:
1181 case CdMarkerBarItem:
1184 popup_ruler_menu (where, item_type);
1188 marker_context_menu (&event->button, item);
1191 case TempoMarkerItem:
1192 tempo_or_meter_marker_context_menu (&event->button, item);
1195 case MeterMarkerItem:
1196 tempo_or_meter_marker_context_menu (&event->button, item);
1199 case CrossfadeViewItem:
1200 popup_track_context_menu (1, event->button.time, item_type, false, where);
1204 case ImageFrameItem:
1205 popup_imageframe_edit_menu(1, event->button.time, item, true) ;
1207 case ImageFrameTimeAxisItem:
1208 popup_imageframe_edit_menu(1, event->button.time, item, false) ;
1210 case MarkerViewItem:
1211 popup_marker_time_axis_edit_menu(1, event->button.time, item, true) ;
1213 case MarkerTimeAxisItem:
1214 popup_marker_time_axis_edit_menu(1, event->button.time, item, false) ;
1226 /* delete events get handled here */
1228 Editing::MouseMode const eff = effective_mouse_mode ();
1230 if (!_drags->active () && Keyboard::is_delete_event (&event->button)) {
1232 switch (item_type) {
1233 case TempoMarkerItem:
1234 remove_tempo_marker (item);
1237 case MeterMarkerItem:
1238 remove_meter_marker (item);
1242 remove_marker (*item, event);
1246 if (eff == MouseObject) {
1247 remove_clicked_region ();
1251 case ControlPointItem:
1252 if (eff == MouseGain) {
1253 remove_gain_control_point (item, event);
1255 remove_control_point (item, event);
1260 remove_midi_note (item, event);
1269 switch (event->button.button) {
1272 switch (item_type) {
1273 /* see comments in button_press_handler */
1274 case PlayheadCursorItem:
1277 case AutomationLineItem:
1278 case StartSelectionTrimItem:
1279 case EndSelectionTrimItem:
1283 if (!_dragging_playhead) {
1284 snap_to_with_modifier (where, event, 0, true);
1285 mouse_add_new_marker (where);
1289 case CdMarkerBarItem:
1290 if (!_dragging_playhead) {
1291 // if we get here then a dragged range wasn't done
1292 snap_to_with_modifier (where, event, 0, true);
1293 mouse_add_new_marker (where, true);
1298 if (!_dragging_playhead) {
1299 snap_to_with_modifier (where, event);
1300 mouse_add_new_tempo_event (where);
1305 if (!_dragging_playhead) {
1306 mouse_add_new_meter_event (pixel_to_frame (event->button.x));
1317 switch (item_type) {
1318 case AutomationTrackItem:
1319 atv = dynamic_cast<AutomationTimeAxisView*>(clicked_axisview);
1321 atv->add_automation_event (item, event, where, event->button.y);
1332 switch (item_type) {
1335 /* check that we didn't drag before releasing, since
1336 its really annoying to create new control
1337 points when doing this.
1339 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (clicked_regionview);
1340 if (were_dragging && arv) {
1341 arv->add_gain_point_event (item, event);
1347 case AutomationTrackItem:
1348 dynamic_cast<AutomationTimeAxisView*>(clicked_axisview)->
1349 add_automation_event (item, event, where, event->button.y);
1358 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1359 if (scrubbing_direction == 0) {
1360 /* no drag, just a click */
1361 switch (item_type) {
1363 play_selected_region ();
1369 /* make sure we stop */
1370 _session->request_transport_speed (0.0);
1387 switch (item_type) {
1389 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1391 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier|Keyboard::SecondaryModifier))) {
1394 // Button2 click is unused
1407 // x_style_paste (where, 1.0);
1427 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1434 if (last_item_entered != item) {
1435 last_item_entered = item;
1436 last_item_entered_n = 0;
1439 switch (item_type) {
1440 case ControlPointItem:
1441 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1442 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1443 cp->set_visible (true);
1447 at_y = cp->get_y ();
1448 cp->i2w (at_x, at_y);
1452 fraction = 1.0 - (cp->get_y() / cp->line().height());
1454 if (is_drawable() && !_drags->active ()) {
1455 track_canvas->get_window()->set_cursor (*fader_cursor);
1458 last_item_entered_n++;
1459 set_verbose_canvas_cursor (cp->line().get_verbose_cursor_string (fraction), at_x, at_y);
1460 if (last_item_entered_n < 10) {
1461 show_verbose_canvas_cursor ();
1467 if (mouse_mode == MouseGain) {
1468 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1470 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredGainLine.get();
1471 if (is_drawable()) {
1472 track_canvas->get_window()->set_cursor (*fader_cursor);
1477 case AutomationLineItem:
1478 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1480 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1482 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredAutomationLine.get();
1484 if (is_drawable()) {
1485 track_canvas->get_window()->set_cursor (*fader_cursor);
1490 case RegionViewNameHighlight:
1491 if (is_drawable() && (mouse_mode == MouseObject || (internal_editing() && mouse_mode == MouseRange))) {
1492 track_canvas->get_window()->set_cursor (*trimmer_cursor);
1496 case LeftFrameHandle:
1497 if (is_drawable() && (mouse_mode == MouseObject || (internal_editing() && mouse_mode == MouseRange))) {
1498 track_canvas->get_window()->set_cursor (*left_side_trim_cursor);
1502 case RightFrameHandle:
1503 if (is_drawable() && (mouse_mode == MouseObject || (internal_editing() && mouse_mode == MouseRange))) {
1504 track_canvas->get_window()->set_cursor (*right_side_trim_cursor);
1508 case StartSelectionTrimItem:
1509 case EndSelectionTrimItem:
1512 case ImageFrameHandleStartItem:
1513 case ImageFrameHandleEndItem:
1514 case MarkerViewHandleStartItem:
1515 case MarkerViewHandleEndItem:
1518 if (is_drawable()) {
1519 track_canvas->get_window()->set_cursor (*trimmer_cursor);
1523 case PlayheadCursorItem:
1524 if (is_drawable()) {
1525 switch (_edit_point) {
1527 track_canvas->get_window()->set_cursor (*grabber_edit_point_cursor);
1530 track_canvas->get_window()->set_cursor (*grabber_cursor);
1536 case RegionViewName:
1538 /* when the name is not an active item, the entire name highlight is for trimming */
1540 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1541 if (mouse_mode == MouseObject && is_drawable()) {
1542 track_canvas->get_window()->set_cursor (*trimmer_cursor);
1548 case AutomationTrackItem:
1549 if (is_drawable()) {
1550 Gdk::Cursor *cursor;
1551 switch (mouse_mode) {
1553 cursor = selector_cursor;
1556 cursor = zoom_cursor;
1559 cursor = cross_hair_cursor;
1563 track_canvas->get_window()->set_cursor (*cursor);
1565 AutomationTimeAxisView* atv;
1566 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1567 clear_entered_track = false;
1568 set_entered_track (atv);
1574 case RangeMarkerBarItem:
1575 case TransportMarkerBarItem:
1576 case CdMarkerBarItem:
1579 if (is_drawable()) {
1580 track_canvas->get_window()->set_cursor (*timebar_cursor);
1585 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1588 entered_marker = marker;
1589 marker->set_color_rgba (ARDOUR_UI::config()->canvasvar_EnteredMarker.get());
1591 case MeterMarkerItem:
1592 case TempoMarkerItem:
1593 if (is_drawable()) {
1594 track_canvas->get_window()->set_cursor (*timebar_cursor);
1598 case FadeInHandleItem:
1599 if (mouse_mode == MouseObject) {
1600 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1602 rect->property_fill_color_rgba() = 0;
1603 rect->property_outline_pixels() = 1;
1605 track_canvas->get_window()->set_cursor (*fade_in_cursor);
1609 case FadeOutHandleItem:
1610 if (mouse_mode == MouseObject) {
1611 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1613 rect->property_fill_color_rgba() = 0;
1614 rect->property_outline_pixels() = 1;
1616 track_canvas->get_window()->set_cursor (*fade_out_cursor);
1624 /* second pass to handle entered track status in a comprehensible way.
1627 switch (item_type) {
1629 case AutomationLineItem:
1630 case ControlPointItem:
1631 /* these do not affect the current entered track state */
1632 clear_entered_track = false;
1635 case AutomationTrackItem:
1636 /* handled above already */
1640 set_entered_track (0);
1648 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1658 switch (item_type) {
1659 case ControlPointItem:
1660 cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
1661 if (cp->line().the_list()->interpolation() != AutomationList::Discrete) {
1662 if (cp->line().npoints() > 1 && !cp->selected()) {
1663 cp->set_visible (false);
1667 if (is_drawable()) {
1668 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1671 hide_verbose_canvas_cursor ();
1674 case RegionViewNameHighlight:
1675 case LeftFrameHandle:
1676 case RightFrameHandle:
1677 case StartSelectionTrimItem:
1678 case EndSelectionTrimItem:
1679 case PlayheadCursorItem:
1682 case ImageFrameHandleStartItem:
1683 case ImageFrameHandleEndItem:
1684 case MarkerViewHandleStartItem:
1685 case MarkerViewHandleEndItem:
1688 if (is_drawable()) {
1689 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1694 case AutomationLineItem:
1695 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1697 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1699 line->property_fill_color_rgba() = al->get_line_color();
1701 if (is_drawable()) {
1702 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1706 case RegionViewName:
1707 /* see enter_handler() for notes */
1708 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1709 if (is_drawable() && mouse_mode == MouseObject) {
1710 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1715 case RangeMarkerBarItem:
1716 case TransportMarkerBarItem:
1717 case CdMarkerBarItem:
1721 if (is_drawable()) {
1722 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1727 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1731 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
1732 location_flags_changed (loc, this);
1735 case MeterMarkerItem:
1736 case TempoMarkerItem:
1738 if (is_drawable()) {
1739 track_canvas->get_window()->set_cursor (*timebar_cursor);
1744 case FadeInHandleItem:
1745 case FadeOutHandleItem:
1746 rv = static_cast<RegionView*>(item->get_data ("regionview"));
1748 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1750 rect->property_fill_color_rgba() = rv->get_fill_color();
1751 rect->property_outline_pixels() = 0;
1754 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1757 case AutomationTrackItem:
1758 if (is_drawable()) {
1759 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1760 clear_entered_track = true;
1761 Glib::signal_idle().connect (sigc::mem_fun(*this, &Editor::left_automation_track));
1773 Editor::left_automation_track ()
1775 if (clear_entered_track) {
1776 set_entered_track (0);
1777 clear_entered_track = false;
1783 Editor::scrub (nframes64_t frame, double current_x)
1787 if (scrubbing_direction == 0) {
1789 _session->request_locate (frame, false);
1790 _session->request_transport_speed (0.1);
1791 scrubbing_direction = 1;
1795 if (last_scrub_x > current_x) {
1797 /* pointer moved to the left */
1799 if (scrubbing_direction > 0) {
1801 /* we reversed direction to go backwards */
1804 scrub_reverse_distance += (int) (last_scrub_x - current_x);
1808 /* still moving to the left (backwards) */
1810 scrub_reversals = 0;
1811 scrub_reverse_distance = 0;
1813 delta = 0.01 * (last_scrub_x - current_x);
1814 _session->request_transport_speed (_session->transport_speed() - delta);
1818 /* pointer moved to the right */
1820 if (scrubbing_direction < 0) {
1821 /* we reversed direction to go forward */
1824 scrub_reverse_distance += (int) (current_x - last_scrub_x);
1827 /* still moving to the right */
1829 scrub_reversals = 0;
1830 scrub_reverse_distance = 0;
1832 delta = 0.01 * (current_x - last_scrub_x);
1833 _session->request_transport_speed (_session->transport_speed() + delta);
1837 /* if there have been more than 2 opposite motion moves detected, or one that moves
1838 back more than 10 pixels, reverse direction
1841 if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
1843 if (scrubbing_direction > 0) {
1844 /* was forwards, go backwards */
1845 _session->request_transport_speed (-0.1);
1846 scrubbing_direction = -1;
1848 /* was backwards, go forwards */
1849 _session->request_transport_speed (0.1);
1850 scrubbing_direction = 1;
1853 scrub_reverse_distance = 0;
1854 scrub_reversals = 0;
1858 last_scrub_x = current_x;
1862 Editor::motion_handler (ArdourCanvas::Item* /*item*/, GdkEvent* event, bool from_autoscroll)
1864 if (event->motion.is_hint) {
1867 /* We call this so that MOTION_NOTIFY events continue to be
1868 delivered to the canvas. We need to do this because we set
1869 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
1870 the density of the events, at the expense of a round-trip
1871 to the server. Given that this will mostly occur on cases
1872 where DISPLAY = :0.0, and given the cost of what the motion
1873 event might do, its a good tradeoff.
1876 track_canvas->get_pointer (x, y);
1879 if (current_stepping_trackview) {
1880 /* don't keep the persistent stepped trackview if the mouse moves */
1881 current_stepping_trackview = 0;
1882 step_timeout.disconnect ();
1885 if (_session && _session->actively_recording()) {
1886 /* Sorry. no dragging stuff around while we record */
1890 JoinObjectRangeState const old = _join_object_range_state;
1891 update_join_object_range_location (event->motion.x, event->motion.y);
1892 if (_join_object_range_state != old) {
1893 set_canvas_cursor ();
1896 bool handled = false;
1897 if (_drags->active ()) {
1898 handled = _drags->motion_handler (event, from_autoscroll);
1904 track_canvas_motion (event);
1909 Editor::remove_gain_control_point (ArdourCanvas::Item*item, GdkEvent* /*event*/)
1911 ControlPoint* control_point;
1913 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
1914 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
1918 // We shouldn't remove the first or last gain point
1919 if (control_point->line().is_last_point(*control_point) ||
1920 control_point->line().is_first_point(*control_point)) {
1924 control_point->line().remove_point (*control_point);
1928 Editor::remove_control_point (ArdourCanvas::Item* item, GdkEvent* /*event*/)
1930 ControlPoint* control_point;
1932 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
1933 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
1937 control_point->line().remove_point (*control_point);
1941 Editor::edit_control_point (ArdourCanvas::Item* item)
1943 ControlPoint* p = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"));
1946 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
1950 ControlPointDialog d (p);
1951 d.set_position (Gtk::WIN_POS_MOUSE);
1954 if (d.run () != RESPONSE_ACCEPT) {
1958 p->line().modify_point_y (*p, d.get_y_fraction ());
1963 Editor::visible_order_range (int* low, int* high) const
1965 *low = TimeAxisView::max_order ();
1968 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
1970 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
1972 if (!rtv->hidden()) {
1974 if (*high < rtv->order()) {
1975 *high = rtv->order ();
1978 if (*low > rtv->order()) {
1979 *low = rtv->order ();
1986 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
1988 /* Either add to or set the set the region selection, unless
1989 this is an alignment click (control used)
1992 if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
1993 TimeAxisView* tv = &rv.get_time_axis_view();
1994 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
1996 if (rtv && rtv->is_track()) {
1997 speed = rtv->track()->speed();
2000 nframes64_t where = get_preferred_edit_position();
2004 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
2006 align_region (rv.region(), SyncPoint, (nframes64_t) (where * speed));
2008 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
2010 align_region (rv.region(), End, (nframes64_t) (where * speed));
2014 align_region (rv.region(), Start, (nframes64_t) (where * speed));
2021 Editor::show_verbose_time_cursor (nframes64_t frame, double offset, double xpos, double ypos)
2024 Timecode::Time timecode;
2027 nframes64_t frame_rate;
2030 if (_session == 0) {
2036 if (Profile->get_sae() || Profile->get_small_screen()) {
2037 m = ARDOUR_UI::instance()->primary_clock.mode();
2039 m = ARDOUR_UI::instance()->secondary_clock.mode();
2043 case AudioClock::BBT:
2044 _session->bbt_time (frame, bbt);
2045 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, bbt.bars, bbt.beats, bbt.ticks);
2048 case AudioClock::Timecode:
2049 _session->timecode_time (frame, timecode);
2050 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
2053 case AudioClock::MinSec:
2054 /* XXX this is copied from show_verbose_duration_cursor() */
2055 frame_rate = _session->frame_rate();
2056 hours = frame / (frame_rate * 3600);
2057 frame = frame % (frame_rate * 3600);
2058 mins = frame / (frame_rate * 60);
2059 frame = frame % (frame_rate * 60);
2060 secs = (float) frame / (float) frame_rate;
2061 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%07.4f", hours, mins, secs);
2065 snprintf (buf, sizeof(buf), "%" PRIi64, frame);
2069 if (xpos >= 0 && ypos >=0) {
2070 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
2072 set_verbose_canvas_cursor (buf, _drags->current_pointer_x() + offset - horizontal_position(), _drags->current_pointer_y() + offset - vertical_adjustment.get_value() + canvas_timebars_vsize);
2074 show_verbose_canvas_cursor ();
2078 Editor::show_verbose_duration_cursor (nframes64_t start, nframes64_t end, double offset, double xpos, double ypos)
2081 Timecode::Time timecode;
2085 nframes64_t distance, frame_rate;
2087 Meter meter_at_start(_session->tempo_map().meter_at(start));
2089 if (_session == 0) {
2095 if (Profile->get_sae() || Profile->get_small_screen()) {
2096 m = ARDOUR_UI::instance()->primary_clock.mode ();
2098 m = ARDOUR_UI::instance()->secondary_clock.mode ();
2102 case AudioClock::BBT:
2103 _session->bbt_time (start, sbbt);
2104 _session->bbt_time (end, ebbt);
2107 /* XXX this computation won't work well if the
2108 user makes a selection that spans any meter changes.
2111 ebbt.bars -= sbbt.bars;
2112 if (ebbt.beats >= sbbt.beats) {
2113 ebbt.beats -= sbbt.beats;
2116 ebbt.beats = int(meter_at_start.beats_per_bar()) + ebbt.beats - sbbt.beats;
2118 if (ebbt.ticks >= sbbt.ticks) {
2119 ebbt.ticks -= sbbt.ticks;
2122 ebbt.ticks = int(Meter::ticks_per_beat) + ebbt.ticks - sbbt.ticks;
2125 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, ebbt.bars, ebbt.beats, ebbt.ticks);
2128 case AudioClock::Timecode:
2129 _session->timecode_duration (end - start, timecode);
2130 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
2133 case AudioClock::MinSec:
2134 /* XXX this stuff should be elsewhere.. */
2135 distance = end - start;
2136 frame_rate = _session->frame_rate();
2137 hours = distance / (frame_rate * 3600);
2138 distance = distance % (frame_rate * 3600);
2139 mins = distance / (frame_rate * 60);
2140 distance = distance % (frame_rate * 60);
2141 secs = (float) distance / (float) frame_rate;
2142 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%07.4f", hours, mins, secs);
2146 snprintf (buf, sizeof(buf), "%" PRIi64, end - start);
2150 if (xpos >= 0 && ypos >=0) {
2151 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
2154 set_verbose_canvas_cursor (buf, _drags->current_pointer_x() + offset, _drags->current_pointer_y() + offset);
2157 show_verbose_canvas_cursor ();
2161 Editor::collect_new_region_view (RegionView* rv)
2163 latest_regionviews.push_back (rv);
2167 Editor::collect_and_select_new_region_view (RegionView* rv)
2170 latest_regionviews.push_back (rv);
2174 Editor::cancel_selection ()
2176 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2177 (*i)->hide_selection ();
2180 selection->clear ();
2181 clicked_selection = 0;
2186 Editor::single_contents_trim (RegionView& rv, nframes64_t frame_delta, bool left_direction, bool swap_direction)
2188 boost::shared_ptr<Region> region (rv.region());
2190 if (region->locked()) {
2194 nframes64_t new_bound;
2197 TimeAxisView* tvp = clicked_axisview;
2198 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2200 if (tv && tv->is_track()) {
2201 speed = tv->track()->speed();
2204 if (left_direction) {
2205 if (swap_direction) {
2206 new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
2208 new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
2211 if (swap_direction) {
2212 new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
2214 new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
2218 region->trim_start ((nframes64_t) (new_bound * speed), this);
2219 rv.region_changed (PropertyChange (ARDOUR::Properties::start));
2223 Editor::single_start_trim (RegionView& rv, nframes64_t new_bound, bool no_overlap)
2225 boost::shared_ptr<Region> region (rv.region());
2227 if (region->locked()) {
2232 TimeAxisView* tvp = clicked_axisview;
2233 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2235 if (tv && tv->is_track()) {
2236 speed = tv->track()->speed();
2239 nframes64_t pre_trim_first_frame = region->first_frame();
2241 region->trim_front ((nframes64_t) (new_bound * speed), this);
2244 //Get the next region on the left of this region and shrink/expand it.
2245 boost::shared_ptr<Playlist> playlist (region->playlist());
2246 boost::shared_ptr<Region> region_left = playlist->find_next_region (pre_trim_first_frame, End, 0);
2248 bool regions_touching = false;
2250 if (region_left != 0 && (pre_trim_first_frame == region_left->last_frame() + 1)){
2251 regions_touching = true;
2254 //Only trim region on the left if the first frame has gone beyond the left region's last frame.
2255 if (region_left != 0 &&
2256 (region_left->last_frame() > region->first_frame() || regions_touching))
2258 region_left->trim_end(region->first_frame() - 1, this);
2262 rv.region_changed (ARDOUR::bounds_change);
2266 Editor::single_end_trim (RegionView& rv, nframes64_t new_bound, bool no_overlap)
2268 boost::shared_ptr<Region> region (rv.region());
2270 if (region->locked()) {
2275 TimeAxisView* tvp = clicked_axisview;
2276 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2278 if (tv && tv->is_track()) {
2279 speed = tv->track()->speed();
2282 nframes64_t pre_trim_last_frame = region->last_frame();
2284 region->trim_end ((nframes64_t) (new_bound * speed), this);
2287 //Get the next region on the right of this region and shrink/expand it.
2288 boost::shared_ptr<Playlist> playlist (region->playlist());
2289 boost::shared_ptr<Region> region_right = playlist->find_next_region (pre_trim_last_frame, Start, 1);
2291 bool regions_touching = false;
2293 if (region_right != 0 && (pre_trim_last_frame == region_right->first_frame() - 1)) {
2294 regions_touching = true;
2297 //Only trim region on the right if the last frame has gone beyond the right region's first frame.
2298 if (region_right != 0 &&
2299 (region_right->first_frame() < region->last_frame() || regions_touching))
2301 region_right->trim_front(region->last_frame() + 1, this);
2304 rv.region_changed (ARDOUR::bounds_change);
2307 rv.region_changed (PropertyChange (ARDOUR::Properties::length));
2313 Editor::point_trim (GdkEvent* event, nframes64_t new_bound)
2315 RegionView* rv = clicked_regionview;
2317 /* Choose action dependant on which button was pressed */
2318 switch (event->button.button) {
2320 begin_reversible_command (_("Start point trim"));
2322 if (selection->selected (rv)) {
2323 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
2324 i != selection->regions.by_layer().end(); ++i)
2327 cerr << "region view contains null region" << endl;
2330 if (!(*i)->region()->locked()) {
2331 (*i)->region()->clear_history ();
2332 (*i)->region()->trim_front (new_bound, this);
2333 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2338 if (!rv->region()->locked()) {
2339 rv->region()->clear_history ();
2340 rv->region()->trim_front (new_bound, this);
2341 _session->add_command(new StatefulDiffCommand (rv->region()));
2345 commit_reversible_command();
2349 begin_reversible_command (_("End point trim"));
2351 if (selection->selected (rv)) {
2353 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
2355 if (!(*i)->region()->locked()) {
2356 (*i)->region()->clear_history();
2357 (*i)->region()->trim_end (new_bound, this);
2358 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2364 if (!rv->region()->locked()) {
2365 rv->region()->clear_history ();
2366 rv->region()->trim_end (new_bound, this);
2367 _session->add_command (new StatefulDiffCommand (rv->region()));
2371 commit_reversible_command();
2380 Editor::thaw_region_after_trim (RegionView& rv)
2382 boost::shared_ptr<Region> region (rv.region());
2384 if (region->locked()) {
2388 region->resume_property_changes ();
2390 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(&rv);
2393 arv->unhide_envelope ();
2398 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* /*event*/)
2403 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2404 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2408 Location* location = find_location_from_marker (marker, is_start);
2409 location->set_hidden (true, this);
2414 Editor::reposition_zoom_rect (nframes64_t start, nframes64_t end)
2416 double x1 = frame_to_pixel (start);
2417 double x2 = frame_to_pixel (end);
2418 double y2 = full_canvas_height - 1.0;
2420 zoom_rect->property_x1() = x1;
2421 zoom_rect->property_y1() = 1.0;
2422 zoom_rect->property_x2() = x2;
2423 zoom_rect->property_y2() = y2;
2428 Editor::mouse_rename_region (ArdourCanvas::Item* /*item*/, GdkEvent* /*event*/)
2430 using namespace Gtkmm2ext;
2432 ArdourPrompter prompter (false);
2434 prompter.set_prompt (_("Name for region:"));
2435 prompter.set_initial_text (clicked_regionview->region()->name());
2436 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
2437 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
2438 prompter.show_all ();
2439 switch (prompter.run ()) {
2440 case Gtk::RESPONSE_ACCEPT:
2442 prompter.get_result(str);
2444 clicked_regionview->region()->set_name (str);
2453 Editor::mouse_brush_insert_region (RegionView* rv, nframes64_t pos)
2455 /* no brushing without a useful snap setting */
2457 switch (_snap_mode) {
2459 return; /* can't work because it allows region to be placed anywhere */
2464 switch (_snap_type) {
2472 /* don't brush a copy over the original */
2474 if (pos == rv->region()->position()) {
2478 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
2480 if (rtv == 0 || !rtv->is_track()) {
2484 boost::shared_ptr<Playlist> playlist = rtv->playlist();
2485 double speed = rtv->track()->speed();
2487 playlist->clear_history ();
2488 boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region()));
2489 playlist->add_region (new_region, (nframes64_t) (pos * speed));
2490 _session->add_command (new StatefulDiffCommand (playlist));
2492 // playlist is frozen, so we have to update manually XXX this is disgusting
2494 playlist->RegionAdded (new_region); /* EMIT SIGNAL */
2498 Editor::track_height_step_timeout ()
2500 if (get_microseconds() - last_track_height_step_timestamp < 250000) {
2501 current_stepping_trackview = 0;
2508 Editor::add_region_drag (ArdourCanvas::Item* item, GdkEvent* event, RegionView* region_view)
2510 assert (region_view);
2512 _region_motion_group->raise_to_top ();
2514 if (Config->get_edit_mode() == Splice) {
2515 _drags->add (new RegionSpliceDrag (this, item, region_view, selection->regions.by_layer()));
2517 RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id);
2518 _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), false, false));
2521 /* sync the canvas to what we think is its current state */
2522 update_canvas_now();
2526 Editor::add_region_copy_drag (ArdourCanvas::Item* item, GdkEvent* event, RegionView* region_view)
2528 assert (region_view);
2530 _region_motion_group->raise_to_top ();
2532 RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id);
2533 _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), false, true));
2537 Editor::add_region_brush_drag (ArdourCanvas::Item* item, GdkEvent* event, RegionView* region_view)
2539 assert (region_view);
2541 if (Config->get_edit_mode() == Splice) {
2545 RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id);
2546 _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), true, false));
2548 begin_reversible_command (_("Drag region brush"));
2551 /** Start a grab where a time range is selected, track(s) are selected, and the
2552 * user clicks and drags a region with a modifier in order to create a new region containing
2553 * the section of the clicked region that lies within the time range.
2556 Editor::start_selection_grab (ArdourCanvas::Item* /*item*/, GdkEvent* event)
2558 if (clicked_regionview == 0) {
2562 /* lets try to create new Region for the selection */
2564 vector<boost::shared_ptr<Region> > new_regions;
2565 create_region_from_selection (new_regions);
2567 if (new_regions.empty()) {
2571 /* XXX fix me one day to use all new regions */
2573 boost::shared_ptr<Region> region (new_regions.front());
2575 /* add it to the current stream/playlist.
2577 tricky: the streamview for the track will add a new regionview. we will
2578 catch the signal it sends when it creates the regionview to
2579 set the regionview we want to then drag.
2582 latest_regionviews.clear();
2583 sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
2585 /* A selection grab currently creates two undo/redo operations, one for
2586 creating the new region and another for moving it.
2589 begin_reversible_command (_("selection grab"));
2591 boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
2593 playlist->clear_history ();
2594 clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
2595 _session->add_command(new StatefulDiffCommand (playlist));
2597 commit_reversible_command ();
2601 if (latest_regionviews.empty()) {
2602 /* something went wrong */
2606 /* we need to deselect all other regionviews, and select this one
2607 i'm ignoring undo stuff, because the region creation will take care of it
2609 selection->set (latest_regionviews);
2611 _drags->set (new RegionMoveDrag (this, latest_regionviews.front()->get_canvas_group(), latest_regionviews.front(), latest_regionviews, false, false), event);
2617 if (_drags->active ()) {
2620 selection->clear ();
2625 Editor::set_internal_edit (bool yn)
2627 _internal_editing = yn;
2630 mouse_select_button.set_image (*(manage (new Image (::get_icon("midi_tool_pencil")))));
2631 mouse_select_button.get_image ()->show ();
2632 ARDOUR_UI::instance()->tooltips().set_tip (mouse_select_button, _("Draw/Edit MIDI Notes"));
2634 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2635 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (*i);
2637 mtv->start_step_editing ();
2641 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
2642 (*i)->hide_selection ();
2645 start_step_editing ();
2646 set_canvas_cursor ();
2650 mouse_select_button.set_image (*(manage (new Image (::get_icon("tool_range")))));
2651 mouse_select_button.get_image ()->show ();
2652 ARDOUR_UI::instance()->tooltips().set_tip (mouse_select_button, _("Select/Move Ranges"));
2653 stop_step_editing ();
2655 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2656 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (*i);
2658 mtv->stop_step_editing ();
2662 mouse_mode_toggled (mouse_mode);
2666 /** Update _join_object_range_state which indicate whether we are over the top or bottom half of a region view,
2667 * used by the `join object/range' tool mode.
2670 Editor::update_join_object_range_location (double x, double y)
2672 /* XXX: actually, this decides based on whether the mouse is in the top or bottom half of a RouteTimeAxisView;
2673 entered_{track,regionview} is not always setup (e.g. if the mouse is over a TimeSelection), and to get a Region
2674 that we're over requires searching the playlist.
2677 if (join_object_range_button.get_active() == false || (mouse_mode != MouseRange && mouse_mode != MouseObject)) {
2678 _join_object_range_state = JOIN_OBJECT_RANGE_NONE;
2682 if (mouse_mode == MouseObject) {
2683 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2684 } else if (mouse_mode == MouseRange) {
2685 _join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
2688 /* XXX: maybe we should make entered_track work in all cases, rather than resorting to this */
2689 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y + vertical_adjustment.get_value() - canvas_timebars_vsize);
2693 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
2698 rtv->canvas_display()->w2i (cx, cy);
2700 bool const top_half = cy < rtv->current_height () / 2;
2702 _join_object_range_state = top_half ? JOIN_OBJECT_RANGE_RANGE : JOIN_OBJECT_RANGE_OBJECT;
2708 Editor::effective_mouse_mode () const
2710 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
2712 } else if (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE) {
2720 Editor::remove_midi_note (ArdourCanvas::Item* item, GdkEvent *)
2722 ArdourCanvas::CanvasNoteEvent* e = dynamic_cast<ArdourCanvas::CanvasNoteEvent*> (item);
2725 e->region_view().delete_note (e->note ());