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 <gtkmm2ext/utils.h>
30 #include <gtkmm2ext/tearoff.h>
31 #include "pbd/memento_command.h"
32 #include "pbd/basename.h"
34 #include "ardour_ui.h"
36 #include "time_axis_view.h"
37 #include "audio_time_axis.h"
38 #include "audio_region_view.h"
39 #include "midi_region_view.h"
41 #include "streamview.h"
42 #include "region_gain_line.h"
43 #include "automation_time_axis.h"
44 #include "control_point.h"
47 #include "selection.h"
50 #include "rgb_macros.h"
51 #include "control_point_dialog.h"
52 #include "editor_drag.h"
54 #include "ardour/types.h"
55 #include "ardour/profile.h"
56 #include "ardour/route.h"
57 #include "ardour/audio_track.h"
58 #include "ardour/audio_diskstream.h"
59 #include "ardour/midi_diskstream.h"
60 #include "ardour/playlist.h"
61 #include "ardour/audioplaylist.h"
62 #include "ardour/audioregion.h"
63 #include "ardour/midi_region.h"
64 #include "ardour/dB.h"
65 #include "ardour/utils.h"
66 #include "ardour/region_factory.h"
67 #include "ardour/source_factory.h"
74 using namespace ARDOUR;
78 using namespace Editing;
81 Editor::mouse_frame (nframes64_t& where, bool& in_track_canvas) const
85 Gdk::ModifierType mask;
86 Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->track_canvas->get_window();
87 Glib::RefPtr<const Gdk::Window> pointer_window;
93 pointer_window = canvas_window->get_pointer (x, y, mask);
95 if (pointer_window == track_canvas->get_bin_window()) {
98 in_track_canvas = true;
101 in_track_canvas = false;
106 event.type = GDK_BUTTON_RELEASE;
110 where = event_frame (&event, 0, 0);
115 Editor::event_frame (GdkEvent const * event, double* pcx, double* pcy) const
129 switch (event->type) {
130 case GDK_BUTTON_RELEASE:
131 case GDK_BUTTON_PRESS:
132 case GDK_2BUTTON_PRESS:
133 case GDK_3BUTTON_PRESS:
135 *pcx = event->button.x;
136 *pcy = event->button.y;
137 _trackview_group->w2i(*pcx, *pcy);
139 case GDK_MOTION_NOTIFY:
141 *pcx = event->motion.x;
142 *pcy = event->motion.y;
143 _trackview_group->w2i(*pcx, *pcy);
145 case GDK_ENTER_NOTIFY:
146 case GDK_LEAVE_NOTIFY:
147 track_canvas->w2c(event->crossing.x, event->crossing.y, *pcx, *pcy);
150 case GDK_KEY_RELEASE:
151 // track_canvas->w2c(event->key.x, event->key.y, *pcx, *pcy);
154 warning << string_compose (_("Editor::event_frame() used on unhandled event type %1"), event->type) << endmsg;
158 /* note that pixel_to_frame() never returns less than zero, so even if the pixel
159 position is negative (as can be the case with motion events in particular),
160 the frame location is always positive.
163 return pixel_to_frame (*pcx);
167 Editor::mouse_mode_toggled (MouseMode m)
169 if (ignore_mouse_mode_toggle) {
175 if (mouse_select_button.get_active()) {
181 if (mouse_move_button.get_active()) {
187 if (mouse_gain_button.get_active()) {
193 if (mouse_zoom_button.get_active()) {
199 if (mouse_timefx_button.get_active()) {
205 if (mouse_audition_button.get_active()) {
211 if (mouse_note_button.get_active()) {
222 Editor::which_grabber_cursor ()
224 switch (_edit_point) {
226 return grabber_edit_point_cursor;
231 return grabber_cursor;
235 Editor::set_canvas_cursor ()
237 switch (mouse_mode) {
239 current_canvas_cursor = selector_cursor;
243 current_canvas_cursor = which_grabber_cursor();
247 current_canvas_cursor = cross_hair_cursor;
251 current_canvas_cursor = zoom_cursor;
255 current_canvas_cursor = time_fx_cursor; // just use playhead
259 current_canvas_cursor = speaker_cursor;
263 set_midi_edit_cursor (current_midi_edit_mode());
268 track_canvas->get_window()->set_cursor(*current_canvas_cursor);
273 Editor::set_mouse_mode (MouseMode m, bool force)
279 if (!force && m == mouse_mode) {
287 if (mouse_mode != MouseRange) {
289 /* in all modes except range, hide the range selection,
290 show the object (region) selection.
293 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
294 (*i)->set_should_show_selection (true);
296 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
297 (*i)->hide_selection ();
303 in range mode,show the range selection.
306 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
307 if ((*i)->get_selected()) {
308 (*i)->show_selection (selection->time);
313 /* XXX the hack of unsetting all other buttons should go
314 away once GTK2 allows us to use regular radio buttons drawn like
315 normal buttons, rather than my silly GroupedButton hack.
318 ignore_mouse_mode_toggle = true;
320 switch (mouse_mode) {
322 mouse_select_button.set_active (true);
326 mouse_move_button.set_active (true);
330 mouse_gain_button.set_active (true);
334 mouse_zoom_button.set_active (true);
338 mouse_timefx_button.set_active (true);
342 mouse_audition_button.set_active (true);
346 mouse_note_button.set_active (true);
347 set_midi_edit_cursor (current_midi_edit_mode());
351 if (midi_tools_tearoff) {
352 if (mouse_mode == MouseNote) {
353 midi_tools_tearoff->show();
355 midi_tools_tearoff->hide();
359 ignore_mouse_mode_toggle = false;
361 set_canvas_cursor ();
365 Editor::step_mouse_mode (bool next)
367 switch (current_mouse_mode()) {
370 if (Profile->get_sae()) {
371 set_mouse_mode (MouseZoom);
373 set_mouse_mode (MouseRange);
376 set_mouse_mode (MouseTimeFX);
381 if (next) set_mouse_mode (MouseZoom);
382 else set_mouse_mode (MouseObject);
387 if (Profile->get_sae()) {
388 set_mouse_mode (MouseTimeFX);
390 set_mouse_mode (MouseGain);
393 if (Profile->get_sae()) {
394 set_mouse_mode (MouseObject);
396 set_mouse_mode (MouseRange);
402 if (next) set_mouse_mode (MouseTimeFX);
403 else set_mouse_mode (MouseZoom);
408 set_mouse_mode (MouseAudition);
410 if (Profile->get_sae()) {
411 set_mouse_mode (MouseZoom);
413 set_mouse_mode (MouseGain);
419 if (next) set_mouse_mode (MouseObject);
420 else set_mouse_mode (MouseTimeFX);
424 if (next) set_mouse_mode (MouseObject);
425 else set_mouse_mode (MouseAudition);
431 Editor::midi_edit_mode_toggled (MidiEditMode m)
433 if (ignore_midi_edit_mode_toggle) {
439 if (midi_tool_pencil_button.get_active())
440 set_midi_edit_mode (m);
444 if (midi_tool_select_button.get_active())
445 set_midi_edit_mode (m);
449 if (midi_tool_resize_button.get_active())
450 set_midi_edit_mode (m);
454 if (midi_tool_erase_button.get_active())
455 set_midi_edit_mode (m);
462 set_midi_edit_cursor(m);
467 Editor::set_midi_edit_mode (MidiEditMode m, bool force)
473 if (!force && m == midi_edit_mode) {
481 ignore_midi_edit_mode_toggle = true;
483 switch (midi_edit_mode) {
485 midi_tool_pencil_button.set_active (true);
489 midi_tool_select_button.set_active (true);
493 midi_tool_resize_button.set_active (true);
497 midi_tool_erase_button.set_active (true);
501 ignore_midi_edit_mode_toggle = false;
503 set_midi_edit_cursor (current_midi_edit_mode());
506 track_canvas->get_window()->set_cursor(*current_canvas_cursor);
511 Editor::set_midi_edit_cursor (MidiEditMode m)
513 switch (midi_edit_mode) {
515 current_canvas_cursor = midi_pencil_cursor;
519 current_canvas_cursor = midi_select_cursor;
523 current_canvas_cursor = midi_resize_cursor;
527 current_canvas_cursor = midi_erase_cursor;
533 Editor::button_selection (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
535 /* in object/audition/timefx/gain-automation mode,
536 any button press sets the selection if the object
537 can be selected. this is a bit of hack, because
538 we want to avoid this if the mouse operation is a
541 note: not dbl-click or triple-click
544 if (((mouse_mode != MouseObject) &&
545 (mouse_mode != MouseAudition || item_type != RegionItem) &&
546 (mouse_mode != MouseTimeFX || item_type != RegionItem) &&
547 (mouse_mode != MouseGain) &&
548 (mouse_mode != MouseRange)) ||
550 ((event->type != GDK_BUTTON_PRESS && event->type != GDK_BUTTON_RELEASE) || event->button.button > 3)) {
555 if (event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) {
557 if ((event->button.state & Keyboard::RelevantModifierKeyMask) && event->button.button != 1) {
559 /* almost no selection action on modified button-2 or button-3 events */
561 if (item_type != RegionItem && event->button.button != 2) {
567 Selection::Operation op = Keyboard::selection_type (event->button.state);
568 bool press = (event->type == GDK_BUTTON_PRESS);
570 // begin_reversible_command (_("select on click"));
574 if (mouse_mode != MouseRange) {
575 set_selected_regionview_from_click (press, op, true);
576 } else if (event->type == GDK_BUTTON_PRESS) {
577 set_selected_track_as_side_effect ();
581 case RegionViewNameHighlight:
583 if (mouse_mode != MouseRange) {
584 set_selected_regionview_from_click (press, op, true);
585 } else if (event->type == GDK_BUTTON_PRESS) {
586 set_selected_track_as_side_effect ();
591 case FadeInHandleItem:
593 case FadeOutHandleItem:
595 if (mouse_mode != MouseRange) {
596 set_selected_regionview_from_click (press, op, true);
597 } else if (event->type == GDK_BUTTON_PRESS) {
598 set_selected_track_as_side_effect ();
602 case ControlPointItem:
603 set_selected_track_as_side_effect ();
604 if (mouse_mode != MouseRange) {
605 set_selected_control_point_from_click (op, false);
610 /* for context click or range selection, select track */
611 if (event->button.button == 3) {
612 set_selected_track_as_side_effect ();
613 } else if (event->type == GDK_BUTTON_PRESS && mouse_mode == MouseRange) {
614 set_selected_track_as_side_effect ();
618 case AutomationTrackItem:
619 set_selected_track_as_side_effect (true);
628 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
630 Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->track_canvas->get_window();
633 Glib::RefPtr<const Gdk::Window> pointer_window;
636 Gdk::ModifierType mask;
638 pointer_window = canvas_window->get_pointer (x, y, mask);
640 if (pointer_window == track_canvas->get_bin_window()) {
641 track_canvas->window_to_world (x, y, wx, wy);
642 allow_vertical_scroll = true;
644 allow_vertical_scroll = false;
648 track_canvas->grab_focus();
650 if (session && session->actively_recording()) {
654 button_selection (item, event, item_type);
657 (Keyboard::is_delete_event (&event->button) ||
658 Keyboard::is_context_menu_event (&event->button) ||
659 Keyboard::is_edit_event (&event->button))) {
661 /* handled by button release */
665 switch (event->button.button) {
668 if (event->type == GDK_BUTTON_PRESS) {
671 _drag->item()->ungrab (event->button.time);
674 /* single mouse clicks on any of these item types operate
675 independent of mouse mode, mostly because they are
676 not on the main track canvas or because we want
681 case PlayheadCursorItem:
683 _drag = new CursorDrag (this, item, true);
684 _drag->start_grab (event);
688 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
689 hide_marker (item, event);
692 _drag = new MarkerDrag (this, item);
693 _drag->start_grab (event);
697 case TempoMarkerItem:
699 _drag = new TempoMarkerDrag (
702 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
704 _drag->start_grab (event);
707 case MeterMarkerItem:
710 _drag = new MeterMarkerDrag (
713 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
716 _drag->start_grab (event);
722 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
724 _drag = new CursorDrag (this, &playhead_cursor->canvas_item, false);
725 _drag->start_grab (event);
731 case RangeMarkerBarItem:
733 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
734 _drag = new CursorDrag (this, &playhead_cursor->canvas_item, false);
736 _drag = new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateRangeMarker);
738 _drag->start_grab (event);
742 case CdMarkerBarItem:
744 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
745 _drag = new CursorDrag (this, &playhead_cursor->canvas_item, false);
747 _drag = new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateCDMarker);
749 _drag->start_grab (event);
753 case TransportMarkerBarItem:
755 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
756 _drag = new CursorDrag (this, &playhead_cursor->canvas_item, false);
758 _drag = new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateTransportMarker);
760 _drag->start_grab (event);
769 switch (mouse_mode) {
772 case StartSelectionTrimItem:
774 _drag = new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim);
775 _drag->start_grab (event);
778 case EndSelectionTrimItem:
780 _drag = new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim);
781 _drag->start_grab (event);
785 if (Keyboard::modifier_state_contains
786 (event->button.state, Keyboard::ModifierMask(Keyboard::SecondaryModifier))) {
787 // contains and not equals because I can't use alt as a modifier alone.
788 start_selection_grab (item, event);
789 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
790 /* grab selection for moving */
792 _drag = new SelectionDrag (this, item, SelectionDrag::SelectionMove);
793 _drag->start_grab (event);
795 /* this was debated, but decided the more common action was to
796 make a new selection */
798 _drag = new SelectionDrag (this, item, SelectionDrag::CreateSelection);
799 _drag->start_grab (event);
805 _drag = new SelectionDrag (this, item, SelectionDrag::CreateSelection);
806 _drag->start_grab (event);
812 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)) &&
813 event->type == GDK_BUTTON_PRESS) {
816 _drag = new RubberbandSelectDrag (this, item);
817 _drag->start_grab (event);
819 } else if (event->type == GDK_BUTTON_PRESS) {
822 case FadeInHandleItem:
824 _drag = new FadeInDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), selection->regions);
825 _drag->start_grab (event);
828 case FadeOutHandleItem:
830 _drag = new FadeOutDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), selection->regions);
831 _drag->start_grab (event);
835 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
836 start_region_copy_grab (item, event, clicked_regionview);
837 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
838 start_region_brush_grab (item, event, clicked_regionview);
840 start_region_grab (item, event, clicked_regionview);
844 case RegionViewNameHighlight:
846 _drag = new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer());
847 _drag->start_grab (event);
852 /* rename happens on edit clicks */
854 _drag = new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer());
855 _drag->start_grab (event);
859 case ControlPointItem:
861 _drag = new ControlPointDrag (this, item);
862 _drag->start_grab (event);
866 case AutomationLineItem:
868 _drag = new LineDrag (this, item);
869 _drag->start_grab (event);
874 case AutomationTrackItem:
876 _drag = new RubberbandSelectDrag (this, item);
877 _drag->start_grab (event);
881 case ImageFrameHandleStartItem:
882 imageframe_start_handle_op(item, event) ;
885 case ImageFrameHandleEndItem:
886 imageframe_end_handle_op(item, event) ;
889 case MarkerViewHandleStartItem:
890 markerview_item_start_handle_op(item, event) ;
893 case MarkerViewHandleEndItem:
894 markerview_item_end_handle_op(item, event) ;
898 start_markerview_grab(item, event) ;
901 start_imageframe_grab(item, event) ;
919 /* start a grab so that if we finish after moving
920 we can tell what happened.
923 _drag = new RegionGainDrag (this, item);
924 _drag->start_grab (event, current_canvas_cursor);
929 _drag = new LineDrag (this, item);
930 _drag->start_grab (event);
933 case ControlPointItem:
935 _drag = new ControlPointDrag (this, item);
936 _drag->start_grab (event);
947 case ControlPointItem:
949 _drag = new ControlPointDrag (this, item);
950 _drag->start_grab (event);
953 case AutomationLineItem:
955 _drag = new LineDrag (this, item);
956 _drag->start_grab (event);
960 // XXX need automation mode to identify which
962 // start_line_grab_from_regionview (item, event);
972 if (event->type == GDK_BUTTON_PRESS) {
974 _drag = new MouseZoomDrag (this, item);
975 _drag->start_grab (event);
982 if (item_type == RegionItem) {
984 _drag = new TimeFXDrag (this, item, clicked_regionview, selection->regions.by_layer());
985 _drag->start_grab (event);
990 _drag = new ScrubDrag (this, item);
991 _drag->start_grab (event);
993 scrub_reverse_distance = 0;
994 last_scrub_x = event->button.x;
995 scrubbing_direction = 0;
996 track_canvas->get_window()->set_cursor (*transparent_cursor);
1000 assert (_drag == 0);
1001 _drag = new RegionCreateDrag (this, item, clicked_axisview);
1002 _drag->start_grab (event);
1011 switch (mouse_mode) {
1013 if (event->type == GDK_BUTTON_PRESS) {
1014 switch (item_type) {
1016 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
1017 start_region_copy_grab (item, event, clicked_regionview);
1019 start_region_grab (item, event, clicked_regionview);
1023 case ControlPointItem:
1024 assert (_drag == 0);
1025 _drag = new ControlPointDrag (this, item);
1026 _drag->start_grab (event);
1036 switch (item_type) {
1037 case RegionViewNameHighlight:
1038 assert (_drag == 0);
1039 _drag = new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer());
1040 _drag->start_grab (event);
1044 case RegionViewName:
1045 assert (_drag == 0);
1046 _drag = new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer());
1047 _drag->start_grab (event);
1058 if (event->type == GDK_BUTTON_PRESS) {
1059 /* relax till release */
1066 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1067 temporal_zoom_session();
1069 temporal_zoom_to_frame (true, event_frame(event));
1092 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1094 nframes64_t where = event_frame (event, 0, 0);
1095 AutomationTimeAxisView* atv = 0;
1097 /* no action if we're recording */
1099 if (session && session->actively_recording()) {
1103 /* first, see if we're finishing a drag ... */
1105 bool were_dragging = false;
1107 bool const r = _drag->end_grab (event);
1111 /* grab dragged, so do nothing else */
1115 were_dragging = true;
1118 button_selection (item, event, item_type);
1120 /* edit events get handled here */
1122 if (_drag == 0 && Keyboard::is_edit_event (&event->button)) {
1123 switch (item_type) {
1128 case TempoMarkerItem:
1129 edit_tempo_marker (item);
1132 case MeterMarkerItem:
1133 edit_meter_marker (item);
1136 case RegionViewName:
1137 if (clicked_regionview->name_active()) {
1138 return mouse_rename_region (item, event);
1142 case ControlPointItem:
1143 edit_control_point (item);
1152 /* context menu events get handled here */
1154 if (Keyboard::is_context_menu_event (&event->button)) {
1158 /* no matter which button pops up the context menu, tell the menu
1159 widget to use button 1 to drive menu selection.
1162 switch (item_type) {
1164 case FadeInHandleItem:
1166 case FadeOutHandleItem:
1167 popup_fade_context_menu (1, event->button.time, item, item_type);
1171 popup_track_context_menu (1, event->button.time, item_type, false, where);
1175 case RegionViewNameHighlight:
1176 case RegionViewName:
1177 popup_track_context_menu (1, event->button.time, item_type, false, where);
1181 popup_track_context_menu (1, event->button.time, item_type, true, where);
1184 case AutomationTrackItem:
1185 popup_track_context_menu (1, event->button.time, item_type, false, where);
1189 case RangeMarkerBarItem:
1190 case TransportMarkerBarItem:
1191 case CdMarkerBarItem:
1194 popup_ruler_menu (where, item_type);
1198 marker_context_menu (&event->button, item);
1201 case TempoMarkerItem:
1202 tm_marker_context_menu (&event->button, item);
1205 case MeterMarkerItem:
1206 tm_marker_context_menu (&event->button, item);
1209 case CrossfadeViewItem:
1210 popup_track_context_menu (1, event->button.time, item_type, false, where);
1214 case ImageFrameItem:
1215 popup_imageframe_edit_menu(1, event->button.time, item, true) ;
1217 case ImageFrameTimeAxisItem:
1218 popup_imageframe_edit_menu(1, event->button.time, item, false) ;
1220 case MarkerViewItem:
1221 popup_marker_time_axis_edit_menu(1, event->button.time, item, true) ;
1223 case MarkerTimeAxisItem:
1224 popup_marker_time_axis_edit_menu(1, event->button.time, item, false) ;
1236 /* delete events get handled here */
1238 if (_drag == 0 && Keyboard::is_delete_event (&event->button)) {
1240 switch (item_type) {
1241 case TempoMarkerItem:
1242 remove_tempo_marker (item);
1245 case MeterMarkerItem:
1246 remove_meter_marker (item);
1250 remove_marker (*item, event);
1254 if (mouse_mode == MouseObject) {
1255 remove_clicked_region ();
1259 case ControlPointItem:
1260 if (mouse_mode == MouseGain) {
1261 remove_gain_control_point (item, event);
1263 remove_control_point (item, event);
1273 switch (event->button.button) {
1276 switch (item_type) {
1277 /* see comments in button_press_handler */
1278 case PlayheadCursorItem:
1281 case AutomationLineItem:
1282 case StartSelectionTrimItem:
1283 case EndSelectionTrimItem:
1287 if (!_dragging_playhead) {
1288 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1289 snap_to (where, 0, true);
1291 mouse_add_new_marker (where);
1295 case CdMarkerBarItem:
1296 if (!_dragging_playhead) {
1297 // if we get here then a dragged range wasn't done
1298 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1299 snap_to (where, 0, true);
1301 mouse_add_new_marker (where, true);
1306 if (!_dragging_playhead) {
1307 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1310 mouse_add_new_tempo_event (where);
1315 if (!_dragging_playhead) {
1316 mouse_add_new_meter_event (pixel_to_frame (event->button.x));
1325 switch (mouse_mode) {
1327 switch (item_type) {
1328 case AutomationTrackItem:
1329 atv = dynamic_cast<AutomationTimeAxisView*>(clicked_axisview);
1331 atv->add_automation_event (item, event, where, event->button.y);
1343 // Gain only makes sense for audio regions
1345 if (!dynamic_cast<AudioRegionView*>(clicked_regionview)) {
1349 switch (item_type) {
1351 /* check that we didn't drag before releasing, since
1352 its really annoying to create new control
1353 points when doing this.
1355 if (were_dragging) {
1356 dynamic_cast<AudioRegionView*>(clicked_regionview)->add_gain_point_event (item, event);
1361 case AutomationTrackItem:
1362 dynamic_cast<AutomationTimeAxisView*>(clicked_axisview)->
1363 add_automation_event (item, event, where, event->button.y);
1372 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1373 if (scrubbing_direction == 0) {
1374 /* no drag, just a click */
1375 switch (item_type) {
1377 play_selected_region ();
1383 /* make sure we stop */
1384 session->request_transport_speed (0.0);
1398 switch (mouse_mode) {
1401 switch (item_type) {
1403 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1405 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier|Keyboard::SecondaryModifier))) {
1408 // Button2 click is unused
1421 // x_style_paste (where, 1.0);
1441 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1447 if (last_item_entered != item) {
1448 last_item_entered = item;
1449 last_item_entered_n = 0;
1452 switch (item_type) {
1453 case ControlPointItem:
1454 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1455 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1456 cp->set_visible (true);
1460 at_y = cp->get_y ();
1461 cp->item()->i2w (at_x, at_y);
1465 fraction = 1.0 - (cp->get_y() / cp->line().height());
1467 if (is_drawable() && dynamic_cast<ScrubDrag*> (_drag) == 0) {
1468 track_canvas->get_window()->set_cursor (*fader_cursor);
1471 last_item_entered_n++;
1472 set_verbose_canvas_cursor (cp->line().get_verbose_cursor_string (fraction), at_x, at_y);
1473 if (last_item_entered_n < 10) {
1474 show_verbose_canvas_cursor ();
1480 if (mouse_mode == MouseGain) {
1481 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1483 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredGainLine.get();
1484 if (is_drawable()) {
1485 track_canvas->get_window()->set_cursor (*fader_cursor);
1490 case AutomationLineItem:
1491 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1493 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1495 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredAutomationLine.get();
1497 if (is_drawable()) {
1498 track_canvas->get_window()->set_cursor (*fader_cursor);
1503 case RegionViewNameHighlight:
1504 if (is_drawable() && mouse_mode == MouseObject) {
1505 track_canvas->get_window()->set_cursor (*trimmer_cursor);
1509 case StartSelectionTrimItem:
1510 case EndSelectionTrimItem:
1513 case ImageFrameHandleStartItem:
1514 case ImageFrameHandleEndItem:
1515 case MarkerViewHandleStartItem:
1516 case MarkerViewHandleEndItem:
1519 if (is_drawable()) {
1520 track_canvas->get_window()->set_cursor (*trimmer_cursor);
1524 case PlayheadCursorItem:
1525 if (is_drawable()) {
1526 switch (_edit_point) {
1528 track_canvas->get_window()->set_cursor (*grabber_edit_point_cursor);
1531 track_canvas->get_window()->set_cursor (*grabber_cursor);
1537 case RegionViewName:
1539 /* when the name is not an active item, the entire name highlight is for trimming */
1541 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1542 if (mouse_mode == MouseObject && is_drawable()) {
1543 track_canvas->get_window()->set_cursor (*trimmer_cursor);
1549 case AutomationTrackItem:
1550 if (is_drawable()) {
1551 Gdk::Cursor *cursor;
1552 switch (mouse_mode) {
1554 cursor = selector_cursor;
1557 cursor = zoom_cursor;
1560 cursor = cross_hair_cursor;
1564 track_canvas->get_window()->set_cursor (*cursor);
1566 AutomationTimeAxisView* atv;
1567 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1568 clear_entered_track = false;
1569 set_entered_track (atv);
1575 case RangeMarkerBarItem:
1576 case TransportMarkerBarItem:
1577 case CdMarkerBarItem:
1580 if (is_drawable()) {
1581 track_canvas->get_window()->set_cursor (*timebar_cursor);
1586 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1589 entered_marker = marker;
1590 marker->set_color_rgba (ARDOUR_UI::config()->canvasvar_EnteredMarker.get());
1592 case MeterMarkerItem:
1593 case TempoMarkerItem:
1594 if (is_drawable()) {
1595 track_canvas->get_window()->set_cursor (*timebar_cursor);
1598 case FadeInHandleItem:
1599 case FadeOutHandleItem:
1600 if (mouse_mode == MouseObject) {
1601 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1603 rect->property_fill_color_rgba() = 0;
1604 rect->property_outline_pixels() = 1;
1613 /* second pass to handle entered track status in a comprehensible way.
1616 switch (item_type) {
1618 case AutomationLineItem:
1619 case ControlPointItem:
1620 /* these do not affect the current entered track state */
1621 clear_entered_track = false;
1624 case AutomationTrackItem:
1625 /* handled above already */
1629 set_entered_track (0);
1637 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1646 switch (item_type) {
1647 case ControlPointItem:
1648 cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
1649 if (cp->line().the_list()->interpolation() != AutomationList::Discrete) {
1650 if (cp->line().npoints() > 1 && !cp->selected()) {
1651 cp->set_visible (false);
1655 if (is_drawable()) {
1656 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1659 hide_verbose_canvas_cursor ();
1662 case RegionViewNameHighlight:
1663 case StartSelectionTrimItem:
1664 case EndSelectionTrimItem:
1665 case PlayheadCursorItem:
1668 case ImageFrameHandleStartItem:
1669 case ImageFrameHandleEndItem:
1670 case MarkerViewHandleStartItem:
1671 case MarkerViewHandleEndItem:
1674 if (is_drawable()) {
1675 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1680 case AutomationLineItem:
1681 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1683 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1685 line->property_fill_color_rgba() = al->get_line_color();
1687 if (is_drawable()) {
1688 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1692 case RegionViewName:
1693 /* see enter_handler() for notes */
1694 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1695 if (is_drawable() && mouse_mode == MouseObject) {
1696 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1701 case RangeMarkerBarItem:
1702 case TransportMarkerBarItem:
1703 case CdMarkerBarItem:
1707 if (is_drawable()) {
1708 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1713 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1717 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
1718 location_flags_changed (loc, this);
1721 case MeterMarkerItem:
1722 case TempoMarkerItem:
1724 if (is_drawable()) {
1725 track_canvas->get_window()->set_cursor (*timebar_cursor);
1730 case FadeInHandleItem:
1731 case FadeOutHandleItem:
1732 rv = static_cast<RegionView*>(item->get_data ("regionview"));
1734 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1736 rect->property_fill_color_rgba() = rv->get_fill_color();
1737 rect->property_outline_pixels() = 0;
1742 case AutomationTrackItem:
1743 if (is_drawable()) {
1744 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1745 clear_entered_track = true;
1746 Glib::signal_idle().connect (mem_fun(*this, &Editor::left_automation_track));
1758 Editor::left_automation_track ()
1760 if (clear_entered_track) {
1761 set_entered_track (0);
1762 clear_entered_track = false;
1772 if (scrubbing_direction == 0) {
1774 session->request_locate (_drag->current_pointer_frame(), false);
1775 session->request_transport_speed (0.1);
1776 scrubbing_direction = 1;
1780 if (last_scrub_x > _drag->current_pointer_x()) {
1782 /* pointer moved to the left */
1784 if (scrubbing_direction > 0) {
1786 /* we reversed direction to go backwards */
1789 scrub_reverse_distance += (int) (last_scrub_x - _drag->current_pointer_x());
1793 /* still moving to the left (backwards) */
1795 scrub_reversals = 0;
1796 scrub_reverse_distance = 0;
1798 delta = 0.01 * (last_scrub_x - _drag->current_pointer_x());
1799 session->request_transport_speed (session->transport_speed() - delta);
1803 /* pointer moved to the right */
1805 if (scrubbing_direction < 0) {
1806 /* we reversed direction to go forward */
1809 scrub_reverse_distance += (int) (_drag->current_pointer_x() - last_scrub_x);
1812 /* still moving to the right */
1814 scrub_reversals = 0;
1815 scrub_reverse_distance = 0;
1817 delta = 0.01 * (_drag->current_pointer_x() - last_scrub_x);
1818 session->request_transport_speed (session->transport_speed() + delta);
1822 /* if there have been more than 2 opposite motion moves detected, or one that moves
1823 back more than 10 pixels, reverse direction
1826 if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
1828 if (scrubbing_direction > 0) {
1829 /* was forwards, go backwards */
1830 session->request_transport_speed (-0.1);
1831 scrubbing_direction = -1;
1833 /* was backwards, go forwards */
1834 session->request_transport_speed (0.1);
1835 scrubbing_direction = 1;
1838 scrub_reverse_distance = 0;
1839 scrub_reversals = 0;
1843 last_scrub_x = _drag->current_pointer_x();
1847 Editor::motion_handler (ArdourCanvas::Item* item, GdkEvent* event, bool from_autoscroll)
1849 if (event->motion.is_hint) {
1852 /* We call this so that MOTION_NOTIFY events continue to be
1853 delivered to the canvas. We need to do this because we set
1854 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
1855 the density of the events, at the expense of a round-trip
1856 to the server. Given that this will mostly occur on cases
1857 where DISPLAY = :0.0, and given the cost of what the motion
1858 event might do, its a good tradeoff.
1861 track_canvas->get_pointer (x, y);
1864 if (current_stepping_trackview) {
1865 /* don't keep the persistent stepped trackview if the mouse moves */
1866 current_stepping_trackview = 0;
1867 step_timeout.disconnect ();
1870 if (session && session->actively_recording()) {
1871 /* Sorry. no dragging stuff around while we record */
1875 bool handled = false;
1877 handled = _drag->motion_handler (event, from_autoscroll);
1884 track_canvas_motion (event);
1889 Editor::remove_gain_control_point (ArdourCanvas::Item*item, GdkEvent* event)
1891 ControlPoint* control_point;
1893 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
1894 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
1898 // We shouldn't remove the first or last gain point
1899 if (control_point->line().is_last_point(*control_point) ||
1900 control_point->line().is_first_point(*control_point)) {
1904 control_point->line().remove_point (*control_point);
1908 Editor::remove_control_point (ArdourCanvas::Item* item, GdkEvent* event)
1910 ControlPoint* control_point;
1912 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
1913 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
1917 control_point->line().remove_point (*control_point);
1921 Editor::edit_control_point (ArdourCanvas::Item* item)
1923 ControlPoint* p = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"));
1926 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
1930 ControlPointDialog d (p);
1931 d.set_position (Gtk::WIN_POS_MOUSE);
1934 if (d.run () != RESPONSE_ACCEPT) {
1938 p->line().modify_point_y (*p, d.get_y_fraction ());
1943 Editor::visible_order_range (int* low, int* high) const
1945 *low = TimeAxisView::max_order ();
1948 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
1950 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
1952 if (!rtv->hidden()) {
1954 if (*high < rtv->order()) {
1955 *high = rtv->order ();
1958 if (*low > rtv->order()) {
1959 *low = rtv->order ();
1966 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
1968 /* Either add to or set the set the region selection, unless
1969 this is an alignment click (control used)
1972 if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
1973 TimeAxisView* tv = &rv.get_time_axis_view();
1974 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
1976 if (rtv && rtv->is_track()) {
1977 speed = rtv->get_diskstream()->speed();
1980 nframes64_t where = get_preferred_edit_position();
1984 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
1986 align_region (rv.region(), SyncPoint, (nframes64_t) (where * speed));
1988 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
1990 align_region (rv.region(), End, (nframes64_t) (where * speed));
1994 align_region (rv.region(), Start, (nframes64_t) (where * speed));
2001 Editor::show_verbose_time_cursor (nframes64_t frame, double offset, double xpos, double ypos)
2007 nframes64_t frame_rate;
2016 if (Profile->get_sae() || Profile->get_small_screen()) {
2017 m = ARDOUR_UI::instance()->primary_clock.mode();
2019 m = ARDOUR_UI::instance()->secondary_clock.mode();
2023 case AudioClock::BBT:
2024 session->bbt_time (frame, bbt);
2025 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, bbt.bars, bbt.beats, bbt.ticks);
2028 case AudioClock::SMPTE:
2029 session->smpte_time (frame, smpte);
2030 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
2033 case AudioClock::MinSec:
2034 /* XXX this is copied from show_verbose_duration_cursor() */
2035 frame_rate = session->frame_rate();
2036 hours = frame / (frame_rate * 3600);
2037 frame = frame % (frame_rate * 3600);
2038 mins = frame / (frame_rate * 60);
2039 frame = frame % (frame_rate * 60);
2040 secs = (float) frame / (float) frame_rate;
2041 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
2045 snprintf (buf, sizeof(buf), "%" PRIi64, frame);
2049 if (xpos >= 0 && ypos >=0) {
2050 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
2053 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);
2055 show_verbose_canvas_cursor ();
2059 Editor::show_verbose_duration_cursor (nframes64_t start, nframes64_t end, double offset, double xpos, double ypos)
2066 nframes64_t distance, frame_rate;
2068 Meter meter_at_start(session->tempo_map().meter_at(start));
2076 if (Profile->get_sae() || Profile->get_small_screen()) {
2077 m = ARDOUR_UI::instance()->primary_clock.mode ();
2079 m = ARDOUR_UI::instance()->secondary_clock.mode ();
2083 case AudioClock::BBT:
2084 session->bbt_time (start, sbbt);
2085 session->bbt_time (end, ebbt);
2088 /* XXX this computation won't work well if the
2089 user makes a selection that spans any meter changes.
2092 ebbt.bars -= sbbt.bars;
2093 if (ebbt.beats >= sbbt.beats) {
2094 ebbt.beats -= sbbt.beats;
2097 ebbt.beats = int(meter_at_start.beats_per_bar()) + ebbt.beats - sbbt.beats;
2099 if (ebbt.ticks >= sbbt.ticks) {
2100 ebbt.ticks -= sbbt.ticks;
2103 ebbt.ticks = int(Meter::ticks_per_beat) + ebbt.ticks - sbbt.ticks;
2106 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, ebbt.bars, ebbt.beats, ebbt.ticks);
2109 case AudioClock::SMPTE:
2110 session->smpte_duration (end - start, smpte);
2111 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
2114 case AudioClock::MinSec:
2115 /* XXX this stuff should be elsewhere.. */
2116 distance = end - start;
2117 frame_rate = session->frame_rate();
2118 hours = distance / (frame_rate * 3600);
2119 distance = distance % (frame_rate * 3600);
2120 mins = distance / (frame_rate * 60);
2121 distance = distance % (frame_rate * 60);
2122 secs = (float) distance / (float) frame_rate;
2123 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
2127 snprintf (buf, sizeof(buf), "%" PRIi64, end - start);
2131 if (xpos >= 0 && ypos >=0) {
2132 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
2135 set_verbose_canvas_cursor (buf, _drag->current_pointer_x() + offset, _drag->current_pointer_y() + offset);
2138 show_verbose_canvas_cursor ();
2142 Editor::collect_new_region_view (RegionView* rv)
2144 latest_regionviews.push_back (rv);
2148 Editor::collect_and_select_new_region_view (RegionView* rv)
2151 latest_regionviews.push_back (rv);
2155 Editor::cancel_selection ()
2157 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2158 (*i)->hide_selection ();
2161 selection->clear ();
2162 clicked_selection = 0;
2167 Editor::single_contents_trim (RegionView& rv, nframes64_t frame_delta, bool left_direction, bool swap_direction, bool obey_snap)
2169 boost::shared_ptr<Region> region (rv.region());
2171 if (region->locked()) {
2175 nframes64_t new_bound;
2178 TimeAxisView* tvp = clicked_axisview;
2179 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2181 if (tv && tv->is_track()) {
2182 speed = tv->get_diskstream()->speed();
2185 if (left_direction) {
2186 if (swap_direction) {
2187 new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
2189 new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
2192 if (swap_direction) {
2193 new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
2195 new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
2200 snap_to (new_bound);
2202 region->trim_start ((nframes64_t) (new_bound * speed), this);
2203 rv.region_changed (StartChanged);
2207 Editor::single_start_trim (RegionView& rv, nframes64_t frame_delta, bool left_direction, bool obey_snap, bool no_overlap)
2209 boost::shared_ptr<Region> region (rv.region());
2211 if (region->locked()) {
2215 nframes64_t new_bound;
2218 TimeAxisView* tvp = clicked_axisview;
2219 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2221 if (tv && tv->is_track()) {
2222 speed = tv->get_diskstream()->speed();
2225 if (left_direction) {
2226 new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
2228 new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
2232 snap_to (new_bound, (left_direction ? 0 : 1));
2235 nframes64_t pre_trim_first_frame = region->first_frame();
2237 region->trim_front ((nframes64_t) (new_bound * speed), this);
2240 //Get the next region on the left of this region and shrink/expand it.
2241 boost::shared_ptr<Playlist> playlist (region->playlist());
2242 boost::shared_ptr<Region> region_left = playlist->find_next_region (pre_trim_first_frame, End, 0);
2244 bool regions_touching = false;
2246 if (region_left != 0 && (pre_trim_first_frame == region_left->last_frame() + 1)){
2247 regions_touching = true;
2250 //Only trim region on the left if the first frame has gone beyond the left region's last frame.
2251 if (region_left != 0 &&
2252 (region_left->last_frame() > region->first_frame() || regions_touching))
2254 region_left->trim_end(region->first_frame(), this);
2260 rv.region_changed (Change (LengthChanged|PositionChanged|StartChanged));
2264 Editor::single_end_trim (RegionView& rv, nframes64_t frame_delta, bool left_direction, bool obey_snap, bool no_overlap)
2266 boost::shared_ptr<Region> region (rv.region());
2268 if (region->locked()) {
2272 nframes64_t new_bound;
2275 TimeAxisView* tvp = clicked_axisview;
2276 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2278 if (tv && tv->is_track()) {
2279 speed = tv->get_diskstream()->speed();
2282 if (left_direction) {
2283 new_bound = (nframes64_t) ((region->last_frame() + 1)/speed) - frame_delta;
2285 new_bound = (nframes64_t) ((region->last_frame() + 1)/speed) + frame_delta;
2289 snap_to (new_bound);
2292 nframes64_t pre_trim_last_frame = region->last_frame();
2294 region->trim_end ((nframes64_t) (new_bound * speed), this);
2297 //Get the next region on the right of this region and shrink/expand it.
2298 boost::shared_ptr<Playlist> playlist (region->playlist());
2299 boost::shared_ptr<Region> region_right = playlist->find_next_region (pre_trim_last_frame, Start, 1);
2301 bool regions_touching = false;
2303 if (region_right != 0 && (pre_trim_last_frame == region_right->first_frame() - 1)){
2304 regions_touching = true;
2307 //Only trim region on the right if the last frame has gone beyond the right region's first frame.
2308 if (region_right != 0 &&
2309 (region_right->first_frame() < region->last_frame() || regions_touching))
2311 region_right->trim_front(region->last_frame() + 1, this);
2314 rv.region_changed (Change (LengthChanged|PositionChanged|StartChanged));
2317 rv.region_changed (LengthChanged);
2323 Editor::point_trim (GdkEvent* event)
2325 RegionView* rv = clicked_regionview;
2327 nframes64_t new_bound = _drag->current_pointer_frame();
2329 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2330 snap_to (new_bound);
2333 /* Choose action dependant on which button was pressed */
2334 switch (event->button.button) {
2336 begin_reversible_command (_("Start point trim"));
2338 if (selection->selected (rv)) {
2339 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
2340 i != selection->regions.by_layer().end(); ++i)
2343 cerr << "region view contains null region" << endl;
2346 if (!(*i)->region()->locked()) {
2347 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
2348 XMLNode &before = pl->get_state();
2350 (*i)->region()->trim_front (new_bound, this);
2352 XMLNode &after = pl->get_state();
2353 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
2358 if (!rv->region()->locked()) {
2359 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
2360 XMLNode &before = pl->get_state();
2361 rv->region()->trim_front (new_bound, this);
2362 XMLNode &after = pl->get_state();
2363 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
2367 commit_reversible_command();
2371 begin_reversible_command (_("End point trim"));
2373 if (selection->selected (rv)) {
2375 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
2377 if (!(*i)->region()->locked()) {
2378 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
2379 XMLNode &before = pl->get_state();
2380 (*i)->region()->trim_end (new_bound, this);
2381 XMLNode &after = pl->get_state();
2382 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
2388 if (!rv->region()->locked()) {
2389 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
2390 XMLNode &before = pl->get_state();
2391 rv->region()->trim_end (new_bound, this);
2392 XMLNode &after = pl->get_state();
2393 session->add_command (new MementoCommand<Playlist>(*pl.get(), &before, &after));
2397 commit_reversible_command();
2406 Editor::thaw_region_after_trim (RegionView& rv)
2408 boost::shared_ptr<Region> region (rv.region());
2410 if (region->locked()) {
2414 region->thaw (_("trimmed region"));
2415 XMLNode &after = region->playlist()->get_state();
2416 session->add_command (new MementoCommand<Playlist>(*(region->playlist()), 0, &after));
2418 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(&rv);
2420 arv->unhide_envelope ();
2424 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* event)
2429 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2430 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2434 Location* location = find_location_from_marker (marker, is_start);
2435 location->set_hidden (true, this);
2440 Editor::reposition_zoom_rect (nframes64_t start, nframes64_t end)
2442 double x1 = frame_to_pixel (start);
2443 double x2 = frame_to_pixel (end);
2444 double y2 = full_canvas_height - 1.0;
2446 zoom_rect->property_x1() = x1;
2447 zoom_rect->property_y1() = 1.0;
2448 zoom_rect->property_x2() = x2;
2449 zoom_rect->property_y2() = y2;
2454 Editor::mouse_rename_region (ArdourCanvas::Item* item, GdkEvent* event)
2456 using namespace Gtkmm2ext;
2458 ArdourPrompter prompter (false);
2460 prompter.set_prompt (_("Name for region:"));
2461 prompter.set_initial_text (clicked_regionview->region()->name());
2462 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
2463 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
2464 prompter.show_all ();
2465 switch (prompter.run ()) {
2466 case Gtk::RESPONSE_ACCEPT:
2468 prompter.get_result(str);
2470 clicked_regionview->region()->set_name (str);
2479 Editor::mouse_brush_insert_region (RegionView* rv, nframes64_t pos)
2481 /* no brushing without a useful snap setting */
2483 switch (snap_mode) {
2485 return; /* can't work because it allows region to be placed anywhere */
2490 switch (snap_type) {
2498 /* don't brush a copy over the original */
2500 if (pos == rv->region()->position()) {
2504 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
2506 if (rtv == 0 || !rtv->is_track()) {
2510 boost::shared_ptr<Playlist> playlist = rtv->playlist();
2511 double speed = rtv->get_diskstream()->speed();
2513 XMLNode &before = playlist->get_state();
2514 playlist->add_region (RegionFactory::create (rv->region()), (nframes64_t) (pos * speed));
2515 XMLNode &after = playlist->get_state();
2516 session->add_command(new MementoCommand<Playlist>(*playlist.get(), &before, &after));
2518 // playlist is frozen, so we have to update manually
2520 playlist->Modified(); /* EMIT SIGNAL */
2524 Editor::track_height_step_timeout ()
2526 if (get_microseconds() - last_track_height_step_timestamp < 250000) {
2527 current_stepping_trackview = 0;
2534 Editor::start_region_grab (ArdourCanvas::Item* item, GdkEvent* event, RegionView* region_view)
2536 assert (region_view);
2538 _region_motion_group->raise_to_top ();
2540 assert (_drag == 0);
2542 if (Config->get_edit_mode() == Splice) {
2543 _drag = new RegionSpliceDrag (this, item, region_view, selection->regions.by_layer());
2545 _drag = new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), false, false);
2548 _drag->start_grab (event);
2550 begin_reversible_command (_("move region(s)"));
2552 /* sync the canvas to what we think is its current state */
2553 update_canvas_now();
2557 Editor::start_region_copy_grab (ArdourCanvas::Item* item, GdkEvent* event, RegionView* region_view)
2559 assert (region_view);
2560 assert (_drag == 0);
2562 _region_motion_group->raise_to_top ();
2563 _drag = new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), false, true);
2564 _drag->start_grab(event);
2568 Editor::start_region_brush_grab (ArdourCanvas::Item* item, GdkEvent* event, RegionView* region_view)
2570 assert (region_view);
2571 assert (_drag == 0);
2573 if (Config->get_edit_mode() == Splice) {
2577 _drag = new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), true, false);
2578 _drag->start_grab (event);
2580 begin_reversible_command (_("Drag region brush"));
2583 /** Start a grab where a time range is selected, track(s) are selected, and the
2584 * user clicks and drags a region with a modifier in order to create a new region containing
2585 * the section of the clicked region that lies within the time range.
2588 Editor::start_selection_grab (ArdourCanvas::Item* item, GdkEvent* event)
2590 if (clicked_regionview == 0) {
2594 /* lets try to create new Region for the selection */
2596 vector<boost::shared_ptr<Region> > new_regions;
2597 create_region_from_selection (new_regions);
2599 if (new_regions.empty()) {
2603 /* XXX fix me one day to use all new regions */
2605 boost::shared_ptr<Region> region (new_regions.front());
2607 /* add it to the current stream/playlist.
2609 tricky: the streamview for the track will add a new regionview. we will
2610 catch the signal it sends when it creates the regionview to
2611 set the regionview we want to then drag.
2614 latest_regionviews.clear();
2615 sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
2617 /* A selection grab currently creates two undo/redo operations, one for
2618 creating the new region and another for moving it.
2621 begin_reversible_command (_("selection grab"));
2623 boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
2625 XMLNode *before = &(playlist->get_state());
2626 clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
2627 XMLNode *after = &(playlist->get_state());
2628 session->add_command(new MementoCommand<Playlist>(*playlist, before, after));
2630 commit_reversible_command ();
2634 if (latest_regionviews.empty()) {
2635 /* something went wrong */
2639 /* we need to deselect all other regionviews, and select this one
2640 i'm ignoring undo stuff, because the region creation will take care of it
2642 selection->set (latest_regionviews);
2644 assert (_drag == 0);
2645 _drag = new RegionMoveDrag (this, latest_regionviews.front()->get_canvas_group(), latest_regionviews.front(), latest_regionviews, false, false);
2646 _drag->start_grab (event);
2650 Editor::break_drag ()
2653 _drag->break_drag ();