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 if (!internal_editing()) {
329 if (mouse_mode != MouseRange && _join_object_range_state == JOIN_OBJECT_RANGE_NONE) {
331 /* in all modes except range and joined object/range, hide the range selection,
332 show the object (region) selection.
335 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
336 (*i)->set_should_show_selection (true);
338 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
339 (*i)->hide_selection ();
345 in range or object/range mode, show the range selection.
348 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
349 (*i)->show_selection (selection->time);
354 set_canvas_cursor ();
358 Editor::step_mouse_mode (bool next)
360 switch (current_mouse_mode()) {
363 if (Profile->get_sae()) {
364 set_mouse_mode (MouseZoom);
366 set_mouse_mode (MouseRange);
369 set_mouse_mode (MouseTimeFX);
374 if (next) set_mouse_mode (MouseZoom);
375 else set_mouse_mode (MouseObject);
380 if (Profile->get_sae()) {
381 set_mouse_mode (MouseTimeFX);
383 set_mouse_mode (MouseGain);
386 if (Profile->get_sae()) {
387 set_mouse_mode (MouseObject);
389 set_mouse_mode (MouseRange);
395 if (next) set_mouse_mode (MouseTimeFX);
396 else set_mouse_mode (MouseZoom);
401 set_mouse_mode (MouseAudition);
403 if (Profile->get_sae()) {
404 set_mouse_mode (MouseZoom);
406 set_mouse_mode (MouseGain);
412 if (next) set_mouse_mode (MouseObject);
413 else set_mouse_mode (MouseTimeFX);
419 Editor::button_selection (ArdourCanvas::Item* /*item*/, GdkEvent* event, ItemType item_type)
421 /* in object/audition/timefx/gain-automation mode,
422 any button press sets the selection if the object
423 can be selected. this is a bit of hack, because
424 we want to avoid this if the mouse operation is a
427 note: not dbl-click or triple-click
430 if (((mouse_mode != MouseObject) &&
431 (_join_object_range_state != JOIN_OBJECT_RANGE_OBJECT) &&
432 (mouse_mode != MouseAudition || item_type != RegionItem) &&
433 (mouse_mode != MouseTimeFX || item_type != RegionItem) &&
434 (mouse_mode != MouseGain) &&
435 (mouse_mode != MouseRange)) ||
437 ((event->type != GDK_BUTTON_PRESS && event->type != GDK_BUTTON_RELEASE) || event->button.button > 3)) {
442 if (event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) {
444 if ((event->button.state & Keyboard::RelevantModifierKeyMask) && event->button.button != 1) {
446 /* almost no selection action on modified button-2 or button-3 events */
448 if (item_type != RegionItem && event->button.button != 2) {
454 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
455 bool press = (event->type == GDK_BUTTON_PRESS);
457 // begin_reversible_command (_("select on click"));
461 if (mouse_mode != MouseRange || _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
462 set_selected_regionview_from_click (press, op, true);
463 } else if (event->type == GDK_BUTTON_PRESS) {
464 selection->clear_tracks ();
465 set_selected_track_as_side_effect (true);
467 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT && !selection->regions.empty()) {
468 clicked_selection = select_range_around_region (selection->regions.front());
473 case RegionViewNameHighlight:
475 if (mouse_mode != MouseRange || _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
476 set_selected_regionview_from_click (press, op, true);
477 } else if (event->type == GDK_BUTTON_PRESS) {
478 set_selected_track_as_side_effect ();
483 case FadeInHandleItem:
485 case FadeOutHandleItem:
487 if (mouse_mode != MouseRange || _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
488 set_selected_regionview_from_click (press, op, true);
489 } else if (event->type == GDK_BUTTON_PRESS) {
490 set_selected_track_as_side_effect ();
494 case ControlPointItem:
495 set_selected_track_as_side_effect ();
496 if (mouse_mode != MouseRange || _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
497 set_selected_control_point_from_click (op, false);
502 /* for context click, select track */
503 if (event->button.button == 3) {
504 selection->clear_tracks ();
505 set_selected_track_as_side_effect (true);
509 case AutomationTrackItem:
510 set_selected_track_as_side_effect (true);
519 Editor::button_press_handler_1 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
521 if (_drags->active ()) {
525 /* single mouse clicks on any of these item types operate
526 independent of mouse mode, mostly because they are
527 not on the main track canvas or because we want
532 case PlayheadCursorItem:
533 _drags->set (new CursorDrag (this, item, true), event);
537 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
538 hide_marker (item, event);
540 _drags->set (new MarkerDrag (this, item), event);
544 case TempoMarkerItem:
546 new TempoMarkerDrag (
549 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
555 case MeterMarkerItem:
557 new MeterMarkerDrag (
560 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
569 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
570 _drags->set (new CursorDrag (this, &playhead_cursor->canvas_item, false), event);
576 case RangeMarkerBarItem:
577 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
578 _drags->set (new CursorDrag (this, &playhead_cursor->canvas_item, false), event);
580 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateRangeMarker), event);
585 case CdMarkerBarItem:
586 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
587 _drags->set (new CursorDrag (this, &playhead_cursor->canvas_item, false), event);
589 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateCDMarker), event);
594 case TransportMarkerBarItem:
595 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
596 _drags->set (new CursorDrag (this, &playhead_cursor->canvas_item, false), event);
598 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateTransportMarker), event);
607 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
608 /* special case: allow trim of range selections in joined object mode;
609 in theory eff should equal MouseRange in this case, but it doesn't
610 because entering the range selection canvas item results in entered_regionview
611 being set to 0, so update_join_object_range_location acts as if we aren't
614 if (item_type == StartSelectionTrimItem) {
615 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
616 } else if (item_type == EndSelectionTrimItem) {
617 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
621 Editing::MouseMode eff = effective_mouse_mode ();
623 /* special case: allow drag of region fade in/out in object mode with join object/range enabled */
624 if (item_type == FadeInHandleItem || item_type == FadeOutHandleItem) {
631 case StartSelectionTrimItem:
632 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
635 case EndSelectionTrimItem:
636 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
640 if (Keyboard::modifier_state_contains
641 (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier))) {
642 // contains and not equals because I can't use alt as a modifier alone.
643 start_selection_grab (item, event);
644 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
645 /* grab selection for moving */
646 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionMove), event);
648 double const y = event->button.y + vertical_adjustment.get_value() - canvas_timebars_vsize;
649 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
651 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
652 if (join_object_range_button.get_active() && atv) {
653 /* smart "join" mode: drag automation */
654 _drags->set (new AutomationRangeDrag (this, atv->base_item(), selection->time), event);
656 /* this was debated, but decided the more common action was to
657 make a new selection */
658 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
665 if (!internal_editing()) {
666 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
675 if (internal_editing()) {
676 _drags->set (new NoteDrag (this, item), event);
685 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)) &&
686 event->type == GDK_BUTTON_PRESS) {
688 _drags->set (new RubberbandSelectDrag (this, item), event);
690 } else if (event->type == GDK_BUTTON_PRESS) {
693 case FadeInHandleItem:
695 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
696 _drags->set (new FadeInDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), s), event);
700 case FadeOutHandleItem:
702 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
703 _drags->set (new FadeOutDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), s), event);
708 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
709 add_region_copy_drag (item, event, clicked_regionview);
710 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
711 add_region_brush_drag (item, event, clicked_regionview);
713 add_region_drag (item, event, clicked_regionview);
716 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT && !selection->regions.empty()) {
717 _drags->add (new SelectionDrag (this, clicked_axisview->get_selection_rect (clicked_selection)->rect, SelectionDrag::SelectionMove));
720 _drags->start_grab (event);
723 case RegionViewNameHighlight:
725 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
726 _drags->set (new TrimDrag (this, item, clicked_regionview, s.by_layer()), event);
733 /* rename happens on edit clicks */
734 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
735 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, s.by_layer()), event);
740 case ControlPointItem:
741 _drags->set (new ControlPointDrag (this, item), event);
745 case AutomationLineItem:
746 _drags->set (new LineDrag (this, item), event);
751 if (internal_editing()) {
752 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
755 _drags->set (new RubberbandSelectDrag (this, item), event);
759 case AutomationTrackItem:
760 /* rubberband drag to select automation points */
761 _drags->set (new RubberbandSelectDrag (this, item), event);
766 if (join_object_range_button.get_active()) {
767 /* we're in "smart" joined mode, and we've clicked on a Selection */
768 double const y = event->button.y + vertical_adjustment.get_value() - canvas_timebars_vsize;
769 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
771 /* if we're over an automation track, start a drag of its data */
772 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
774 _drags->set (new AutomationRangeDrag (this, atv->base_item(), selection->time), event);
777 /* if we're over a track and a region, and in the `object' part of a region,
778 put a selection around the region and drag both
780 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
781 if (rtv && _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
782 boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track> (rtv->route ());
784 boost::shared_ptr<Playlist> pl = t->playlist ();
787 boost::shared_ptr<Region> r = pl->top_region_at (event_frame (event));
789 RegionView* rv = rtv->view()->find_view (r);
790 clicked_selection = select_range_around_region (rv);
791 _drags->add (new SelectionDrag (this, item, SelectionDrag::SelectionMove));
792 list<RegionView*> rvs;
794 _drags->add (new RegionMoveDrag (this, item, rv, rvs, false, false));
795 _drags->start_grab (event);
806 case ImageFrameHandleStartItem:
807 imageframe_start_handle_op(item, event) ;
810 case ImageFrameHandleEndItem:
811 imageframe_end_handle_op(item, event) ;
814 case MarkerViewHandleStartItem:
815 markerview_item_start_handle_op(item, event) ;
818 case MarkerViewHandleEndItem:
819 markerview_item_end_handle_op(item, event) ;
823 start_markerview_grab(item, event) ;
826 start_imageframe_grab(item, event) ;
844 /* start a grab so that if we finish after moving
845 we can tell what happened.
847 _drags->set (new RegionGainDrag (this, item), event, current_canvas_cursor);
851 _drags->set (new LineDrag (this, item), event);
854 case ControlPointItem:
855 _drags->set (new ControlPointDrag (this, item), event);
866 case ControlPointItem:
867 _drags->set (new ControlPointDrag (this, item), event);
870 case AutomationLineItem:
871 _drags->set (new LineDrag (this, item), event);
875 // XXX need automation mode to identify which
877 // start_line_grab_from_regionview (item, event);
887 if (event->type == GDK_BUTTON_PRESS) {
888 _drags->set (new MouseZoomDrag (this, item), event);
895 if (internal_editing() && item_type == NoteItem) {
896 /* drag notes if we're in internal edit mode */
897 _drags->set (new NoteResizeDrag (this, item), event);
899 } else if ((!internal_editing() || dynamic_cast<AudioRegionView*> (clicked_regionview)) && clicked_regionview) {
900 /* do time-FX if we're not in internal edit mode, or we are but we clicked on an audio region */
901 _drags->set (new TimeFXDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
907 _drags->set (new ScrubDrag (this, item), event);
909 scrub_reverse_distance = 0;
910 last_scrub_x = event->button.x;
911 scrubbing_direction = 0;
912 track_canvas->get_window()->set_cursor (*transparent_cursor);
924 Editor::button_press_handler_2 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
926 Editing::MouseMode const eff = effective_mouse_mode ();
931 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
932 add_region_copy_drag (item, event, clicked_regionview);
934 add_region_drag (item, event, clicked_regionview);
936 _drags->start_grab (event);
939 case ControlPointItem:
940 _drags->set (new ControlPointDrag (this, item), event);
949 case RegionViewNameHighlight:
950 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
955 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer()), event);
966 /* relax till release */
972 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
973 temporal_zoom_session();
975 temporal_zoom_to_frame (true, event_frame(event));
988 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
990 if (event->type != GDK_BUTTON_PRESS) {
994 Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->track_canvas->get_window();
997 Glib::RefPtr<const Gdk::Window> pointer_window;
1000 Gdk::ModifierType mask;
1002 pointer_window = canvas_window->get_pointer (x, y, mask);
1004 if (pointer_window == track_canvas->get_bin_window()) {
1005 track_canvas->window_to_world (x, y, wx, wy);
1006 allow_vertical_scroll = true;
1008 allow_vertical_scroll = false;
1012 track_canvas->grab_focus();
1014 if (_session && _session->actively_recording()) {
1018 button_selection (item, event, item_type);
1020 if (!_drags->active () &&
1021 (Keyboard::is_delete_event (&event->button) ||
1022 Keyboard::is_context_menu_event (&event->button) ||
1023 Keyboard::is_edit_event (&event->button))) {
1025 /* handled by button release */
1029 switch (event->button.button) {
1031 return button_press_handler_1 (item, event, item_type);
1035 return button_press_handler_2 (item, event, item_type);
1050 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1052 nframes64_t where = event_frame (event, 0, 0);
1053 AutomationTimeAxisView* atv = 0;
1055 /* no action if we're recording */
1057 if (_session && _session->actively_recording()) {
1061 /* first, see if we're finishing a drag ... */
1063 bool were_dragging = false;
1064 if (_drags->active ()) {
1065 bool const r = _drags->end_grab (event);
1067 /* grab dragged, so do nothing else */
1071 were_dragging = true;
1074 button_selection (item, event, item_type);
1076 /* edit events get handled here */
1078 if (!_drags->active () && Keyboard::is_edit_event (&event->button)) {
1079 switch (item_type) {
1084 case TempoMarkerItem:
1085 edit_tempo_marker (item);
1088 case MeterMarkerItem:
1089 edit_meter_marker (item);
1092 case RegionViewName:
1093 if (clicked_regionview->name_active()) {
1094 return mouse_rename_region (item, event);
1098 case ControlPointItem:
1099 edit_control_point (item);
1108 /* context menu events get handled here */
1110 if (Keyboard::is_context_menu_event (&event->button)) {
1112 if (!_drags->active ()) {
1114 /* no matter which button pops up the context menu, tell the menu
1115 widget to use button 1 to drive menu selection.
1118 switch (item_type) {
1120 case FadeInHandleItem:
1122 case FadeOutHandleItem:
1123 popup_fade_context_menu (1, event->button.time, item, item_type);
1127 popup_track_context_menu (1, event->button.time, item_type, false, where);
1131 case RegionViewNameHighlight:
1132 case RegionViewName:
1133 popup_track_context_menu (1, event->button.time, item_type, false, where);
1137 popup_track_context_menu (1, event->button.time, item_type, true, where);
1140 case AutomationTrackItem:
1141 popup_track_context_menu (1, event->button.time, item_type, false, where);
1145 case RangeMarkerBarItem:
1146 case TransportMarkerBarItem:
1147 case CdMarkerBarItem:
1150 popup_ruler_menu (where, item_type);
1154 marker_context_menu (&event->button, item);
1157 case TempoMarkerItem:
1158 tempo_or_meter_marker_context_menu (&event->button, item);
1161 case MeterMarkerItem:
1162 tempo_or_meter_marker_context_menu (&event->button, item);
1165 case CrossfadeViewItem:
1166 popup_track_context_menu (1, event->button.time, item_type, false, where);
1170 case ImageFrameItem:
1171 popup_imageframe_edit_menu(1, event->button.time, item, true) ;
1173 case ImageFrameTimeAxisItem:
1174 popup_imageframe_edit_menu(1, event->button.time, item, false) ;
1176 case MarkerViewItem:
1177 popup_marker_time_axis_edit_menu(1, event->button.time, item, true) ;
1179 case MarkerTimeAxisItem:
1180 popup_marker_time_axis_edit_menu(1, event->button.time, item, false) ;
1192 /* delete events get handled here */
1194 Editing::MouseMode const eff = effective_mouse_mode ();
1196 if (!_drags->active () && Keyboard::is_delete_event (&event->button)) {
1198 switch (item_type) {
1199 case TempoMarkerItem:
1200 remove_tempo_marker (item);
1203 case MeterMarkerItem:
1204 remove_meter_marker (item);
1208 remove_marker (*item, event);
1212 if (eff == MouseObject) {
1213 remove_clicked_region ();
1217 case ControlPointItem:
1218 if (eff == MouseGain) {
1219 remove_gain_control_point (item, event);
1221 remove_control_point (item, event);
1226 remove_midi_note (item, event);
1235 switch (event->button.button) {
1238 switch (item_type) {
1239 /* see comments in button_press_handler */
1240 case PlayheadCursorItem:
1243 case AutomationLineItem:
1244 case StartSelectionTrimItem:
1245 case EndSelectionTrimItem:
1249 if (!_dragging_playhead) {
1250 snap_to_with_modifier (where, event, 0, true);
1251 mouse_add_new_marker (where);
1255 case CdMarkerBarItem:
1256 if (!_dragging_playhead) {
1257 // if we get here then a dragged range wasn't done
1258 snap_to_with_modifier (where, event, 0, true);
1259 mouse_add_new_marker (where, true);
1264 if (!_dragging_playhead) {
1265 snap_to_with_modifier (where, event);
1266 mouse_add_new_tempo_event (where);
1271 if (!_dragging_playhead) {
1272 mouse_add_new_meter_event (pixel_to_frame (event->button.x));
1283 switch (item_type) {
1284 case AutomationTrackItem:
1285 atv = dynamic_cast<AutomationTimeAxisView*>(clicked_axisview);
1287 atv->add_automation_event (item, event, where, event->button.y);
1298 // Gain only makes sense for audio regions
1300 if (!dynamic_cast<AudioRegionView*>(clicked_regionview)) {
1304 switch (item_type) {
1306 /* check that we didn't drag before releasing, since
1307 its really annoying to create new control
1308 points when doing this.
1310 if (were_dragging) {
1311 dynamic_cast<AudioRegionView*>(clicked_regionview)->add_gain_point_event (item, event);
1316 case AutomationTrackItem:
1317 dynamic_cast<AutomationTimeAxisView*>(clicked_axisview)->
1318 add_automation_event (item, event, where, event->button.y);
1327 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1328 if (scrubbing_direction == 0) {
1329 /* no drag, just a click */
1330 switch (item_type) {
1332 play_selected_region ();
1338 /* make sure we stop */
1339 _session->request_transport_speed (0.0);
1356 switch (item_type) {
1358 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1360 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier|Keyboard::SecondaryModifier))) {
1363 // Button2 click is unused
1376 // x_style_paste (where, 1.0);
1396 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1402 if (last_item_entered != item) {
1403 last_item_entered = item;
1404 last_item_entered_n = 0;
1407 switch (item_type) {
1408 case ControlPointItem:
1409 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1410 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1411 cp->set_visible (true);
1415 at_y = cp->get_y ();
1416 cp->i2w (at_x, at_y);
1420 fraction = 1.0 - (cp->get_y() / cp->line().height());
1422 if (is_drawable() && !_drags->active ()) {
1423 track_canvas->get_window()->set_cursor (*fader_cursor);
1426 last_item_entered_n++;
1427 set_verbose_canvas_cursor (cp->line().get_verbose_cursor_string (fraction), at_x, at_y);
1428 if (last_item_entered_n < 10) {
1429 show_verbose_canvas_cursor ();
1435 if (mouse_mode == MouseGain) {
1436 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1438 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredGainLine.get();
1439 if (is_drawable()) {
1440 track_canvas->get_window()->set_cursor (*fader_cursor);
1445 case AutomationLineItem:
1446 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1448 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1450 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredAutomationLine.get();
1452 if (is_drawable()) {
1453 track_canvas->get_window()->set_cursor (*fader_cursor);
1458 case RegionViewNameHighlight:
1459 if (is_drawable() && mouse_mode == MouseObject) {
1460 track_canvas->get_window()->set_cursor (*trimmer_cursor);
1464 case StartSelectionTrimItem:
1465 case EndSelectionTrimItem:
1468 case ImageFrameHandleStartItem:
1469 case ImageFrameHandleEndItem:
1470 case MarkerViewHandleStartItem:
1471 case MarkerViewHandleEndItem:
1474 if (is_drawable()) {
1475 track_canvas->get_window()->set_cursor (*trimmer_cursor);
1479 case PlayheadCursorItem:
1480 if (is_drawable()) {
1481 switch (_edit_point) {
1483 track_canvas->get_window()->set_cursor (*grabber_edit_point_cursor);
1486 track_canvas->get_window()->set_cursor (*grabber_cursor);
1492 case RegionViewName:
1494 /* when the name is not an active item, the entire name highlight is for trimming */
1496 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1497 if (mouse_mode == MouseObject && is_drawable()) {
1498 track_canvas->get_window()->set_cursor (*trimmer_cursor);
1504 case AutomationTrackItem:
1505 if (is_drawable()) {
1506 Gdk::Cursor *cursor;
1507 switch (mouse_mode) {
1509 cursor = selector_cursor;
1512 cursor = zoom_cursor;
1515 cursor = cross_hair_cursor;
1519 track_canvas->get_window()->set_cursor (*cursor);
1521 AutomationTimeAxisView* atv;
1522 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1523 clear_entered_track = false;
1524 set_entered_track (atv);
1530 case RangeMarkerBarItem:
1531 case TransportMarkerBarItem:
1532 case CdMarkerBarItem:
1535 if (is_drawable()) {
1536 track_canvas->get_window()->set_cursor (*timebar_cursor);
1541 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1544 entered_marker = marker;
1545 marker->set_color_rgba (ARDOUR_UI::config()->canvasvar_EnteredMarker.get());
1547 case MeterMarkerItem:
1548 case TempoMarkerItem:
1549 if (is_drawable()) {
1550 track_canvas->get_window()->set_cursor (*timebar_cursor);
1553 case FadeInHandleItem:
1554 case FadeOutHandleItem:
1555 if (mouse_mode == MouseObject) {
1556 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1558 rect->property_fill_color_rgba() = 0;
1559 rect->property_outline_pixels() = 1;
1561 track_canvas->get_window()->set_cursor (*grabber_cursor);
1569 /* second pass to handle entered track status in a comprehensible way.
1572 switch (item_type) {
1574 case AutomationLineItem:
1575 case ControlPointItem:
1576 /* these do not affect the current entered track state */
1577 clear_entered_track = false;
1580 case AutomationTrackItem:
1581 /* handled above already */
1585 set_entered_track (0);
1593 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1602 switch (item_type) {
1603 case ControlPointItem:
1604 cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
1605 if (cp->line().the_list()->interpolation() != AutomationList::Discrete) {
1606 if (cp->line().npoints() > 1 && !cp->selected()) {
1607 cp->set_visible (false);
1611 if (is_drawable()) {
1612 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1615 hide_verbose_canvas_cursor ();
1618 case RegionViewNameHighlight:
1619 case StartSelectionTrimItem:
1620 case EndSelectionTrimItem:
1621 case PlayheadCursorItem:
1624 case ImageFrameHandleStartItem:
1625 case ImageFrameHandleEndItem:
1626 case MarkerViewHandleStartItem:
1627 case MarkerViewHandleEndItem:
1630 if (is_drawable()) {
1631 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1636 case AutomationLineItem:
1637 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1639 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1641 line->property_fill_color_rgba() = al->get_line_color();
1643 if (is_drawable()) {
1644 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1648 case RegionViewName:
1649 /* see enter_handler() for notes */
1650 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1651 if (is_drawable() && mouse_mode == MouseObject) {
1652 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1657 case RangeMarkerBarItem:
1658 case TransportMarkerBarItem:
1659 case CdMarkerBarItem:
1663 if (is_drawable()) {
1664 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1669 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1673 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
1674 location_flags_changed (loc, this);
1677 case MeterMarkerItem:
1678 case TempoMarkerItem:
1680 if (is_drawable()) {
1681 track_canvas->get_window()->set_cursor (*timebar_cursor);
1686 case FadeInHandleItem:
1687 case FadeOutHandleItem:
1688 rv = static_cast<RegionView*>(item->get_data ("regionview"));
1690 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1692 rect->property_fill_color_rgba() = rv->get_fill_color();
1693 rect->property_outline_pixels() = 0;
1696 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1699 case AutomationTrackItem:
1700 if (is_drawable()) {
1701 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1702 clear_entered_track = true;
1703 Glib::signal_idle().connect (sigc::mem_fun(*this, &Editor::left_automation_track));
1715 Editor::left_automation_track ()
1717 if (clear_entered_track) {
1718 set_entered_track (0);
1719 clear_entered_track = false;
1725 Editor::scrub (nframes64_t frame, double current_x)
1729 if (scrubbing_direction == 0) {
1731 _session->request_locate (frame, false);
1732 _session->request_transport_speed (0.1);
1733 scrubbing_direction = 1;
1737 if (last_scrub_x > current_x) {
1739 /* pointer moved to the left */
1741 if (scrubbing_direction > 0) {
1743 /* we reversed direction to go backwards */
1746 scrub_reverse_distance += (int) (last_scrub_x - current_x);
1750 /* still moving to the left (backwards) */
1752 scrub_reversals = 0;
1753 scrub_reverse_distance = 0;
1755 delta = 0.01 * (last_scrub_x - current_x);
1756 _session->request_transport_speed (_session->transport_speed() - delta);
1760 /* pointer moved to the right */
1762 if (scrubbing_direction < 0) {
1763 /* we reversed direction to go forward */
1766 scrub_reverse_distance += (int) (current_x - last_scrub_x);
1769 /* still moving to the right */
1771 scrub_reversals = 0;
1772 scrub_reverse_distance = 0;
1774 delta = 0.01 * (current_x - last_scrub_x);
1775 _session->request_transport_speed (_session->transport_speed() + delta);
1779 /* if there have been more than 2 opposite motion moves detected, or one that moves
1780 back more than 10 pixels, reverse direction
1783 if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
1785 if (scrubbing_direction > 0) {
1786 /* was forwards, go backwards */
1787 _session->request_transport_speed (-0.1);
1788 scrubbing_direction = -1;
1790 /* was backwards, go forwards */
1791 _session->request_transport_speed (0.1);
1792 scrubbing_direction = 1;
1795 scrub_reverse_distance = 0;
1796 scrub_reversals = 0;
1800 last_scrub_x = current_x;
1804 Editor::motion_handler (ArdourCanvas::Item* /*item*/, GdkEvent* event, bool from_autoscroll)
1806 if (event->motion.is_hint) {
1809 /* We call this so that MOTION_NOTIFY events continue to be
1810 delivered to the canvas. We need to do this because we set
1811 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
1812 the density of the events, at the expense of a round-trip
1813 to the server. Given that this will mostly occur on cases
1814 where DISPLAY = :0.0, and given the cost of what the motion
1815 event might do, its a good tradeoff.
1818 track_canvas->get_pointer (x, y);
1821 if (current_stepping_trackview) {
1822 /* don't keep the persistent stepped trackview if the mouse moves */
1823 current_stepping_trackview = 0;
1824 step_timeout.disconnect ();
1827 if (_session && _session->actively_recording()) {
1828 /* Sorry. no dragging stuff around while we record */
1832 JoinObjectRangeState const old = _join_object_range_state;
1833 update_join_object_range_location (event->motion.x, event->motion.y);
1834 if (_join_object_range_state != old) {
1835 set_canvas_cursor ();
1838 bool handled = false;
1839 if (_drags->active ()) {
1840 handled = _drags->motion_handler (event, from_autoscroll);
1847 track_canvas_motion (event);
1852 Editor::remove_gain_control_point (ArdourCanvas::Item*item, GdkEvent* /*event*/)
1854 ControlPoint* control_point;
1856 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
1857 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
1861 // We shouldn't remove the first or last gain point
1862 if (control_point->line().is_last_point(*control_point) ||
1863 control_point->line().is_first_point(*control_point)) {
1867 control_point->line().remove_point (*control_point);
1871 Editor::remove_control_point (ArdourCanvas::Item* item, GdkEvent* /*event*/)
1873 ControlPoint* control_point;
1875 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
1876 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
1880 control_point->line().remove_point (*control_point);
1884 Editor::edit_control_point (ArdourCanvas::Item* item)
1886 ControlPoint* p = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"));
1889 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
1893 ControlPointDialog d (p);
1894 d.set_position (Gtk::WIN_POS_MOUSE);
1897 if (d.run () != RESPONSE_ACCEPT) {
1901 p->line().modify_point_y (*p, d.get_y_fraction ());
1906 Editor::visible_order_range (int* low, int* high) const
1908 *low = TimeAxisView::max_order ();
1911 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
1913 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
1915 if (!rtv->hidden()) {
1917 if (*high < rtv->order()) {
1918 *high = rtv->order ();
1921 if (*low > rtv->order()) {
1922 *low = rtv->order ();
1929 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
1931 /* Either add to or set the set the region selection, unless
1932 this is an alignment click (control used)
1935 if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
1936 TimeAxisView* tv = &rv.get_time_axis_view();
1937 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
1939 if (rtv && rtv->is_track()) {
1940 speed = rtv->track()->speed();
1943 nframes64_t where = get_preferred_edit_position();
1947 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
1949 align_region (rv.region(), SyncPoint, (nframes64_t) (where * speed));
1951 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
1953 align_region (rv.region(), End, (nframes64_t) (where * speed));
1957 align_region (rv.region(), Start, (nframes64_t) (where * speed));
1964 Editor::show_verbose_time_cursor (nframes64_t frame, double offset, double xpos, double ypos)
1967 Timecode::Time timecode;
1970 nframes64_t frame_rate;
1973 if (_session == 0) {
1979 if (Profile->get_sae() || Profile->get_small_screen()) {
1980 m = ARDOUR_UI::instance()->primary_clock.mode();
1982 m = ARDOUR_UI::instance()->secondary_clock.mode();
1986 case AudioClock::BBT:
1987 _session->bbt_time (frame, bbt);
1988 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, bbt.bars, bbt.beats, bbt.ticks);
1991 case AudioClock::Timecode:
1992 _session->timecode_time (frame, timecode);
1993 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
1996 case AudioClock::MinSec:
1997 /* XXX this is copied from show_verbose_duration_cursor() */
1998 frame_rate = _session->frame_rate();
1999 hours = frame / (frame_rate * 3600);
2000 frame = frame % (frame_rate * 3600);
2001 mins = frame / (frame_rate * 60);
2002 frame = frame % (frame_rate * 60);
2003 secs = (float) frame / (float) frame_rate;
2004 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
2008 snprintf (buf, sizeof(buf), "%" PRIi64, frame);
2012 if (xpos >= 0 && ypos >=0) {
2013 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
2015 set_verbose_canvas_cursor (buf, _drags->current_pointer_x() + offset - horizontal_position(), _drags->current_pointer_y() + offset - vertical_adjustment.get_value() + canvas_timebars_vsize);
2017 show_verbose_canvas_cursor ();
2021 Editor::show_verbose_duration_cursor (nframes64_t start, nframes64_t end, double offset, double xpos, double ypos)
2024 Timecode::Time timecode;
2028 nframes64_t distance, frame_rate;
2030 Meter meter_at_start(_session->tempo_map().meter_at(start));
2032 if (_session == 0) {
2038 if (Profile->get_sae() || Profile->get_small_screen()) {
2039 m = ARDOUR_UI::instance()->primary_clock.mode ();
2041 m = ARDOUR_UI::instance()->secondary_clock.mode ();
2045 case AudioClock::BBT:
2046 _session->bbt_time (start, sbbt);
2047 _session->bbt_time (end, ebbt);
2050 /* XXX this computation won't work well if the
2051 user makes a selection that spans any meter changes.
2054 ebbt.bars -= sbbt.bars;
2055 if (ebbt.beats >= sbbt.beats) {
2056 ebbt.beats -= sbbt.beats;
2059 ebbt.beats = int(meter_at_start.beats_per_bar()) + ebbt.beats - sbbt.beats;
2061 if (ebbt.ticks >= sbbt.ticks) {
2062 ebbt.ticks -= sbbt.ticks;
2065 ebbt.ticks = int(Meter::ticks_per_beat) + ebbt.ticks - sbbt.ticks;
2068 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, ebbt.bars, ebbt.beats, ebbt.ticks);
2071 case AudioClock::Timecode:
2072 _session->timecode_duration (end - start, timecode);
2073 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
2076 case AudioClock::MinSec:
2077 /* XXX this stuff should be elsewhere.. */
2078 distance = end - start;
2079 frame_rate = _session->frame_rate();
2080 hours = distance / (frame_rate * 3600);
2081 distance = distance % (frame_rate * 3600);
2082 mins = distance / (frame_rate * 60);
2083 distance = distance % (frame_rate * 60);
2084 secs = (float) distance / (float) frame_rate;
2085 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
2089 snprintf (buf, sizeof(buf), "%" PRIi64, end - start);
2093 if (xpos >= 0 && ypos >=0) {
2094 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
2097 set_verbose_canvas_cursor (buf, _drags->current_pointer_x() + offset, _drags->current_pointer_y() + offset);
2100 show_verbose_canvas_cursor ();
2104 Editor::collect_new_region_view (RegionView* rv)
2106 latest_regionviews.push_back (rv);
2110 Editor::collect_and_select_new_region_view (RegionView* rv)
2113 latest_regionviews.push_back (rv);
2117 Editor::cancel_selection ()
2119 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2120 (*i)->hide_selection ();
2123 selection->clear ();
2124 clicked_selection = 0;
2129 Editor::single_contents_trim (RegionView& rv, nframes64_t frame_delta, bool left_direction, bool swap_direction)
2131 boost::shared_ptr<Region> region (rv.region());
2133 if (region->locked()) {
2137 nframes64_t new_bound;
2140 TimeAxisView* tvp = clicked_axisview;
2141 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2143 if (tv && tv->is_track()) {
2144 speed = tv->track()->speed();
2147 if (left_direction) {
2148 if (swap_direction) {
2149 new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
2151 new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
2154 if (swap_direction) {
2155 new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
2157 new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
2161 region->trim_start ((nframes64_t) (new_bound * speed), this);
2162 rv.region_changed (PropertyChange (ARDOUR::Properties::start));
2166 Editor::single_start_trim (RegionView& rv, nframes64_t new_bound, bool no_overlap)
2168 boost::shared_ptr<Region> region (rv.region());
2170 if (region->locked()) {
2175 TimeAxisView* tvp = clicked_axisview;
2176 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2178 if (tv && tv->is_track()) {
2179 speed = tv->track()->speed();
2182 nframes64_t pre_trim_first_frame = region->first_frame();
2184 region->trim_front ((nframes64_t) (new_bound * speed), this);
2187 //Get the next region on the left of this region and shrink/expand it.
2188 boost::shared_ptr<Playlist> playlist (region->playlist());
2189 boost::shared_ptr<Region> region_left = playlist->find_next_region (pre_trim_first_frame, End, 0);
2191 bool regions_touching = false;
2193 if (region_left != 0 && (pre_trim_first_frame == region_left->last_frame() + 1)){
2194 regions_touching = true;
2197 //Only trim region on the left if the first frame has gone beyond the left region's last frame.
2198 if (region_left != 0 &&
2199 (region_left->last_frame() > region->first_frame() || regions_touching))
2201 region_left->trim_end(region->first_frame() - 1, this);
2205 rv.region_changed (ARDOUR::bounds_change);
2209 Editor::single_end_trim (RegionView& rv, nframes64_t new_bound, bool no_overlap)
2211 boost::shared_ptr<Region> region (rv.region());
2213 if (region->locked()) {
2218 TimeAxisView* tvp = clicked_axisview;
2219 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2221 if (tv && tv->is_track()) {
2222 speed = tv->track()->speed();
2225 nframes64_t pre_trim_last_frame = region->last_frame();
2227 region->trim_end ((nframes64_t) (new_bound * speed), this);
2230 //Get the next region on the right of this region and shrink/expand it.
2231 boost::shared_ptr<Playlist> playlist (region->playlist());
2232 boost::shared_ptr<Region> region_right = playlist->find_next_region (pre_trim_last_frame, Start, 1);
2234 bool regions_touching = false;
2236 if (region_right != 0 && (pre_trim_last_frame == region_right->first_frame() - 1)) {
2237 regions_touching = true;
2240 //Only trim region on the right if the last frame has gone beyond the right region's first frame.
2241 if (region_right != 0 &&
2242 (region_right->first_frame() < region->last_frame() || regions_touching))
2244 region_right->trim_front(region->last_frame() + 1, this);
2247 rv.region_changed (ARDOUR::bounds_change);
2250 rv.region_changed (PropertyChange (ARDOUR::Properties::length));
2256 Editor::point_trim (GdkEvent* event, nframes64_t new_bound)
2258 RegionView* rv = clicked_regionview;
2260 /* Choose action dependant on which button was pressed */
2261 switch (event->button.button) {
2263 begin_reversible_command (_("Start point trim"));
2265 if (selection->selected (rv)) {
2266 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
2267 i != selection->regions.by_layer().end(); ++i)
2270 cerr << "region view contains null region" << endl;
2273 if (!(*i)->region()->locked()) {
2274 (*i)->region()->clear_history ();
2275 (*i)->region()->trim_front (new_bound, this);
2276 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2281 if (!rv->region()->locked()) {
2282 rv->region()->clear_history ();
2283 rv->region()->trim_front (new_bound, this);
2284 _session->add_command(new StatefulDiffCommand (rv->region()));
2288 commit_reversible_command();
2292 begin_reversible_command (_("End point trim"));
2294 if (selection->selected (rv)) {
2296 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
2298 if (!(*i)->region()->locked()) {
2299 (*i)->region()->clear_history();
2300 (*i)->region()->trim_end (new_bound, this);
2301 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2307 if (!rv->region()->locked()) {
2308 rv->region()->clear_history ();
2309 rv->region()->trim_end (new_bound, this);
2310 _session->add_command (new StatefulDiffCommand (rv->region()));
2314 commit_reversible_command();
2323 Editor::thaw_region_after_trim (RegionView& rv)
2325 boost::shared_ptr<Region> region (rv.region());
2327 if (region->locked()) {
2331 region->resume_property_changes ();
2333 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(&rv);
2336 arv->unhide_envelope ();
2341 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* /*event*/)
2346 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2347 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2351 Location* location = find_location_from_marker (marker, is_start);
2352 location->set_hidden (true, this);
2357 Editor::reposition_zoom_rect (nframes64_t start, nframes64_t end)
2359 double x1 = frame_to_pixel (start);
2360 double x2 = frame_to_pixel (end);
2361 double y2 = full_canvas_height - 1.0;
2363 zoom_rect->property_x1() = x1;
2364 zoom_rect->property_y1() = 1.0;
2365 zoom_rect->property_x2() = x2;
2366 zoom_rect->property_y2() = y2;
2371 Editor::mouse_rename_region (ArdourCanvas::Item* /*item*/, GdkEvent* /*event*/)
2373 using namespace Gtkmm2ext;
2375 ArdourPrompter prompter (false);
2377 prompter.set_prompt (_("Name for region:"));
2378 prompter.set_initial_text (clicked_regionview->region()->name());
2379 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
2380 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
2381 prompter.show_all ();
2382 switch (prompter.run ()) {
2383 case Gtk::RESPONSE_ACCEPT:
2385 prompter.get_result(str);
2387 clicked_regionview->region()->set_name (str);
2396 Editor::mouse_brush_insert_region (RegionView* rv, nframes64_t pos)
2398 /* no brushing without a useful snap setting */
2400 switch (_snap_mode) {
2402 return; /* can't work because it allows region to be placed anywhere */
2407 switch (_snap_type) {
2415 /* don't brush a copy over the original */
2417 if (pos == rv->region()->position()) {
2421 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
2423 if (rtv == 0 || !rtv->is_track()) {
2427 boost::shared_ptr<Playlist> playlist = rtv->playlist();
2428 double speed = rtv->track()->speed();
2430 playlist->clear_history ();
2431 boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region()));
2432 playlist->add_region (new_region, (nframes64_t) (pos * speed));
2433 _session->add_command (new StatefulDiffCommand (playlist));
2435 // playlist is frozen, so we have to update manually XXX this is disgusting
2437 playlist->RegionAdded (new_region); /* EMIT SIGNAL */
2441 Editor::track_height_step_timeout ()
2443 if (get_microseconds() - last_track_height_step_timestamp < 250000) {
2444 current_stepping_trackview = 0;
2451 Editor::add_region_drag (ArdourCanvas::Item* item, GdkEvent* event, RegionView* region_view)
2453 assert (region_view);
2455 _region_motion_group->raise_to_top ();
2457 if (Config->get_edit_mode() == Splice) {
2458 _drags->add (new RegionSpliceDrag (this, item, region_view, selection->regions.by_layer()));
2460 RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id);
2461 _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), false, false));
2464 /* sync the canvas to what we think is its current state */
2465 update_canvas_now();
2469 Editor::add_region_copy_drag (ArdourCanvas::Item* item, GdkEvent* event, RegionView* region_view)
2471 assert (region_view);
2473 _region_motion_group->raise_to_top ();
2475 RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id);
2476 _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), false, true));
2480 Editor::add_region_brush_drag (ArdourCanvas::Item* item, GdkEvent* event, RegionView* region_view)
2482 assert (region_view);
2484 if (Config->get_edit_mode() == Splice) {
2488 RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id);
2489 _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), true, false));
2491 begin_reversible_command (_("Drag region brush"));
2494 /** Start a grab where a time range is selected, track(s) are selected, and the
2495 * user clicks and drags a region with a modifier in order to create a new region containing
2496 * the section of the clicked region that lies within the time range.
2499 Editor::start_selection_grab (ArdourCanvas::Item* /*item*/, GdkEvent* event)
2501 if (clicked_regionview == 0) {
2505 /* lets try to create new Region for the selection */
2507 vector<boost::shared_ptr<Region> > new_regions;
2508 create_region_from_selection (new_regions);
2510 if (new_regions.empty()) {
2514 /* XXX fix me one day to use all new regions */
2516 boost::shared_ptr<Region> region (new_regions.front());
2518 /* add it to the current stream/playlist.
2520 tricky: the streamview for the track will add a new regionview. we will
2521 catch the signal it sends when it creates the regionview to
2522 set the regionview we want to then drag.
2525 latest_regionviews.clear();
2526 sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
2528 /* A selection grab currently creates two undo/redo operations, one for
2529 creating the new region and another for moving it.
2532 begin_reversible_command (_("selection grab"));
2534 boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
2536 playlist->clear_history ();
2537 clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
2538 _session->add_command(new StatefulDiffCommand (playlist));
2540 commit_reversible_command ();
2544 if (latest_regionviews.empty()) {
2545 /* something went wrong */
2549 /* we need to deselect all other regionviews, and select this one
2550 i'm ignoring undo stuff, because the region creation will take care of it
2552 selection->set (latest_regionviews);
2554 _drags->set (new RegionMoveDrag (this, latest_regionviews.front()->get_canvas_group(), latest_regionviews.front(), latest_regionviews, false, false), event);
2560 if (_drags->active ()) {
2563 selection->clear ();
2568 Editor::set_internal_edit (bool yn)
2570 _internal_editing = yn;
2573 mouse_select_button.set_image (*(manage (new Image (::get_icon("midi_tool_pencil")))));
2574 mouse_select_button.get_image ()->show ();
2575 ARDOUR_UI::instance()->tooltips().set_tip (mouse_select_button, _("Draw/Edit MIDI Notes"));
2577 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2578 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (*i);
2580 mtv->start_step_editing ();
2584 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
2585 (*i)->hide_selection ();
2588 start_step_editing ();
2589 set_canvas_cursor ();
2593 mouse_select_button.set_image (*(manage (new Image (::get_icon("tool_range")))));
2594 mouse_select_button.get_image ()->show ();
2595 ARDOUR_UI::instance()->tooltips().set_tip (mouse_select_button, _("Select/Move Ranges"));
2596 stop_step_editing ();
2598 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2599 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (*i);
2601 mtv->stop_step_editing ();
2605 mouse_mode_toggled (mouse_mode);
2609 /** Update _join_object_range_state which indicate whether we are over the top or bottom half of a region view,
2610 * used by the `join object/range' tool mode.
2613 Editor::update_join_object_range_location (double x, double y)
2615 /* XXX: actually, this decides based on whether the mouse is in the top or bottom half of a RouteTimeAxisView;
2616 entered_{track,regionview} is not always setup (e.g. if the mouse is over a TimeSelection), and to get a Region
2617 that we're over requires searching the playlist.
2620 if (join_object_range_button.get_active() == false || (mouse_mode != MouseRange && mouse_mode != MouseObject)) {
2621 _join_object_range_state = JOIN_OBJECT_RANGE_NONE;
2625 if (mouse_mode == MouseObject) {
2626 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2627 } else if (mouse_mode == MouseRange) {
2628 _join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
2631 /* XXX: maybe we should make entered_track work in all cases, rather than resorting to this */
2632 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y + vertical_adjustment.get_value() - canvas_timebars_vsize);
2636 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
2641 rtv->canvas_display()->w2i (cx, cy);
2643 bool const top_half = cy < rtv->current_height () / 2;
2645 _join_object_range_state = top_half ? JOIN_OBJECT_RANGE_RANGE : JOIN_OBJECT_RANGE_OBJECT;
2651 Editor::effective_mouse_mode () const
2653 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
2655 } else if (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE) {
2663 Editor::remove_midi_note (ArdourCanvas::Item* item, GdkEvent *)
2665 ArdourCanvas::CanvasNoteEvent* e = dynamic_cast<ArdourCanvas::CanvasNoteEvent*> (item);
2668 e->region_view().delete_note (e->note ());