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 (mouse_mode != MouseRange && _join_object_range_state == JOIN_OBJECT_RANGE_NONE) {
330 /* in all modes except range and joined object/range, hide the range selection,
331 show the object (region) selection.
334 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
335 (*i)->set_should_show_selection (true);
337 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
338 (*i)->hide_selection ();
344 in range or object/range mode, show the range selection.
347 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
348 (*i)->show_selection (selection->time);
352 set_canvas_cursor ();
356 Editor::step_mouse_mode (bool next)
358 switch (current_mouse_mode()) {
361 if (Profile->get_sae()) {
362 set_mouse_mode (MouseZoom);
364 set_mouse_mode (MouseRange);
367 set_mouse_mode (MouseTimeFX);
372 if (next) set_mouse_mode (MouseZoom);
373 else set_mouse_mode (MouseObject);
378 if (Profile->get_sae()) {
379 set_mouse_mode (MouseTimeFX);
381 set_mouse_mode (MouseGain);
384 if (Profile->get_sae()) {
385 set_mouse_mode (MouseObject);
387 set_mouse_mode (MouseRange);
393 if (next) set_mouse_mode (MouseTimeFX);
394 else set_mouse_mode (MouseZoom);
399 set_mouse_mode (MouseAudition);
401 if (Profile->get_sae()) {
402 set_mouse_mode (MouseZoom);
404 set_mouse_mode (MouseGain);
410 if (next) set_mouse_mode (MouseObject);
411 else set_mouse_mode (MouseTimeFX);
417 Editor::button_selection (ArdourCanvas::Item* /*item*/, GdkEvent* event, ItemType item_type)
419 /* in object/audition/timefx/gain-automation mode,
420 any button press sets the selection if the object
421 can be selected. this is a bit of hack, because
422 we want to avoid this if the mouse operation is a
425 note: not dbl-click or triple-click
428 if (((mouse_mode != MouseObject) &&
429 (_join_object_range_state != JOIN_OBJECT_RANGE_OBJECT) &&
430 (mouse_mode != MouseAudition || item_type != RegionItem) &&
431 (mouse_mode != MouseTimeFX || item_type != RegionItem) &&
432 (mouse_mode != MouseGain) &&
433 (mouse_mode != MouseRange)) ||
435 ((event->type != GDK_BUTTON_PRESS && event->type != GDK_BUTTON_RELEASE) || event->button.button > 3)) {
440 if (event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) {
442 if ((event->button.state & Keyboard::RelevantModifierKeyMask) && event->button.button != 1) {
444 /* almost no selection action on modified button-2 or button-3 events */
446 if (item_type != RegionItem && event->button.button != 2) {
452 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
453 bool press = (event->type == GDK_BUTTON_PRESS);
455 // begin_reversible_command (_("select on click"));
459 if (mouse_mode != MouseRange || _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
460 set_selected_regionview_from_click (press, op, true);
461 } else if (event->type == GDK_BUTTON_PRESS) {
462 selection->clear_tracks ();
463 set_selected_track_as_side_effect (true);
465 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT && !selection->regions.empty()) {
466 clicked_selection = select_range_around_region (selection->regions.front());
471 case RegionViewNameHighlight:
473 if (mouse_mode != MouseRange || _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
474 set_selected_regionview_from_click (press, op, true);
475 } else if (event->type == GDK_BUTTON_PRESS) {
476 set_selected_track_as_side_effect ();
481 case FadeInHandleItem:
483 case FadeOutHandleItem:
485 if (mouse_mode != MouseRange || _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
486 set_selected_regionview_from_click (press, op, true);
487 } else if (event->type == GDK_BUTTON_PRESS) {
488 set_selected_track_as_side_effect ();
492 case ControlPointItem:
493 set_selected_track_as_side_effect ();
494 if (mouse_mode != MouseRange || _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
495 set_selected_control_point_from_click (op, false);
500 /* for context click, select track */
501 if (event->button.button == 3) {
502 selection->clear_tracks ();
503 set_selected_track_as_side_effect (true);
507 case AutomationTrackItem:
508 set_selected_track_as_side_effect (true);
517 Editor::button_press_handler_1 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
519 if (_drags->active ()) {
523 /* single mouse clicks on any of these item types operate
524 independent of mouse mode, mostly because they are
525 not on the main track canvas or because we want
530 case PlayheadCursorItem:
531 _drags->set (new CursorDrag (this, item, true), event);
535 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
536 hide_marker (item, event);
538 _drags->set (new MarkerDrag (this, item), event);
542 case TempoMarkerItem:
544 new TempoMarkerDrag (
547 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
553 case MeterMarkerItem:
555 new MeterMarkerDrag (
558 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
567 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
568 _drags->set (new CursorDrag (this, &playhead_cursor->canvas_item, false), event);
574 case RangeMarkerBarItem:
575 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
576 _drags->set (new CursorDrag (this, &playhead_cursor->canvas_item, false), event);
578 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateRangeMarker), event);
583 case CdMarkerBarItem:
584 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
585 _drags->set (new CursorDrag (this, &playhead_cursor->canvas_item, false), event);
587 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateCDMarker), event);
592 case TransportMarkerBarItem:
593 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
594 _drags->set (new CursorDrag (this, &playhead_cursor->canvas_item, false), event);
596 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateTransportMarker), event);
605 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
606 /* special case: allow trim of range selections in joined object mode;
607 in theory eff should equal MouseRange in this case, but it doesn't
608 because entering the range selection canvas item results in entered_regionview
609 being set to 0, so update_join_object_range_location acts as if we aren't
612 if (item_type == StartSelectionTrimItem) {
613 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
614 } else if (item_type == EndSelectionTrimItem) {
615 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
619 Editing::MouseMode eff = effective_mouse_mode ();
621 /* special case: allow drag of region fade in/out in object mode with join object/range enabled */
622 if (item_type == FadeInHandleItem || item_type == FadeOutHandleItem) {
629 case StartSelectionTrimItem:
630 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
633 case EndSelectionTrimItem:
634 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
638 if (Keyboard::modifier_state_contains
639 (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier))) {
640 // contains and not equals because I can't use alt as a modifier alone.
641 start_selection_grab (item, event);
642 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
643 /* grab selection for moving */
644 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionMove), event);
646 double const y = event->button.y + vertical_adjustment.get_value() - canvas_timebars_vsize;
647 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
649 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
650 if (join_object_range_button.get_active() && atv) {
651 /* smart "join" mode: drag automation */
652 _drags->set (new AutomationRangeDrag (this, atv->base_item(), selection->time), event);
654 /* this was debated, but decided the more common action was to
655 make a new selection */
656 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
663 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
671 if (internal_editing()) {
672 /* Note: we don't get here if not in internal_editing() mode */
673 _drags->set (new NoteDrag (this, item), event);
682 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)) &&
683 event->type == GDK_BUTTON_PRESS) {
685 _drags->set (new RubberbandSelectDrag (this, item), event);
687 } else if (event->type == GDK_BUTTON_PRESS) {
690 case FadeInHandleItem:
692 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
693 _drags->set (new FadeInDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), s), event);
697 case FadeOutHandleItem:
699 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
700 _drags->set (new FadeOutDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), s), event);
705 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
706 add_region_copy_drag (item, event, clicked_regionview);
707 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
708 add_region_brush_drag (item, event, clicked_regionview);
710 add_region_drag (item, event, clicked_regionview);
713 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT && !selection->regions.empty()) {
714 _drags->add (new SelectionDrag (this, clicked_axisview->get_selection_rect (clicked_selection)->rect, SelectionDrag::SelectionMove));
717 _drags->start_grab (event);
720 case RegionViewNameHighlight:
722 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
723 _drags->set (new TrimDrag (this, item, clicked_regionview, s.by_layer()), event);
730 /* rename happens on edit clicks */
731 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
732 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, s.by_layer()), event);
737 case ControlPointItem:
738 _drags->set (new ControlPointDrag (this, item), event);
742 case AutomationLineItem:
743 _drags->set (new LineDrag (this, item), event);
748 if (internal_editing()) {
749 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
752 _drags->set (new RubberbandSelectDrag (this, item), event);
756 case AutomationTrackItem:
757 /* rubberband drag to select automation points */
758 _drags->set (new RubberbandSelectDrag (this, item), event);
763 if (join_object_range_button.get_active()) {
764 /* we're in "smart" joined mode, and we've clicked on a Selection */
765 double const y = event->button.y + vertical_adjustment.get_value() - canvas_timebars_vsize;
766 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
768 /* if we're over an automation track, start a drag of its data */
769 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
771 _drags->set (new AutomationRangeDrag (this, atv->base_item(), selection->time), event);
774 /* if we're over a track and a region, and in the `object' part of a region,
775 put a selection around the region and drag both
777 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
778 if (rtv && _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
779 boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track> (rtv->route ());
781 boost::shared_ptr<Playlist> pl = t->playlist ();
784 boost::shared_ptr<Region> r = pl->top_region_at (event_frame (event));
786 RegionView* rv = rtv->view()->find_view (r);
787 clicked_selection = select_range_around_region (rv);
788 _drags->add (new SelectionDrag (this, item, SelectionDrag::SelectionMove));
789 list<RegionView*> rvs;
791 _drags->add (new RegionMoveDrag (this, item, rv, rvs, false, false));
792 _drags->start_grab (event);
803 case ImageFrameHandleStartItem:
804 imageframe_start_handle_op(item, event) ;
807 case ImageFrameHandleEndItem:
808 imageframe_end_handle_op(item, event) ;
811 case MarkerViewHandleStartItem:
812 markerview_item_start_handle_op(item, event) ;
815 case MarkerViewHandleEndItem:
816 markerview_item_end_handle_op(item, event) ;
820 start_markerview_grab(item, event) ;
823 start_imageframe_grab(item, event) ;
841 /* start a grab so that if we finish after moving
842 we can tell what happened.
844 _drags->set (new RegionGainDrag (this, item), event, current_canvas_cursor);
848 _drags->set (new LineDrag (this, item), event);
851 case ControlPointItem:
852 _drags->set (new ControlPointDrag (this, item), event);
863 case ControlPointItem:
864 _drags->set (new ControlPointDrag (this, item), event);
867 case AutomationLineItem:
868 _drags->set (new LineDrag (this, item), event);
872 // XXX need automation mode to identify which
874 // start_line_grab_from_regionview (item, event);
884 if (event->type == GDK_BUTTON_PRESS) {
885 _drags->set (new MouseZoomDrag (this, item), event);
892 if (internal_editing() && item_type == NoteItem) {
893 /* drag notes if we're in internal edit mode */
894 _drags->set (new NoteResizeDrag (this, item), event);
896 } else if (!internal_editing() || dynamic_cast<AudioRegionView*> (clicked_regionview)) {
897 /* do time-FX if we're not in internal edit mode, or we are but we clicked on an audio region */
898 _drags->set (new TimeFXDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
904 _drags->set (new ScrubDrag (this, item), event);
906 scrub_reverse_distance = 0;
907 last_scrub_x = event->button.x;
908 scrubbing_direction = 0;
909 track_canvas->get_window()->set_cursor (*transparent_cursor);
921 Editor::button_press_handler_2 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
923 Editing::MouseMode const eff = effective_mouse_mode ();
928 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
929 add_region_copy_drag (item, event, clicked_regionview);
931 add_region_drag (item, event, clicked_regionview);
933 _drags->start_grab (event);
936 case ControlPointItem:
937 _drags->set (new ControlPointDrag (this, item), event);
946 case RegionViewNameHighlight:
947 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
952 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer()), event);
963 /* relax till release */
969 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
970 temporal_zoom_session();
972 temporal_zoom_to_frame (true, event_frame(event));
985 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
987 if (event->type != GDK_BUTTON_PRESS) {
991 Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->track_canvas->get_window();
994 Glib::RefPtr<const Gdk::Window> pointer_window;
997 Gdk::ModifierType mask;
999 pointer_window = canvas_window->get_pointer (x, y, mask);
1001 if (pointer_window == track_canvas->get_bin_window()) {
1002 track_canvas->window_to_world (x, y, wx, wy);
1003 allow_vertical_scroll = true;
1005 allow_vertical_scroll = false;
1009 track_canvas->grab_focus();
1011 if (_session && _session->actively_recording()) {
1015 button_selection (item, event, item_type);
1017 if (!_drags->active () &&
1018 (Keyboard::is_delete_event (&event->button) ||
1019 Keyboard::is_context_menu_event (&event->button) ||
1020 Keyboard::is_edit_event (&event->button))) {
1022 /* handled by button release */
1026 switch (event->button.button) {
1028 return button_press_handler_1 (item, event, item_type);
1032 return button_press_handler_2 (item, event, item_type);
1047 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1049 nframes64_t where = event_frame (event, 0, 0);
1050 AutomationTimeAxisView* atv = 0;
1052 /* no action if we're recording */
1054 if (_session && _session->actively_recording()) {
1058 /* first, see if we're finishing a drag ... */
1060 bool were_dragging = false;
1061 if (_drags->active ()) {
1062 bool const r = _drags->end_grab (event);
1064 /* grab dragged, so do nothing else */
1068 were_dragging = true;
1071 button_selection (item, event, item_type);
1073 /* edit events get handled here */
1075 if (!_drags->active () && Keyboard::is_edit_event (&event->button)) {
1076 switch (item_type) {
1081 case TempoMarkerItem:
1082 edit_tempo_marker (item);
1085 case MeterMarkerItem:
1086 edit_meter_marker (item);
1089 case RegionViewName:
1090 if (clicked_regionview->name_active()) {
1091 return mouse_rename_region (item, event);
1095 case ControlPointItem:
1096 edit_control_point (item);
1105 /* context menu events get handled here */
1107 if (Keyboard::is_context_menu_event (&event->button)) {
1109 if (!_drags->active ()) {
1111 /* no matter which button pops up the context menu, tell the menu
1112 widget to use button 1 to drive menu selection.
1115 switch (item_type) {
1117 case FadeInHandleItem:
1119 case FadeOutHandleItem:
1120 popup_fade_context_menu (1, event->button.time, item, item_type);
1124 popup_track_context_menu (1, event->button.time, item_type, false, where);
1128 case RegionViewNameHighlight:
1129 case RegionViewName:
1130 popup_track_context_menu (1, event->button.time, item_type, false, where);
1134 popup_track_context_menu (1, event->button.time, item_type, true, where);
1137 case AutomationTrackItem:
1138 popup_track_context_menu (1, event->button.time, item_type, false, where);
1142 case RangeMarkerBarItem:
1143 case TransportMarkerBarItem:
1144 case CdMarkerBarItem:
1147 popup_ruler_menu (where, item_type);
1151 marker_context_menu (&event->button, item);
1154 case TempoMarkerItem:
1155 tempo_or_meter_marker_context_menu (&event->button, item);
1158 case MeterMarkerItem:
1159 tempo_or_meter_marker_context_menu (&event->button, item);
1162 case CrossfadeViewItem:
1163 popup_track_context_menu (1, event->button.time, item_type, false, where);
1167 case ImageFrameItem:
1168 popup_imageframe_edit_menu(1, event->button.time, item, true) ;
1170 case ImageFrameTimeAxisItem:
1171 popup_imageframe_edit_menu(1, event->button.time, item, false) ;
1173 case MarkerViewItem:
1174 popup_marker_time_axis_edit_menu(1, event->button.time, item, true) ;
1176 case MarkerTimeAxisItem:
1177 popup_marker_time_axis_edit_menu(1, event->button.time, item, false) ;
1189 /* delete events get handled here */
1191 Editing::MouseMode const eff = effective_mouse_mode ();
1193 if (!_drags->active () && Keyboard::is_delete_event (&event->button)) {
1195 switch (item_type) {
1196 case TempoMarkerItem:
1197 remove_tempo_marker (item);
1200 case MeterMarkerItem:
1201 remove_meter_marker (item);
1205 remove_marker (*item, event);
1209 if (eff == MouseObject) {
1210 remove_clicked_region ();
1214 case ControlPointItem:
1215 if (eff == MouseGain) {
1216 remove_gain_control_point (item, event);
1218 remove_control_point (item, event);
1228 switch (event->button.button) {
1231 switch (item_type) {
1232 /* see comments in button_press_handler */
1233 case PlayheadCursorItem:
1236 case AutomationLineItem:
1237 case StartSelectionTrimItem:
1238 case EndSelectionTrimItem:
1242 if (!_dragging_playhead) {
1243 snap_to_with_modifier (where, event, 0, true);
1244 mouse_add_new_marker (where);
1248 case CdMarkerBarItem:
1249 if (!_dragging_playhead) {
1250 // if we get here then a dragged range wasn't done
1251 snap_to_with_modifier (where, event, 0, true);
1252 mouse_add_new_marker (where, true);
1257 if (!_dragging_playhead) {
1258 snap_to_with_modifier (where, event);
1259 mouse_add_new_tempo_event (where);
1264 if (!_dragging_playhead) {
1265 mouse_add_new_meter_event (pixel_to_frame (event->button.x));
1276 switch (item_type) {
1277 case AutomationTrackItem:
1278 atv = dynamic_cast<AutomationTimeAxisView*>(clicked_axisview);
1280 atv->add_automation_event (item, event, where, event->button.y);
1291 // Gain only makes sense for audio regions
1293 if (!dynamic_cast<AudioRegionView*>(clicked_regionview)) {
1297 switch (item_type) {
1299 /* check that we didn't drag before releasing, since
1300 its really annoying to create new control
1301 points when doing this.
1303 if (were_dragging) {
1304 dynamic_cast<AudioRegionView*>(clicked_regionview)->add_gain_point_event (item, event);
1309 case AutomationTrackItem:
1310 dynamic_cast<AutomationTimeAxisView*>(clicked_axisview)->
1311 add_automation_event (item, event, where, event->button.y);
1320 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1321 if (scrubbing_direction == 0) {
1322 /* no drag, just a click */
1323 switch (item_type) {
1325 play_selected_region ();
1331 /* make sure we stop */
1332 _session->request_transport_speed (0.0);
1349 switch (item_type) {
1351 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1353 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier|Keyboard::SecondaryModifier))) {
1356 // Button2 click is unused
1369 // x_style_paste (where, 1.0);
1389 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1395 if (last_item_entered != item) {
1396 last_item_entered = item;
1397 last_item_entered_n = 0;
1400 switch (item_type) {
1401 case ControlPointItem:
1402 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1403 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1404 cp->set_visible (true);
1408 at_y = cp->get_y ();
1409 cp->i2w (at_x, at_y);
1413 fraction = 1.0 - (cp->get_y() / cp->line().height());
1415 if (is_drawable() && !_drags->active ()) {
1416 track_canvas->get_window()->set_cursor (*fader_cursor);
1419 last_item_entered_n++;
1420 set_verbose_canvas_cursor (cp->line().get_verbose_cursor_string (fraction), at_x, at_y);
1421 if (last_item_entered_n < 10) {
1422 show_verbose_canvas_cursor ();
1428 if (mouse_mode == MouseGain) {
1429 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1431 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredGainLine.get();
1432 if (is_drawable()) {
1433 track_canvas->get_window()->set_cursor (*fader_cursor);
1438 case AutomationLineItem:
1439 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1441 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1443 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredAutomationLine.get();
1445 if (is_drawable()) {
1446 track_canvas->get_window()->set_cursor (*fader_cursor);
1451 case RegionViewNameHighlight:
1452 if (is_drawable() && mouse_mode == MouseObject) {
1453 track_canvas->get_window()->set_cursor (*trimmer_cursor);
1457 case StartSelectionTrimItem:
1458 case EndSelectionTrimItem:
1461 case ImageFrameHandleStartItem:
1462 case ImageFrameHandleEndItem:
1463 case MarkerViewHandleStartItem:
1464 case MarkerViewHandleEndItem:
1467 if (is_drawable()) {
1468 track_canvas->get_window()->set_cursor (*trimmer_cursor);
1472 case PlayheadCursorItem:
1473 if (is_drawable()) {
1474 switch (_edit_point) {
1476 track_canvas->get_window()->set_cursor (*grabber_edit_point_cursor);
1479 track_canvas->get_window()->set_cursor (*grabber_cursor);
1485 case RegionViewName:
1487 /* when the name is not an active item, the entire name highlight is for trimming */
1489 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1490 if (mouse_mode == MouseObject && is_drawable()) {
1491 track_canvas->get_window()->set_cursor (*trimmer_cursor);
1497 case AutomationTrackItem:
1498 if (is_drawable()) {
1499 Gdk::Cursor *cursor;
1500 switch (mouse_mode) {
1502 cursor = selector_cursor;
1505 cursor = zoom_cursor;
1508 cursor = cross_hair_cursor;
1512 track_canvas->get_window()->set_cursor (*cursor);
1514 AutomationTimeAxisView* atv;
1515 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1516 clear_entered_track = false;
1517 set_entered_track (atv);
1523 case RangeMarkerBarItem:
1524 case TransportMarkerBarItem:
1525 case CdMarkerBarItem:
1528 if (is_drawable()) {
1529 track_canvas->get_window()->set_cursor (*timebar_cursor);
1534 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1537 entered_marker = marker;
1538 marker->set_color_rgba (ARDOUR_UI::config()->canvasvar_EnteredMarker.get());
1540 case MeterMarkerItem:
1541 case TempoMarkerItem:
1542 if (is_drawable()) {
1543 track_canvas->get_window()->set_cursor (*timebar_cursor);
1546 case FadeInHandleItem:
1547 case FadeOutHandleItem:
1548 if (mouse_mode == MouseObject) {
1549 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1551 rect->property_fill_color_rgba() = 0;
1552 rect->property_outline_pixels() = 1;
1554 track_canvas->get_window()->set_cursor (*grabber_cursor);
1562 /* second pass to handle entered track status in a comprehensible way.
1565 switch (item_type) {
1567 case AutomationLineItem:
1568 case ControlPointItem:
1569 /* these do not affect the current entered track state */
1570 clear_entered_track = false;
1573 case AutomationTrackItem:
1574 /* handled above already */
1578 set_entered_track (0);
1586 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1595 switch (item_type) {
1596 case ControlPointItem:
1597 cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
1598 if (cp->line().the_list()->interpolation() != AutomationList::Discrete) {
1599 if (cp->line().npoints() > 1 && !cp->selected()) {
1600 cp->set_visible (false);
1604 if (is_drawable()) {
1605 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1608 hide_verbose_canvas_cursor ();
1611 case RegionViewNameHighlight:
1612 case StartSelectionTrimItem:
1613 case EndSelectionTrimItem:
1614 case PlayheadCursorItem:
1617 case ImageFrameHandleStartItem:
1618 case ImageFrameHandleEndItem:
1619 case MarkerViewHandleStartItem:
1620 case MarkerViewHandleEndItem:
1623 if (is_drawable()) {
1624 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1629 case AutomationLineItem:
1630 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1632 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1634 line->property_fill_color_rgba() = al->get_line_color();
1636 if (is_drawable()) {
1637 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1641 case RegionViewName:
1642 /* see enter_handler() for notes */
1643 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1644 if (is_drawable() && mouse_mode == MouseObject) {
1645 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1650 case RangeMarkerBarItem:
1651 case TransportMarkerBarItem:
1652 case CdMarkerBarItem:
1656 if (is_drawable()) {
1657 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1662 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1666 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
1667 location_flags_changed (loc, this);
1670 case MeterMarkerItem:
1671 case TempoMarkerItem:
1673 if (is_drawable()) {
1674 track_canvas->get_window()->set_cursor (*timebar_cursor);
1679 case FadeInHandleItem:
1680 case FadeOutHandleItem:
1681 rv = static_cast<RegionView*>(item->get_data ("regionview"));
1683 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1685 rect->property_fill_color_rgba() = rv->get_fill_color();
1686 rect->property_outline_pixels() = 0;
1689 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1692 case AutomationTrackItem:
1693 if (is_drawable()) {
1694 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1695 clear_entered_track = true;
1696 Glib::signal_idle().connect (sigc::mem_fun(*this, &Editor::left_automation_track));
1708 Editor::left_automation_track ()
1710 if (clear_entered_track) {
1711 set_entered_track (0);
1712 clear_entered_track = false;
1718 Editor::scrub (nframes64_t frame, double current_x)
1722 if (scrubbing_direction == 0) {
1724 _session->request_locate (frame, false);
1725 _session->request_transport_speed (0.1);
1726 scrubbing_direction = 1;
1730 if (last_scrub_x > current_x) {
1732 /* pointer moved to the left */
1734 if (scrubbing_direction > 0) {
1736 /* we reversed direction to go backwards */
1739 scrub_reverse_distance += (int) (last_scrub_x - current_x);
1743 /* still moving to the left (backwards) */
1745 scrub_reversals = 0;
1746 scrub_reverse_distance = 0;
1748 delta = 0.01 * (last_scrub_x - current_x);
1749 _session->request_transport_speed (_session->transport_speed() - delta);
1753 /* pointer moved to the right */
1755 if (scrubbing_direction < 0) {
1756 /* we reversed direction to go forward */
1759 scrub_reverse_distance += (int) (current_x - last_scrub_x);
1762 /* still moving to the right */
1764 scrub_reversals = 0;
1765 scrub_reverse_distance = 0;
1767 delta = 0.01 * (current_x - last_scrub_x);
1768 _session->request_transport_speed (_session->transport_speed() + delta);
1772 /* if there have been more than 2 opposite motion moves detected, or one that moves
1773 back more than 10 pixels, reverse direction
1776 if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
1778 if (scrubbing_direction > 0) {
1779 /* was forwards, go backwards */
1780 _session->request_transport_speed (-0.1);
1781 scrubbing_direction = -1;
1783 /* was backwards, go forwards */
1784 _session->request_transport_speed (0.1);
1785 scrubbing_direction = 1;
1788 scrub_reverse_distance = 0;
1789 scrub_reversals = 0;
1793 last_scrub_x = current_x;
1797 Editor::motion_handler (ArdourCanvas::Item* /*item*/, GdkEvent* event, bool from_autoscroll)
1799 if (event->motion.is_hint) {
1802 /* We call this so that MOTION_NOTIFY events continue to be
1803 delivered to the canvas. We need to do this because we set
1804 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
1805 the density of the events, at the expense of a round-trip
1806 to the server. Given that this will mostly occur on cases
1807 where DISPLAY = :0.0, and given the cost of what the motion
1808 event might do, its a good tradeoff.
1811 track_canvas->get_pointer (x, y);
1814 if (current_stepping_trackview) {
1815 /* don't keep the persistent stepped trackview if the mouse moves */
1816 current_stepping_trackview = 0;
1817 step_timeout.disconnect ();
1820 if (_session && _session->actively_recording()) {
1821 /* Sorry. no dragging stuff around while we record */
1825 JoinObjectRangeState const old = _join_object_range_state;
1826 update_join_object_range_location (event->motion.x, event->motion.y);
1827 if (_join_object_range_state != old) {
1828 set_canvas_cursor ();
1831 bool handled = false;
1832 if (_drags->active ()) {
1833 handled = _drags->motion_handler (event, from_autoscroll);
1840 track_canvas_motion (event);
1845 Editor::remove_gain_control_point (ArdourCanvas::Item*item, GdkEvent* /*event*/)
1847 ControlPoint* control_point;
1849 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
1850 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
1854 // We shouldn't remove the first or last gain point
1855 if (control_point->line().is_last_point(*control_point) ||
1856 control_point->line().is_first_point(*control_point)) {
1860 control_point->line().remove_point (*control_point);
1864 Editor::remove_control_point (ArdourCanvas::Item* item, GdkEvent* /*event*/)
1866 ControlPoint* control_point;
1868 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
1869 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
1873 control_point->line().remove_point (*control_point);
1877 Editor::edit_control_point (ArdourCanvas::Item* item)
1879 ControlPoint* p = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"));
1882 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
1886 ControlPointDialog d (p);
1887 d.set_position (Gtk::WIN_POS_MOUSE);
1890 if (d.run () != RESPONSE_ACCEPT) {
1894 p->line().modify_point_y (*p, d.get_y_fraction ());
1899 Editor::visible_order_range (int* low, int* high) const
1901 *low = TimeAxisView::max_order ();
1904 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
1906 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
1908 if (!rtv->hidden()) {
1910 if (*high < rtv->order()) {
1911 *high = rtv->order ();
1914 if (*low > rtv->order()) {
1915 *low = rtv->order ();
1922 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
1924 /* Either add to or set the set the region selection, unless
1925 this is an alignment click (control used)
1928 if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
1929 TimeAxisView* tv = &rv.get_time_axis_view();
1930 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
1932 if (rtv && rtv->is_track()) {
1933 speed = rtv->track()->speed();
1936 nframes64_t where = get_preferred_edit_position();
1940 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
1942 align_region (rv.region(), SyncPoint, (nframes64_t) (where * speed));
1944 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
1946 align_region (rv.region(), End, (nframes64_t) (where * speed));
1950 align_region (rv.region(), Start, (nframes64_t) (where * speed));
1957 Editor::show_verbose_time_cursor (nframes64_t frame, double offset, double xpos, double ypos)
1960 Timecode::Time timecode;
1963 nframes64_t frame_rate;
1966 if (_session == 0) {
1972 if (Profile->get_sae() || Profile->get_small_screen()) {
1973 m = ARDOUR_UI::instance()->primary_clock.mode();
1975 m = ARDOUR_UI::instance()->secondary_clock.mode();
1979 case AudioClock::BBT:
1980 _session->bbt_time (frame, bbt);
1981 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, bbt.bars, bbt.beats, bbt.ticks);
1984 case AudioClock::Timecode:
1985 _session->timecode_time (frame, timecode);
1986 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
1989 case AudioClock::MinSec:
1990 /* XXX this is copied from show_verbose_duration_cursor() */
1991 frame_rate = _session->frame_rate();
1992 hours = frame / (frame_rate * 3600);
1993 frame = frame % (frame_rate * 3600);
1994 mins = frame / (frame_rate * 60);
1995 frame = frame % (frame_rate * 60);
1996 secs = (float) frame / (float) frame_rate;
1997 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
2001 snprintf (buf, sizeof(buf), "%" PRIi64, frame);
2005 if (xpos >= 0 && ypos >=0) {
2006 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
2008 set_verbose_canvas_cursor (buf, _drags->current_pointer_x() + offset - horizontal_position(), _drags->current_pointer_y() + offset - vertical_adjustment.get_value() + canvas_timebars_vsize);
2010 show_verbose_canvas_cursor ();
2014 Editor::show_verbose_duration_cursor (nframes64_t start, nframes64_t end, double offset, double xpos, double ypos)
2017 Timecode::Time timecode;
2021 nframes64_t distance, frame_rate;
2023 Meter meter_at_start(_session->tempo_map().meter_at(start));
2025 if (_session == 0) {
2031 if (Profile->get_sae() || Profile->get_small_screen()) {
2032 m = ARDOUR_UI::instance()->primary_clock.mode ();
2034 m = ARDOUR_UI::instance()->secondary_clock.mode ();
2038 case AudioClock::BBT:
2039 _session->bbt_time (start, sbbt);
2040 _session->bbt_time (end, ebbt);
2043 /* XXX this computation won't work well if the
2044 user makes a selection that spans any meter changes.
2047 ebbt.bars -= sbbt.bars;
2048 if (ebbt.beats >= sbbt.beats) {
2049 ebbt.beats -= sbbt.beats;
2052 ebbt.beats = int(meter_at_start.beats_per_bar()) + ebbt.beats - sbbt.beats;
2054 if (ebbt.ticks >= sbbt.ticks) {
2055 ebbt.ticks -= sbbt.ticks;
2058 ebbt.ticks = int(Meter::ticks_per_beat) + ebbt.ticks - sbbt.ticks;
2061 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, ebbt.bars, ebbt.beats, ebbt.ticks);
2064 case AudioClock::Timecode:
2065 _session->timecode_duration (end - start, timecode);
2066 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
2069 case AudioClock::MinSec:
2070 /* XXX this stuff should be elsewhere.. */
2071 distance = end - start;
2072 frame_rate = _session->frame_rate();
2073 hours = distance / (frame_rate * 3600);
2074 distance = distance % (frame_rate * 3600);
2075 mins = distance / (frame_rate * 60);
2076 distance = distance % (frame_rate * 60);
2077 secs = (float) distance / (float) frame_rate;
2078 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
2082 snprintf (buf, sizeof(buf), "%" PRIi64, end - start);
2086 if (xpos >= 0 && ypos >=0) {
2087 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
2090 set_verbose_canvas_cursor (buf, _drags->current_pointer_x() + offset, _drags->current_pointer_y() + offset);
2093 show_verbose_canvas_cursor ();
2097 Editor::collect_new_region_view (RegionView* rv)
2099 latest_regionviews.push_back (rv);
2103 Editor::collect_and_select_new_region_view (RegionView* rv)
2106 latest_regionviews.push_back (rv);
2110 Editor::cancel_selection ()
2112 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2113 (*i)->hide_selection ();
2116 selection->clear ();
2117 clicked_selection = 0;
2122 Editor::single_contents_trim (RegionView& rv, nframes64_t frame_delta, bool left_direction, bool swap_direction)
2124 boost::shared_ptr<Region> region (rv.region());
2126 if (region->locked()) {
2130 nframes64_t new_bound;
2133 TimeAxisView* tvp = clicked_axisview;
2134 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2136 if (tv && tv->is_track()) {
2137 speed = tv->track()->speed();
2140 if (left_direction) {
2141 if (swap_direction) {
2142 new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
2144 new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
2147 if (swap_direction) {
2148 new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
2150 new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
2154 region->trim_start ((nframes64_t) (new_bound * speed), this);
2155 rv.region_changed (PropertyChange (ARDOUR::Properties::start));
2159 Editor::single_start_trim (RegionView& rv, nframes64_t new_bound, bool no_overlap)
2161 boost::shared_ptr<Region> region (rv.region());
2163 if (region->locked()) {
2168 TimeAxisView* tvp = clicked_axisview;
2169 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2171 if (tv && tv->is_track()) {
2172 speed = tv->track()->speed();
2175 nframes64_t pre_trim_first_frame = region->first_frame();
2177 region->trim_front ((nframes64_t) (new_bound * speed), this);
2180 //Get the next region on the left of this region and shrink/expand it.
2181 boost::shared_ptr<Playlist> playlist (region->playlist());
2182 boost::shared_ptr<Region> region_left = playlist->find_next_region (pre_trim_first_frame, End, 0);
2184 bool regions_touching = false;
2186 if (region_left != 0 && (pre_trim_first_frame == region_left->last_frame() + 1)){
2187 regions_touching = true;
2190 //Only trim region on the left if the first frame has gone beyond the left region's last frame.
2191 if (region_left != 0 &&
2192 (region_left->last_frame() > region->first_frame() || regions_touching))
2194 region_left->trim_end(region->first_frame() - 1, this);
2198 rv.region_changed (ARDOUR::bounds_change);
2202 Editor::single_end_trim (RegionView& rv, nframes64_t new_bound, bool no_overlap)
2204 boost::shared_ptr<Region> region (rv.region());
2206 if (region->locked()) {
2211 TimeAxisView* tvp = clicked_axisview;
2212 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2214 if (tv && tv->is_track()) {
2215 speed = tv->track()->speed();
2218 nframes64_t pre_trim_last_frame = region->last_frame();
2220 region->trim_end ((nframes64_t) (new_bound * speed), this);
2223 //Get the next region on the right of this region and shrink/expand it.
2224 boost::shared_ptr<Playlist> playlist (region->playlist());
2225 boost::shared_ptr<Region> region_right = playlist->find_next_region (pre_trim_last_frame, Start, 1);
2227 bool regions_touching = false;
2229 if (region_right != 0 && (pre_trim_last_frame == region_right->first_frame() - 1)) {
2230 regions_touching = true;
2233 //Only trim region on the right if the last frame has gone beyond the right region's first frame.
2234 if (region_right != 0 &&
2235 (region_right->first_frame() < region->last_frame() || regions_touching))
2237 region_right->trim_front(region->last_frame() + 1, this);
2240 rv.region_changed (ARDOUR::bounds_change);
2243 rv.region_changed (PropertyChange (ARDOUR::Properties::length));
2249 Editor::point_trim (GdkEvent* event, nframes64_t new_bound)
2251 RegionView* rv = clicked_regionview;
2253 /* Choose action dependant on which button was pressed */
2254 switch (event->button.button) {
2256 begin_reversible_command (_("Start point trim"));
2258 if (selection->selected (rv)) {
2259 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
2260 i != selection->regions.by_layer().end(); ++i)
2263 cerr << "region view contains null region" << endl;
2266 if (!(*i)->region()->locked()) {
2267 (*i)->region()->clear_history ();
2268 (*i)->region()->trim_front (new_bound, this);
2269 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2274 if (!rv->region()->locked()) {
2275 rv->region()->clear_history ();
2276 rv->region()->trim_front (new_bound, this);
2277 _session->add_command(new StatefulDiffCommand (rv->region()));
2281 commit_reversible_command();
2285 begin_reversible_command (_("End point trim"));
2287 if (selection->selected (rv)) {
2289 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
2291 if (!(*i)->region()->locked()) {
2292 (*i)->region()->clear_history();
2293 (*i)->region()->trim_end (new_bound, this);
2294 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2300 if (!rv->region()->locked()) {
2301 rv->region()->clear_history ();
2302 rv->region()->trim_end (new_bound, this);
2303 _session->add_command (new StatefulDiffCommand (rv->region()));
2307 commit_reversible_command();
2316 Editor::thaw_region_after_trim (RegionView& rv)
2318 boost::shared_ptr<Region> region (rv.region());
2320 if (region->locked()) {
2324 region->resume_property_changes ();
2326 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(&rv);
2329 arv->unhide_envelope ();
2334 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* /*event*/)
2339 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2340 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2344 Location* location = find_location_from_marker (marker, is_start);
2345 location->set_hidden (true, this);
2350 Editor::reposition_zoom_rect (nframes64_t start, nframes64_t end)
2352 double x1 = frame_to_pixel (start);
2353 double x2 = frame_to_pixel (end);
2354 double y2 = full_canvas_height - 1.0;
2356 zoom_rect->property_x1() = x1;
2357 zoom_rect->property_y1() = 1.0;
2358 zoom_rect->property_x2() = x2;
2359 zoom_rect->property_y2() = y2;
2364 Editor::mouse_rename_region (ArdourCanvas::Item* /*item*/, GdkEvent* /*event*/)
2366 using namespace Gtkmm2ext;
2368 ArdourPrompter prompter (false);
2370 prompter.set_prompt (_("Name for region:"));
2371 prompter.set_initial_text (clicked_regionview->region()->name());
2372 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
2373 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
2374 prompter.show_all ();
2375 switch (prompter.run ()) {
2376 case Gtk::RESPONSE_ACCEPT:
2378 prompter.get_result(str);
2380 clicked_regionview->region()->set_name (str);
2389 Editor::mouse_brush_insert_region (RegionView* rv, nframes64_t pos)
2391 /* no brushing without a useful snap setting */
2393 switch (_snap_mode) {
2395 return; /* can't work because it allows region to be placed anywhere */
2400 switch (_snap_type) {
2408 /* don't brush a copy over the original */
2410 if (pos == rv->region()->position()) {
2414 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
2416 if (rtv == 0 || !rtv->is_track()) {
2420 boost::shared_ptr<Playlist> playlist = rtv->playlist();
2421 double speed = rtv->track()->speed();
2423 playlist->clear_history ();
2424 boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region()));
2425 playlist->add_region (new_region, (nframes64_t) (pos * speed));
2426 _session->add_command (new StatefulDiffCommand (playlist));
2428 // playlist is frozen, so we have to update manually XXX this is disgusting
2430 playlist->RegionAdded (new_region); /* EMIT SIGNAL */
2434 Editor::track_height_step_timeout ()
2436 if (get_microseconds() - last_track_height_step_timestamp < 250000) {
2437 current_stepping_trackview = 0;
2444 Editor::add_region_drag (ArdourCanvas::Item* item, GdkEvent* event, RegionView* region_view)
2446 assert (region_view);
2448 _region_motion_group->raise_to_top ();
2450 if (Config->get_edit_mode() == Splice) {
2451 _drags->add (new RegionSpliceDrag (this, item, region_view, selection->regions.by_layer()));
2453 RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id);
2454 _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), false, false));
2457 /* sync the canvas to what we think is its current state */
2458 update_canvas_now();
2462 Editor::add_region_copy_drag (ArdourCanvas::Item* item, GdkEvent* event, RegionView* region_view)
2464 assert (region_view);
2466 _region_motion_group->raise_to_top ();
2468 RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id);
2469 _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), false, true));
2473 Editor::add_region_brush_drag (ArdourCanvas::Item* item, GdkEvent* event, RegionView* region_view)
2475 assert (region_view);
2477 if (Config->get_edit_mode() == Splice) {
2481 RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id);
2482 _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), true, false));
2484 begin_reversible_command (_("Drag region brush"));
2487 /** Start a grab where a time range is selected, track(s) are selected, and the
2488 * user clicks and drags a region with a modifier in order to create a new region containing
2489 * the section of the clicked region that lies within the time range.
2492 Editor::start_selection_grab (ArdourCanvas::Item* /*item*/, GdkEvent* event)
2494 if (clicked_regionview == 0) {
2498 /* lets try to create new Region for the selection */
2500 vector<boost::shared_ptr<Region> > new_regions;
2501 create_region_from_selection (new_regions);
2503 if (new_regions.empty()) {
2507 /* XXX fix me one day to use all new regions */
2509 boost::shared_ptr<Region> region (new_regions.front());
2511 /* add it to the current stream/playlist.
2513 tricky: the streamview for the track will add a new regionview. we will
2514 catch the signal it sends when it creates the regionview to
2515 set the regionview we want to then drag.
2518 latest_regionviews.clear();
2519 sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
2521 /* A selection grab currently creates two undo/redo operations, one for
2522 creating the new region and another for moving it.
2525 begin_reversible_command (_("selection grab"));
2527 boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
2529 playlist->clear_history ();
2530 clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
2531 _session->add_command(new StatefulDiffCommand (playlist));
2533 commit_reversible_command ();
2537 if (latest_regionviews.empty()) {
2538 /* something went wrong */
2542 /* we need to deselect all other regionviews, and select this one
2543 i'm ignoring undo stuff, because the region creation will take care of it
2545 selection->set (latest_regionviews);
2547 _drags->set (new RegionMoveDrag (this, latest_regionviews.front()->get_canvas_group(), latest_regionviews.front(), latest_regionviews, false, false), event);
2553 if (_drags->active ()) {
2556 selection->clear ();
2561 Editor::set_internal_edit (bool yn)
2563 _internal_editing = yn;
2566 mouse_select_button.set_image (*(manage (new Image (::get_icon("midi_tool_pencil")))));
2567 mouse_select_button.get_image ()->show ();
2569 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2570 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (*i);
2572 mtv->start_step_editing ();
2575 start_step_editing ();
2579 mouse_select_button.set_image (*(manage (new Image (::get_icon("tool_range")))));
2580 mouse_select_button.get_image ()->show ();
2581 stop_step_editing ();
2583 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2584 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (*i);
2586 mtv->stop_step_editing ();
2591 set_canvas_cursor ();
2596 /** Update _join_object_range_state which indicate whether we are over the top or bottom half of a region view,
2597 * used by the `join object/range' tool mode.
2600 Editor::update_join_object_range_location (double x, double y)
2602 /* XXX: actually, this decides based on whether the mouse is in the top or bottom half of a RouteTimeAxisView;
2603 entered_{track,regionview} is not always setup (e.g. if the mouse is over a TimeSelection), and to get a Region
2604 that we're over requires searching the playlist.
2607 if (join_object_range_button.get_active() == false || (mouse_mode != MouseRange && mouse_mode != MouseObject)) {
2608 _join_object_range_state = JOIN_OBJECT_RANGE_NONE;
2612 if (mouse_mode == MouseObject) {
2613 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2614 } else if (mouse_mode == MouseRange) {
2615 _join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
2618 /* XXX: maybe we should make entered_track work in all cases, rather than resorting to this */
2619 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y + vertical_adjustment.get_value() - canvas_timebars_vsize);
2623 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
2628 rtv->canvas_display()->w2i (cx, cy);
2630 bool const top_half = cy < rtv->current_height () / 2;
2632 _join_object_range_state = top_half ? JOIN_OBJECT_RANGE_RANGE : JOIN_OBJECT_RANGE_OBJECT;
2638 Editor::effective_mouse_mode () const
2640 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
2642 } else if (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE) {