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"
35 #include "ardour_ui.h"
38 #include "time_axis_view.h"
39 #include "audio_time_axis.h"
40 #include "audio_region_view.h"
41 #include "midi_region_view.h"
43 #include "streamview.h"
44 #include "region_gain_line.h"
45 #include "automation_time_axis.h"
46 #include "control_point.h"
49 #include "selection.h"
52 #include "rgb_macros.h"
53 #include "control_point_dialog.h"
54 #include "editor_drag.h"
56 #include "ardour/types.h"
57 #include "ardour/profile.h"
58 #include "ardour/route.h"
59 #include "ardour/audio_track.h"
60 #include "ardour/audio_diskstream.h"
61 #include "ardour/midi_diskstream.h"
62 #include "ardour/playlist.h"
63 #include "ardour/audioplaylist.h"
64 #include "ardour/audioregion.h"
65 #include "ardour/midi_region.h"
66 #include "ardour/dB.h"
67 #include "ardour/utils.h"
68 #include "ardour/region_factory.h"
69 #include "ardour/source_factory.h"
70 #include "ardour/session.h"
77 using namespace ARDOUR;
81 using namespace Editing;
84 Editor::mouse_frame (nframes64_t& where, bool& in_track_canvas) const
88 Gdk::ModifierType mask;
89 Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->track_canvas->get_window();
90 Glib::RefPtr<const Gdk::Window> pointer_window;
96 pointer_window = canvas_window->get_pointer (x, y, mask);
98 if (pointer_window == track_canvas->get_bin_window()) {
101 in_track_canvas = true;
104 in_track_canvas = false;
109 event.type = GDK_BUTTON_RELEASE;
113 where = event_frame (&event, 0, 0);
118 Editor::event_frame (GdkEvent const * event, double* pcx, double* pcy) const
132 switch (event->type) {
133 case GDK_BUTTON_RELEASE:
134 case GDK_BUTTON_PRESS:
135 case GDK_2BUTTON_PRESS:
136 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:
144 *pcx = event->motion.x;
145 *pcy = event->motion.y;
146 _trackview_group->w2i(*pcx, *pcy);
148 case GDK_ENTER_NOTIFY:
149 case GDK_LEAVE_NOTIFY:
150 track_canvas->w2c(event->crossing.x, event->crossing.y, *pcx, *pcy);
153 case GDK_KEY_RELEASE:
154 // track_canvas->w2c(event->key.x, event->key.y, *pcx, *pcy);
157 warning << string_compose (_("Editor::event_frame() used on unhandled event type %1"), event->type) << endmsg;
161 /* note that pixel_to_frame() never returns less than zero, so even if the pixel
162 position is negative (as can be the case with motion events in particular),
163 the frame location is always positive.
166 return pixel_to_frame (*pcx);
170 Editor::which_grabber_cursor ()
172 Gdk::Cursor* c = grabber_cursor;
174 if (_internal_editing) {
175 switch (mouse_mode) {
177 c = midi_pencil_cursor;
185 c = midi_resize_cursor;
194 switch (_edit_point) {
196 c = grabber_edit_point_cursor;
207 Editor::set_canvas_cursor ()
209 if (_internal_editing) {
211 switch (mouse_mode) {
213 current_canvas_cursor = midi_pencil_cursor;
217 current_canvas_cursor = which_grabber_cursor();
221 current_canvas_cursor = midi_resize_cursor;
230 switch (mouse_mode) {
232 current_canvas_cursor = selector_cursor;
236 current_canvas_cursor = which_grabber_cursor();
240 current_canvas_cursor = cross_hair_cursor;
244 current_canvas_cursor = zoom_cursor;
248 current_canvas_cursor = time_fx_cursor; // just use playhead
252 current_canvas_cursor = speaker_cursor;
258 track_canvas->get_window()->set_cursor(*current_canvas_cursor);
263 Editor::set_mouse_mode (MouseMode m, bool force)
269 if (!force && m == mouse_mode) {
273 Glib::RefPtr<Action> act;
277 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-range"));
281 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-object"));
285 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-gain"));
289 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-zoom"));
293 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-timefx"));
297 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-audition"));
303 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
306 /* go there and back to ensure that the toggled handler is called to set up mouse_mode */
307 tact->set_active (false);
308 tact->set_active (true);
312 Editor::mouse_mode_toggled (MouseMode m)
318 if (mouse_mode != MouseRange) {
320 /* in all modes except range, hide the range selection,
321 show the object (region) selection.
324 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
325 (*i)->set_should_show_selection (true);
327 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
328 (*i)->hide_selection ();
334 in range mode,show the range selection.
337 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
338 if ((*i)->get_selected()) {
339 (*i)->show_selection (selection->time);
344 set_canvas_cursor ();
348 Editor::step_mouse_mode (bool next)
350 switch (current_mouse_mode()) {
353 if (Profile->get_sae()) {
354 set_mouse_mode (MouseZoom);
356 set_mouse_mode (MouseRange);
359 set_mouse_mode (MouseTimeFX);
364 if (next) set_mouse_mode (MouseZoom);
365 else set_mouse_mode (MouseObject);
370 if (Profile->get_sae()) {
371 set_mouse_mode (MouseTimeFX);
373 set_mouse_mode (MouseGain);
376 if (Profile->get_sae()) {
377 set_mouse_mode (MouseObject);
379 set_mouse_mode (MouseRange);
385 if (next) set_mouse_mode (MouseTimeFX);
386 else set_mouse_mode (MouseZoom);
391 set_mouse_mode (MouseAudition);
393 if (Profile->get_sae()) {
394 set_mouse_mode (MouseZoom);
396 set_mouse_mode (MouseGain);
402 if (next) set_mouse_mode (MouseObject);
403 else set_mouse_mode (MouseTimeFX);
409 Editor::button_selection (ArdourCanvas::Item* /*item*/, GdkEvent* event, ItemType item_type)
411 /* in object/audition/timefx/gain-automation mode,
412 any button press sets the selection if the object
413 can be selected. this is a bit of hack, because
414 we want to avoid this if the mouse operation is a
417 note: not dbl-click or triple-click
420 if (((mouse_mode != MouseObject) &&
421 (mouse_mode != MouseAudition || item_type != RegionItem) &&
422 (mouse_mode != MouseTimeFX || item_type != RegionItem) &&
423 (mouse_mode != MouseGain) &&
424 (mouse_mode != MouseRange)) ||
426 ((event->type != GDK_BUTTON_PRESS && event->type != GDK_BUTTON_RELEASE) || event->button.button > 3)) {
431 if (event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) {
433 if ((event->button.state & Keyboard::RelevantModifierKeyMask) && event->button.button != 1) {
435 /* almost no selection action on modified button-2 or button-3 events */
437 if (item_type != RegionItem && event->button.button != 2) {
443 Selection::Operation op = Keyboard::selection_type (event->button.state);
444 bool press = (event->type == GDK_BUTTON_PRESS);
446 // begin_reversible_command (_("select on click"));
450 if (mouse_mode != MouseRange) {
451 set_selected_regionview_from_click (press, op, true);
452 } else if (event->type == GDK_BUTTON_PRESS) {
453 set_selected_track_as_side_effect ();
457 case RegionViewNameHighlight:
459 if (mouse_mode != MouseRange) {
460 set_selected_regionview_from_click (press, op, true);
461 } else if (event->type == GDK_BUTTON_PRESS) {
462 set_selected_track_as_side_effect ();
467 case FadeInHandleItem:
469 case FadeOutHandleItem:
471 if (mouse_mode != MouseRange) {
472 set_selected_regionview_from_click (press, op, true);
473 } else if (event->type == GDK_BUTTON_PRESS) {
474 set_selected_track_as_side_effect ();
478 case ControlPointItem:
479 set_selected_track_as_side_effect ();
480 if (mouse_mode != MouseRange) {
481 set_selected_control_point_from_click (op, false);
486 /* for context click or range selection, select track */
487 if (event->button.button == 3) {
488 set_selected_track_as_side_effect ();
489 } else if (event->type == GDK_BUTTON_PRESS && mouse_mode == MouseRange) {
490 set_selected_track_as_side_effect ();
494 case AutomationTrackItem:
495 set_selected_track_as_side_effect (true);
504 Editor::button_press_handler_1 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
507 _drag->item()->ungrab (event->button.time);
512 /* single mouse clicks on any of these item types operate
513 independent of mouse mode, mostly because they are
514 not on the main track canvas or because we want
519 case PlayheadCursorItem:
521 _drag = new CursorDrag (this, item, true);
522 _drag->start_grab (event);
526 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
527 hide_marker (item, event);
530 _drag = new MarkerDrag (this, item);
531 _drag->start_grab (event);
535 case TempoMarkerItem:
537 _drag = new TempoMarkerDrag (
540 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
542 _drag->start_grab (event);
545 case MeterMarkerItem:
547 _drag = new MeterMarkerDrag (
550 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
552 _drag->start_grab (event);
558 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
560 _drag = new CursorDrag (this, &playhead_cursor->canvas_item, false);
561 _drag->start_grab (event);
567 case RangeMarkerBarItem:
569 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
570 _drag = new CursorDrag (this, &playhead_cursor->canvas_item, false);
572 _drag = new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateRangeMarker);
574 _drag->start_grab (event);
578 case CdMarkerBarItem:
580 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
581 _drag = new CursorDrag (this, &playhead_cursor->canvas_item, false);
583 _drag = new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateCDMarker);
585 _drag->start_grab (event);
589 case TransportMarkerBarItem:
591 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
592 _drag = new CursorDrag (this, &playhead_cursor->canvas_item, false);
594 _drag = new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateTransportMarker);
596 _drag->start_grab (event);
604 if (internal_editing()) {
608 _drag = new RegionCreateDrag (this, item, clicked_axisview);
609 _drag->start_grab (event);
612 /* Note: we don't get here if not in internal_editing() mode */
613 if (mouse_mode == MouseTimeFX) {
615 _drag = new NoteResizeDrag (this, item);
616 _drag->start_grab (event);
618 } else if (mouse_mode == MouseObject) {
620 _drag = new NoteDrag (this, item);
621 _drag->start_grab (event);
631 switch (mouse_mode) {
634 case StartSelectionTrimItem:
636 _drag = new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim);
637 _drag->start_grab (event);
640 case EndSelectionTrimItem:
642 _drag = new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim);
643 _drag->start_grab (event);
647 if (Keyboard::modifier_state_contains
648 (event->button.state, Keyboard::ModifierMask(Keyboard::SecondaryModifier))) {
649 // contains and not equals because I can't use alt as a modifier alone.
650 start_selection_grab (item, event);
651 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
652 /* grab selection for moving */
654 _drag = new SelectionDrag (this, item, SelectionDrag::SelectionMove);
655 _drag->start_grab (event);
657 /* this was debated, but decided the more common action was to
658 make a new selection */
660 _drag = new SelectionDrag (this, item, SelectionDrag::CreateSelection);
661 _drag->start_grab (event);
667 _drag = new SelectionDrag (this, item, SelectionDrag::CreateSelection);
668 _drag->start_grab (event);
674 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)) &&
675 event->type == GDK_BUTTON_PRESS) {
678 _drag = new RubberbandSelectDrag (this, item);
679 _drag->start_grab (event);
681 } else if (event->type == GDK_BUTTON_PRESS) {
684 case FadeInHandleItem:
687 RegionSelection s = get_equivalent_regions (selection->regions, RouteGroup::Edit);
688 _drag = new FadeInDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), s);
689 _drag->start_grab (event);
693 case FadeOutHandleItem:
696 RegionSelection s = get_equivalent_regions (selection->regions, RouteGroup::Edit);
697 _drag = new FadeOutDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), s);
698 _drag->start_grab (event);
703 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
704 start_region_copy_grab (item, event, clicked_regionview);
705 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
706 start_region_brush_grab (item, event, clicked_regionview);
708 start_region_grab (item, event, clicked_regionview);
712 case RegionViewNameHighlight:
715 RegionSelection s = get_equivalent_regions (selection->regions, RouteGroup::Edit);
716 _drag = new TrimDrag (this, item, clicked_regionview, s.by_layer());
717 _drag->start_grab (event);
724 /* rename happens on edit clicks */
726 RegionSelection s = get_equivalent_regions (selection->regions, RouteGroup::Edit);
727 _drag = new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, s.by_layer());
728 _drag->start_grab (event);
733 case ControlPointItem:
735 _drag = new ControlPointDrag (this, item);
736 _drag->start_grab (event);
740 case AutomationLineItem:
742 _drag = new LineDrag (this, item);
743 _drag->start_grab (event);
748 case AutomationTrackItem:
750 _drag = new RubberbandSelectDrag (this, item);
751 _drag->start_grab (event);
755 case ImageFrameHandleStartItem:
756 imageframe_start_handle_op(item, event) ;
759 case ImageFrameHandleEndItem:
760 imageframe_end_handle_op(item, event) ;
763 case MarkerViewHandleStartItem:
764 markerview_item_start_handle_op(item, event) ;
767 case MarkerViewHandleEndItem:
768 markerview_item_end_handle_op(item, event) ;
772 start_markerview_grab(item, event) ;
775 start_imageframe_grab(item, event) ;
793 /* start a grab so that if we finish after moving
794 we can tell what happened.
797 _drag = new RegionGainDrag (this, item);
798 _drag->start_grab (event, current_canvas_cursor);
803 _drag = new LineDrag (this, item);
804 _drag->start_grab (event);
807 case ControlPointItem:
809 _drag = new ControlPointDrag (this, item);
810 _drag->start_grab (event);
821 case ControlPointItem:
823 _drag = new ControlPointDrag (this, item);
824 _drag->start_grab (event);
827 case AutomationLineItem:
829 _drag = new LineDrag (this, item);
830 _drag->start_grab (event);
834 // XXX need automation mode to identify which
836 // start_line_grab_from_regionview (item, event);
846 if (event->type == GDK_BUTTON_PRESS) {
848 _drag = new MouseZoomDrag (this, item);
849 _drag->start_grab (event);
856 if (item_type == RegionItem) {
858 _drag = new TimeFXDrag (this, item, clicked_regionview, selection->regions.by_layer());
859 _drag->start_grab (event);
864 _drag = new ScrubDrag (this, item);
865 _drag->start_grab (event);
867 scrub_reverse_distance = 0;
868 last_scrub_x = event->button.x;
869 scrubbing_direction = 0;
870 track_canvas->get_window()->set_cursor (*transparent_cursor);
882 Editor::button_press_handler_2 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
884 switch (mouse_mode) {
888 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
889 start_region_copy_grab (item, event, clicked_regionview);
891 start_region_grab (item, event, clicked_regionview);
895 case ControlPointItem:
897 _drag = new ControlPointDrag (this, item);
898 _drag->start_grab (event);
907 case RegionViewNameHighlight:
909 _drag = new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer());
910 _drag->start_grab (event);
916 _drag = new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer());
917 _drag->start_grab (event);
928 /* relax till release */
934 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
935 temporal_zoom_session();
937 temporal_zoom_to_frame (true, event_frame(event));
950 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
952 if (event->type != GDK_BUTTON_PRESS) {
956 Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->track_canvas->get_window();
959 Glib::RefPtr<const Gdk::Window> pointer_window;
962 Gdk::ModifierType mask;
964 pointer_window = canvas_window->get_pointer (x, y, mask);
966 if (pointer_window == track_canvas->get_bin_window()) {
967 track_canvas->window_to_world (x, y, wx, wy);
968 allow_vertical_scroll = true;
970 allow_vertical_scroll = false;
974 track_canvas->grab_focus();
976 if (session && session->actively_recording()) {
980 button_selection (item, event, item_type);
983 (Keyboard::is_delete_event (&event->button) ||
984 Keyboard::is_context_menu_event (&event->button) ||
985 Keyboard::is_edit_event (&event->button))) {
987 /* handled by button release */
991 switch (event->button.button) {
993 return button_press_handler_1 (item, event, item_type);
997 return button_press_handler_2 (item, event, item_type);
1012 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1014 nframes64_t where = event_frame (event, 0, 0);
1015 AutomationTimeAxisView* atv = 0;
1017 /* no action if we're recording */
1019 if (session && session->actively_recording()) {
1023 /* first, see if we're finishing a drag ... */
1025 bool were_dragging = false;
1027 bool const r = _drag->end_grab (event);
1031 /* grab dragged, so do nothing else */
1035 were_dragging = true;
1038 button_selection (item, event, item_type);
1040 /* edit events get handled here */
1042 if (_drag == 0 && Keyboard::is_edit_event (&event->button)) {
1043 switch (item_type) {
1048 case TempoMarkerItem:
1049 edit_tempo_marker (item);
1052 case MeterMarkerItem:
1053 edit_meter_marker (item);
1056 case RegionViewName:
1057 if (clicked_regionview->name_active()) {
1058 return mouse_rename_region (item, event);
1062 case ControlPointItem:
1063 edit_control_point (item);
1072 /* context menu events get handled here */
1074 if (Keyboard::is_context_menu_event (&event->button)) {
1078 /* no matter which button pops up the context menu, tell the menu
1079 widget to use button 1 to drive menu selection.
1082 switch (item_type) {
1084 case FadeInHandleItem:
1086 case FadeOutHandleItem:
1087 popup_fade_context_menu (1, event->button.time, item, item_type);
1091 popup_track_context_menu (1, event->button.time, item_type, false, where);
1095 case RegionViewNameHighlight:
1096 case RegionViewName:
1097 popup_track_context_menu (1, event->button.time, item_type, false, where);
1101 popup_track_context_menu (1, event->button.time, item_type, true, where);
1104 case AutomationTrackItem:
1105 popup_track_context_menu (1, event->button.time, item_type, false, where);
1109 case RangeMarkerBarItem:
1110 case TransportMarkerBarItem:
1111 case CdMarkerBarItem:
1114 popup_ruler_menu (where, item_type);
1118 marker_context_menu (&event->button, item);
1121 case TempoMarkerItem:
1122 tm_marker_context_menu (&event->button, item);
1125 case MeterMarkerItem:
1126 tm_marker_context_menu (&event->button, item);
1129 case CrossfadeViewItem:
1130 popup_track_context_menu (1, event->button.time, item_type, false, where);
1134 case ImageFrameItem:
1135 popup_imageframe_edit_menu(1, event->button.time, item, true) ;
1137 case ImageFrameTimeAxisItem:
1138 popup_imageframe_edit_menu(1, event->button.time, item, false) ;
1140 case MarkerViewItem:
1141 popup_marker_time_axis_edit_menu(1, event->button.time, item, true) ;
1143 case MarkerTimeAxisItem:
1144 popup_marker_time_axis_edit_menu(1, event->button.time, item, false) ;
1156 /* delete events get handled here */
1158 if (_drag == 0 && Keyboard::is_delete_event (&event->button)) {
1160 switch (item_type) {
1161 case TempoMarkerItem:
1162 remove_tempo_marker (item);
1165 case MeterMarkerItem:
1166 remove_meter_marker (item);
1170 remove_marker (*item, event);
1174 if (mouse_mode == MouseObject) {
1175 remove_clicked_region ();
1179 case ControlPointItem:
1180 if (mouse_mode == MouseGain) {
1181 remove_gain_control_point (item, event);
1183 remove_control_point (item, event);
1193 switch (event->button.button) {
1196 switch (item_type) {
1197 /* see comments in button_press_handler */
1198 case PlayheadCursorItem:
1201 case AutomationLineItem:
1202 case StartSelectionTrimItem:
1203 case EndSelectionTrimItem:
1207 if (!_dragging_playhead) {
1208 snap_to_with_modifier (where, event, 0, true);
1209 mouse_add_new_marker (where);
1213 case CdMarkerBarItem:
1214 if (!_dragging_playhead) {
1215 // if we get here then a dragged range wasn't done
1216 snap_to_with_modifier (where, event, 0, true);
1217 mouse_add_new_marker (where, true);
1222 if (!_dragging_playhead) {
1223 snap_to_with_modifier (where, event);
1224 mouse_add_new_tempo_event (where);
1229 if (!_dragging_playhead) {
1230 mouse_add_new_meter_event (pixel_to_frame (event->button.x));
1239 switch (mouse_mode) {
1241 switch (item_type) {
1242 case AutomationTrackItem:
1243 atv = dynamic_cast<AutomationTimeAxisView*>(clicked_axisview);
1245 atv->add_automation_event (item, event, where, event->button.y);
1256 // Gain only makes sense for audio regions
1258 if (!dynamic_cast<AudioRegionView*>(clicked_regionview)) {
1262 switch (item_type) {
1264 /* check that we didn't drag before releasing, since
1265 its really annoying to create new control
1266 points when doing this.
1268 if (were_dragging) {
1269 dynamic_cast<AudioRegionView*>(clicked_regionview)->add_gain_point_event (item, event);
1274 case AutomationTrackItem:
1275 dynamic_cast<AutomationTimeAxisView*>(clicked_axisview)->
1276 add_automation_event (item, event, where, event->button.y);
1285 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1286 if (scrubbing_direction == 0) {
1287 /* no drag, just a click */
1288 switch (item_type) {
1290 play_selected_region ();
1296 /* make sure we stop */
1297 session->request_transport_speed (0.0);
1311 switch (mouse_mode) {
1314 switch (item_type) {
1316 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1318 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier|Keyboard::SecondaryModifier))) {
1321 // Button2 click is unused
1334 // x_style_paste (where, 1.0);
1354 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* /*event*/, ItemType item_type)
1360 if (last_item_entered != item) {
1361 last_item_entered = item;
1362 last_item_entered_n = 0;
1365 switch (item_type) {
1366 case ControlPointItem:
1367 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1368 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1369 cp->set_visible (true);
1373 at_y = cp->get_y ();
1374 cp->item()->i2w (at_x, at_y);
1378 fraction = 1.0 - (cp->get_y() / cp->line().height());
1380 if (is_drawable() && dynamic_cast<ScrubDrag*> (_drag) == 0) {
1381 track_canvas->get_window()->set_cursor (*fader_cursor);
1384 last_item_entered_n++;
1385 set_verbose_canvas_cursor (cp->line().get_verbose_cursor_string (fraction), at_x, at_y);
1386 if (last_item_entered_n < 10) {
1387 show_verbose_canvas_cursor ();
1393 if (mouse_mode == MouseGain) {
1394 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1396 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredGainLine.get();
1397 if (is_drawable()) {
1398 track_canvas->get_window()->set_cursor (*fader_cursor);
1403 case AutomationLineItem:
1404 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1406 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1408 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredAutomationLine.get();
1410 if (is_drawable()) {
1411 track_canvas->get_window()->set_cursor (*fader_cursor);
1416 case RegionViewNameHighlight:
1417 if (is_drawable() && mouse_mode == MouseObject) {
1418 track_canvas->get_window()->set_cursor (*trimmer_cursor);
1422 case StartSelectionTrimItem:
1423 case EndSelectionTrimItem:
1426 case ImageFrameHandleStartItem:
1427 case ImageFrameHandleEndItem:
1428 case MarkerViewHandleStartItem:
1429 case MarkerViewHandleEndItem:
1432 if (is_drawable()) {
1433 track_canvas->get_window()->set_cursor (*trimmer_cursor);
1437 case PlayheadCursorItem:
1438 if (is_drawable()) {
1439 switch (_edit_point) {
1441 track_canvas->get_window()->set_cursor (*grabber_edit_point_cursor);
1444 track_canvas->get_window()->set_cursor (*grabber_cursor);
1450 case RegionViewName:
1452 /* when the name is not an active item, the entire name highlight is for trimming */
1454 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1455 if (mouse_mode == MouseObject && is_drawable()) {
1456 track_canvas->get_window()->set_cursor (*trimmer_cursor);
1462 case AutomationTrackItem:
1463 if (is_drawable()) {
1464 Gdk::Cursor *cursor;
1465 switch (mouse_mode) {
1467 cursor = selector_cursor;
1470 cursor = zoom_cursor;
1473 cursor = cross_hair_cursor;
1477 track_canvas->get_window()->set_cursor (*cursor);
1479 AutomationTimeAxisView* atv;
1480 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1481 clear_entered_track = false;
1482 set_entered_track (atv);
1488 case RangeMarkerBarItem:
1489 case TransportMarkerBarItem:
1490 case CdMarkerBarItem:
1493 if (is_drawable()) {
1494 track_canvas->get_window()->set_cursor (*timebar_cursor);
1499 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1502 entered_marker = marker;
1503 marker->set_color_rgba (ARDOUR_UI::config()->canvasvar_EnteredMarker.get());
1505 case MeterMarkerItem:
1506 case TempoMarkerItem:
1507 if (is_drawable()) {
1508 track_canvas->get_window()->set_cursor (*timebar_cursor);
1511 case FadeInHandleItem:
1512 case FadeOutHandleItem:
1513 if (mouse_mode == MouseObject) {
1514 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1516 rect->property_fill_color_rgba() = 0;
1517 rect->property_outline_pixels() = 1;
1526 /* second pass to handle entered track status in a comprehensible way.
1529 switch (item_type) {
1531 case AutomationLineItem:
1532 case ControlPointItem:
1533 /* these do not affect the current entered track state */
1534 clear_entered_track = false;
1537 case AutomationTrackItem:
1538 /* handled above already */
1542 set_entered_track (0);
1550 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent* /*event*/, ItemType item_type)
1559 switch (item_type) {
1560 case ControlPointItem:
1561 cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
1562 if (cp->line().the_list()->interpolation() != AutomationList::Discrete) {
1563 if (cp->line().npoints() > 1 && !cp->selected()) {
1564 cp->set_visible (false);
1568 if (is_drawable()) {
1569 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1572 hide_verbose_canvas_cursor ();
1575 case RegionViewNameHighlight:
1576 case StartSelectionTrimItem:
1577 case EndSelectionTrimItem:
1578 case PlayheadCursorItem:
1581 case ImageFrameHandleStartItem:
1582 case ImageFrameHandleEndItem:
1583 case MarkerViewHandleStartItem:
1584 case MarkerViewHandleEndItem:
1587 if (is_drawable()) {
1588 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1593 case AutomationLineItem:
1594 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1596 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1598 line->property_fill_color_rgba() = al->get_line_color();
1600 if (is_drawable()) {
1601 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1605 case RegionViewName:
1606 /* see enter_handler() for notes */
1607 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1608 if (is_drawable() && mouse_mode == MouseObject) {
1609 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1614 case RangeMarkerBarItem:
1615 case TransportMarkerBarItem:
1616 case CdMarkerBarItem:
1620 if (is_drawable()) {
1621 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1626 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1630 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
1631 location_flags_changed (loc, this);
1634 case MeterMarkerItem:
1635 case TempoMarkerItem:
1637 if (is_drawable()) {
1638 track_canvas->get_window()->set_cursor (*timebar_cursor);
1643 case FadeInHandleItem:
1644 case FadeOutHandleItem:
1645 rv = static_cast<RegionView*>(item->get_data ("regionview"));
1647 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1649 rect->property_fill_color_rgba() = rv->get_fill_color();
1650 rect->property_outline_pixels() = 0;
1655 case AutomationTrackItem:
1656 if (is_drawable()) {
1657 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1658 clear_entered_track = true;
1659 Glib::signal_idle().connect (mem_fun(*this, &Editor::left_automation_track));
1671 Editor::left_automation_track ()
1673 if (clear_entered_track) {
1674 set_entered_track (0);
1675 clear_entered_track = false;
1685 if (scrubbing_direction == 0) {
1687 session->request_locate (_drag->current_pointer_frame(), false);
1688 session->request_transport_speed (0.1);
1689 scrubbing_direction = 1;
1693 if (last_scrub_x > _drag->current_pointer_x()) {
1695 /* pointer moved to the left */
1697 if (scrubbing_direction > 0) {
1699 /* we reversed direction to go backwards */
1702 scrub_reverse_distance += (int) (last_scrub_x - _drag->current_pointer_x());
1706 /* still moving to the left (backwards) */
1708 scrub_reversals = 0;
1709 scrub_reverse_distance = 0;
1711 delta = 0.01 * (last_scrub_x - _drag->current_pointer_x());
1712 session->request_transport_speed (session->transport_speed() - delta);
1716 /* pointer moved to the right */
1718 if (scrubbing_direction < 0) {
1719 /* we reversed direction to go forward */
1722 scrub_reverse_distance += (int) (_drag->current_pointer_x() - last_scrub_x);
1725 /* still moving to the right */
1727 scrub_reversals = 0;
1728 scrub_reverse_distance = 0;
1730 delta = 0.01 * (_drag->current_pointer_x() - last_scrub_x);
1731 session->request_transport_speed (session->transport_speed() + delta);
1735 /* if there have been more than 2 opposite motion moves detected, or one that moves
1736 back more than 10 pixels, reverse direction
1739 if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
1741 if (scrubbing_direction > 0) {
1742 /* was forwards, go backwards */
1743 session->request_transport_speed (-0.1);
1744 scrubbing_direction = -1;
1746 /* was backwards, go forwards */
1747 session->request_transport_speed (0.1);
1748 scrubbing_direction = 1;
1751 scrub_reverse_distance = 0;
1752 scrub_reversals = 0;
1756 last_scrub_x = _drag->current_pointer_x();
1760 Editor::motion_handler (ArdourCanvas::Item* /*item*/, GdkEvent* event, bool from_autoscroll)
1762 if (event->motion.is_hint) {
1765 /* We call this so that MOTION_NOTIFY events continue to be
1766 delivered to the canvas. We need to do this because we set
1767 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
1768 the density of the events, at the expense of a round-trip
1769 to the server. Given that this will mostly occur on cases
1770 where DISPLAY = :0.0, and given the cost of what the motion
1771 event might do, its a good tradeoff.
1774 track_canvas->get_pointer (x, y);
1777 if (current_stepping_trackview) {
1778 /* don't keep the persistent stepped trackview if the mouse moves */
1779 current_stepping_trackview = 0;
1780 step_timeout.disconnect ();
1783 if (session && session->actively_recording()) {
1784 /* Sorry. no dragging stuff around while we record */
1788 bool handled = false;
1790 handled = _drag->motion_handler (event, from_autoscroll);
1797 track_canvas_motion (event);
1802 Editor::remove_gain_control_point (ArdourCanvas::Item*item, GdkEvent* /*event*/)
1804 ControlPoint* control_point;
1806 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
1807 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
1811 // We shouldn't remove the first or last gain point
1812 if (control_point->line().is_last_point(*control_point) ||
1813 control_point->line().is_first_point(*control_point)) {
1817 control_point->line().remove_point (*control_point);
1821 Editor::remove_control_point (ArdourCanvas::Item* item, GdkEvent* /*event*/)
1823 ControlPoint* control_point;
1825 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
1826 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
1830 control_point->line().remove_point (*control_point);
1834 Editor::edit_control_point (ArdourCanvas::Item* item)
1836 ControlPoint* p = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"));
1839 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
1843 ControlPointDialog d (p);
1844 d.set_position (Gtk::WIN_POS_MOUSE);
1847 if (d.run () != RESPONSE_ACCEPT) {
1851 p->line().modify_point_y (*p, d.get_y_fraction ());
1856 Editor::visible_order_range (int* low, int* high) const
1858 *low = TimeAxisView::max_order ();
1861 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
1863 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
1865 if (!rtv->hidden()) {
1867 if (*high < rtv->order()) {
1868 *high = rtv->order ();
1871 if (*low > rtv->order()) {
1872 *low = rtv->order ();
1879 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
1881 /* Either add to or set the set the region selection, unless
1882 this is an alignment click (control used)
1885 if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
1886 TimeAxisView* tv = &rv.get_time_axis_view();
1887 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
1889 if (rtv && rtv->is_track()) {
1890 speed = rtv->get_diskstream()->speed();
1893 nframes64_t where = get_preferred_edit_position();
1897 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
1899 align_region (rv.region(), SyncPoint, (nframes64_t) (where * speed));
1901 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
1903 align_region (rv.region(), End, (nframes64_t) (where * speed));
1907 align_region (rv.region(), Start, (nframes64_t) (where * speed));
1914 Editor::show_verbose_time_cursor (nframes64_t frame, double offset, double xpos, double ypos)
1917 Timecode::Time timecode;
1920 nframes64_t frame_rate;
1929 if (Profile->get_sae() || Profile->get_small_screen()) {
1930 m = ARDOUR_UI::instance()->primary_clock.mode();
1932 m = ARDOUR_UI::instance()->secondary_clock.mode();
1936 case AudioClock::BBT:
1937 session->bbt_time (frame, bbt);
1938 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, bbt.bars, bbt.beats, bbt.ticks);
1941 case AudioClock::Timecode:
1942 session->timecode_time (frame, timecode);
1943 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
1946 case AudioClock::MinSec:
1947 /* XXX this is copied from show_verbose_duration_cursor() */
1948 frame_rate = session->frame_rate();
1949 hours = frame / (frame_rate * 3600);
1950 frame = frame % (frame_rate * 3600);
1951 mins = frame / (frame_rate * 60);
1952 frame = frame % (frame_rate * 60);
1953 secs = (float) frame / (float) frame_rate;
1954 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
1958 snprintf (buf, sizeof(buf), "%" PRIi64, frame);
1962 if (xpos >= 0 && ypos >=0) {
1963 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
1966 set_verbose_canvas_cursor (buf, _drag->current_pointer_x() + offset - horizontal_adjustment.get_value(), _drag->current_pointer_y() + offset - vertical_adjustment.get_value() + canvas_timebars_vsize);
1968 show_verbose_canvas_cursor ();
1972 Editor::show_verbose_duration_cursor (nframes64_t start, nframes64_t end, double offset, double xpos, double ypos)
1975 Timecode::Time timecode;
1979 nframes64_t distance, frame_rate;
1981 Meter meter_at_start(session->tempo_map().meter_at(start));
1989 if (Profile->get_sae() || Profile->get_small_screen()) {
1990 m = ARDOUR_UI::instance()->primary_clock.mode ();
1992 m = ARDOUR_UI::instance()->secondary_clock.mode ();
1996 case AudioClock::BBT:
1997 session->bbt_time (start, sbbt);
1998 session->bbt_time (end, ebbt);
2001 /* XXX this computation won't work well if the
2002 user makes a selection that spans any meter changes.
2005 ebbt.bars -= sbbt.bars;
2006 if (ebbt.beats >= sbbt.beats) {
2007 ebbt.beats -= sbbt.beats;
2010 ebbt.beats = int(meter_at_start.beats_per_bar()) + ebbt.beats - sbbt.beats;
2012 if (ebbt.ticks >= sbbt.ticks) {
2013 ebbt.ticks -= sbbt.ticks;
2016 ebbt.ticks = int(Meter::ticks_per_beat) + ebbt.ticks - sbbt.ticks;
2019 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, ebbt.bars, ebbt.beats, ebbt.ticks);
2022 case AudioClock::Timecode:
2023 session->timecode_duration (end - start, timecode);
2024 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
2027 case AudioClock::MinSec:
2028 /* XXX this stuff should be elsewhere.. */
2029 distance = end - start;
2030 frame_rate = session->frame_rate();
2031 hours = distance / (frame_rate * 3600);
2032 distance = distance % (frame_rate * 3600);
2033 mins = distance / (frame_rate * 60);
2034 distance = distance % (frame_rate * 60);
2035 secs = (float) distance / (float) frame_rate;
2036 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
2040 snprintf (buf, sizeof(buf), "%" PRIi64, end - start);
2044 if (xpos >= 0 && ypos >=0) {
2045 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
2048 set_verbose_canvas_cursor (buf, _drag->current_pointer_x() + offset, _drag->current_pointer_y() + offset);
2051 show_verbose_canvas_cursor ();
2055 Editor::collect_new_region_view (RegionView* rv)
2057 latest_regionviews.push_back (rv);
2061 Editor::collect_and_select_new_region_view (RegionView* rv)
2064 latest_regionviews.push_back (rv);
2068 Editor::cancel_selection ()
2070 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2071 (*i)->hide_selection ();
2074 selection->clear ();
2075 clicked_selection = 0;
2080 Editor::single_contents_trim (RegionView& rv, nframes64_t frame_delta, bool left_direction, bool swap_direction, bool obey_snap)
2082 boost::shared_ptr<Region> region (rv.region());
2084 if (region->locked()) {
2088 nframes64_t new_bound;
2091 TimeAxisView* tvp = clicked_axisview;
2092 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2094 if (tv && tv->is_track()) {
2095 speed = tv->get_diskstream()->speed();
2098 if (left_direction) {
2099 if (swap_direction) {
2100 new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
2102 new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
2105 if (swap_direction) {
2106 new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
2108 new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
2113 snap_to (new_bound);
2115 region->trim_start ((nframes64_t) (new_bound * speed), this);
2116 rv.region_changed (StartChanged);
2120 Editor::single_start_trim (RegionView& rv, nframes64_t frame_delta, bool left_direction, bool obey_snap, bool no_overlap)
2122 boost::shared_ptr<Region> region (rv.region());
2124 if (region->locked()) {
2128 nframes64_t new_bound;
2131 TimeAxisView* tvp = clicked_axisview;
2132 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2134 if (tv && tv->is_track()) {
2135 speed = tv->get_diskstream()->speed();
2138 if (left_direction) {
2139 new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
2141 new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
2145 snap_to (new_bound, (left_direction ? 0 : 1));
2148 nframes64_t pre_trim_first_frame = region->first_frame();
2150 region->trim_front ((nframes64_t) (new_bound * speed), this);
2153 //Get the next region on the left of this region and shrink/expand it.
2154 boost::shared_ptr<Playlist> playlist (region->playlist());
2155 boost::shared_ptr<Region> region_left = playlist->find_next_region (pre_trim_first_frame, End, 0);
2157 bool regions_touching = false;
2159 if (region_left != 0 && (pre_trim_first_frame == region_left->last_frame() + 1)){
2160 regions_touching = true;
2163 //Only trim region on the left if the first frame has gone beyond the left region's last frame.
2164 if (region_left != 0 &&
2165 (region_left->last_frame() > region->first_frame() || regions_touching))
2167 region_left->trim_end(region->first_frame(), this);
2173 rv.region_changed (Change (LengthChanged|PositionChanged|StartChanged));
2177 Editor::single_end_trim (RegionView& rv, nframes64_t frame_delta, bool left_direction, bool obey_snap, bool no_overlap)
2179 boost::shared_ptr<Region> region (rv.region());
2181 if (region->locked()) {
2185 nframes64_t new_bound;
2188 TimeAxisView* tvp = clicked_axisview;
2189 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2191 if (tv && tv->is_track()) {
2192 speed = tv->get_diskstream()->speed();
2195 if (left_direction) {
2196 new_bound = (nframes64_t) ((region->last_frame() + 1)/speed) - frame_delta;
2198 new_bound = (nframes64_t) ((region->last_frame() + 1)/speed) + frame_delta;
2202 snap_to (new_bound);
2205 nframes64_t pre_trim_last_frame = region->last_frame();
2207 region->trim_end ((nframes64_t) (new_bound * speed), this);
2210 //Get the next region on the right of this region and shrink/expand it.
2211 boost::shared_ptr<Playlist> playlist (region->playlist());
2212 boost::shared_ptr<Region> region_right = playlist->find_next_region (pre_trim_last_frame, Start, 1);
2214 bool regions_touching = false;
2216 if (region_right != 0 && (pre_trim_last_frame == region_right->first_frame() - 1)){
2217 regions_touching = true;
2220 //Only trim region on the right if the last frame has gone beyond the right region's first frame.
2221 if (region_right != 0 &&
2222 (region_right->first_frame() < region->last_frame() || regions_touching))
2224 region_right->trim_front(region->last_frame() + 1, this);
2227 rv.region_changed (Change (LengthChanged|PositionChanged|StartChanged));
2230 rv.region_changed (LengthChanged);
2236 Editor::point_trim (GdkEvent* event)
2238 RegionView* rv = clicked_regionview;
2240 nframes64_t new_bound = _drag->current_pointer_frame();
2242 snap_to_with_modifier (new_bound, event);
2244 /* Choose action dependant on which button was pressed */
2245 switch (event->button.button) {
2247 begin_reversible_command (_("Start point trim"));
2249 if (selection->selected (rv)) {
2250 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
2251 i != selection->regions.by_layer().end(); ++i)
2254 cerr << "region view contains null region" << endl;
2257 if (!(*i)->region()->locked()) {
2258 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
2259 XMLNode &before = pl->get_state();
2261 (*i)->region()->trim_front (new_bound, this);
2263 XMLNode &after = pl->get_state();
2264 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
2269 if (!rv->region()->locked()) {
2270 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
2271 XMLNode &before = pl->get_state();
2272 rv->region()->trim_front (new_bound, this);
2273 XMLNode &after = pl->get_state();
2274 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
2278 commit_reversible_command();
2282 begin_reversible_command (_("End point trim"));
2284 if (selection->selected (rv)) {
2286 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
2288 if (!(*i)->region()->locked()) {
2289 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
2290 XMLNode &before = pl->get_state();
2291 (*i)->region()->trim_end (new_bound, this);
2292 XMLNode &after = pl->get_state();
2293 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
2299 if (!rv->region()->locked()) {
2300 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
2301 XMLNode &before = pl->get_state();
2302 rv->region()->trim_end (new_bound, this);
2303 XMLNode &after = pl->get_state();
2304 session->add_command (new MementoCommand<Playlist>(*pl.get(), &before, &after));
2308 commit_reversible_command();
2317 Editor::thaw_region_after_trim (RegionView& rv)
2319 boost::shared_ptr<Region> region (rv.region());
2321 if (region->locked()) {
2325 region->thaw (_("trimmed region"));
2327 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(&rv);
2330 arv->unhide_envelope ();
2335 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* /*event*/)
2340 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2341 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2345 Location* location = find_location_from_marker (marker, is_start);
2346 location->set_hidden (true, this);
2351 Editor::reposition_zoom_rect (nframes64_t start, nframes64_t end)
2353 double x1 = frame_to_pixel (start);
2354 double x2 = frame_to_pixel (end);
2355 double y2 = full_canvas_height - 1.0;
2357 zoom_rect->property_x1() = x1;
2358 zoom_rect->property_y1() = 1.0;
2359 zoom_rect->property_x2() = x2;
2360 zoom_rect->property_y2() = y2;
2365 Editor::mouse_rename_region (ArdourCanvas::Item* /*item*/, GdkEvent* /*event*/)
2367 using namespace Gtkmm2ext;
2369 ArdourPrompter prompter (false);
2371 prompter.set_prompt (_("Name for region:"));
2372 prompter.set_initial_text (clicked_regionview->region()->name());
2373 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
2374 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
2375 prompter.show_all ();
2376 switch (prompter.run ()) {
2377 case Gtk::RESPONSE_ACCEPT:
2379 prompter.get_result(str);
2381 clicked_regionview->region()->set_name (str);
2390 Editor::mouse_brush_insert_region (RegionView* rv, nframes64_t pos)
2392 /* no brushing without a useful snap setting */
2394 switch (_snap_mode) {
2396 return; /* can't work because it allows region to be placed anywhere */
2401 switch (_snap_type) {
2409 /* don't brush a copy over the original */
2411 if (pos == rv->region()->position()) {
2415 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
2417 if (rtv == 0 || !rtv->is_track()) {
2421 boost::shared_ptr<Playlist> playlist = rtv->playlist();
2422 double speed = rtv->get_diskstream()->speed();
2424 XMLNode &before = playlist->get_state();
2425 playlist->add_region (RegionFactory::create (rv->region()), (nframes64_t) (pos * speed));
2426 XMLNode &after = playlist->get_state();
2427 session->add_command(new MementoCommand<Playlist>(*playlist.get(), &before, &after));
2429 // playlist is frozen, so we have to update manually
2431 playlist->Modified(); /* EMIT SIGNAL */
2435 Editor::track_height_step_timeout ()
2437 if (get_microseconds() - last_track_height_step_timestamp < 250000) {
2438 current_stepping_trackview = 0;
2445 Editor::start_region_grab (ArdourCanvas::Item* item, GdkEvent* event, RegionView* region_view)
2447 assert (region_view);
2449 _region_motion_group->raise_to_top ();
2451 assert (_drag == 0);
2453 if (Config->get_edit_mode() == Splice) {
2454 _drag = new RegionSpliceDrag (this, item, region_view, selection->regions.by_layer());
2456 RegionSelection s = get_equivalent_regions (selection->regions, RouteGroup::Edit);
2457 _drag = new RegionMoveDrag (this, item, region_view, s.by_layer(), false, false);
2460 _drag->start_grab (event);
2462 begin_reversible_command (_("move region(s)"));
2464 /* sync the canvas to what we think is its current state */
2465 update_canvas_now();
2469 Editor::start_region_copy_grab (ArdourCanvas::Item* item, GdkEvent* event, RegionView* region_view)
2471 assert (region_view);
2472 assert (_drag == 0);
2474 _region_motion_group->raise_to_top ();
2476 RegionSelection s = get_equivalent_regions (selection->regions, RouteGroup::Edit);
2477 _drag = new RegionMoveDrag (this, item, region_view, s.by_layer(), false, true);
2478 _drag->start_grab(event);
2482 Editor::start_region_brush_grab (ArdourCanvas::Item* item, GdkEvent* event, RegionView* region_view)
2484 assert (region_view);
2485 assert (_drag == 0);
2487 if (Config->get_edit_mode() == Splice) {
2491 RegionSelection s = get_equivalent_regions (selection->regions, RouteGroup::Edit);
2492 _drag = new RegionMoveDrag (this, item, region_view, s.by_layer(), true, false);
2493 _drag->start_grab (event);
2495 begin_reversible_command (_("Drag region brush"));
2498 /** Start a grab where a time range is selected, track(s) are selected, and the
2499 * user clicks and drags a region with a modifier in order to create a new region containing
2500 * the section of the clicked region that lies within the time range.
2503 Editor::start_selection_grab (ArdourCanvas::Item* /*item*/, GdkEvent* event)
2505 if (clicked_regionview == 0) {
2509 /* lets try to create new Region for the selection */
2511 vector<boost::shared_ptr<Region> > new_regions;
2512 create_region_from_selection (new_regions);
2514 if (new_regions.empty()) {
2518 /* XXX fix me one day to use all new regions */
2520 boost::shared_ptr<Region> region (new_regions.front());
2522 /* add it to the current stream/playlist.
2524 tricky: the streamview for the track will add a new regionview. we will
2525 catch the signal it sends when it creates the regionview to
2526 set the regionview we want to then drag.
2529 latest_regionviews.clear();
2530 sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
2532 /* A selection grab currently creates two undo/redo operations, one for
2533 creating the new region and another for moving it.
2536 begin_reversible_command (_("selection grab"));
2538 boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
2540 XMLNode *before = &(playlist->get_state());
2541 clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
2542 XMLNode *after = &(playlist->get_state());
2543 session->add_command(new MementoCommand<Playlist>(*playlist, before, after));
2545 commit_reversible_command ();
2549 if (latest_regionviews.empty()) {
2550 /* something went wrong */
2554 /* we need to deselect all other regionviews, and select this one
2555 i'm ignoring undo stuff, because the region creation will take care of it
2557 selection->set (latest_regionviews);
2559 assert (_drag == 0);
2560 _drag = new RegionMoveDrag (this, latest_regionviews.front()->get_canvas_group(), latest_regionviews.front(), latest_regionviews, false, false);
2561 _drag->start_grab (event);
2565 Editor::break_drag ()
2568 _drag->break_drag ();
2573 Editor::set_internal_edit (bool yn)
2575 _internal_editing = yn;
2578 mouse_select_button.set_image (*(manage (new Image (::get_icon("midi_tool_pencil")))));
2579 mouse_select_button.get_image ()->show ();
2581 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2582 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (*i);
2584 mtv->start_step_editing ();
2587 start_step_editing ();
2591 mouse_select_button.set_image (*(manage (new Image (::get_icon("tool_range")))));
2592 mouse_select_button.get_image ()->show ();
2593 stop_step_editing ();
2595 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2596 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (*i);
2598 mtv->stop_step_editing ();
2603 set_canvas_cursor ();