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* 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);
992 scrub_reverse_distance = 0;
993 last_scrub_x = event->button.x;
994 scrubbing_direction = 0;
995 track_canvas->get_window()->set_cursor (*transparent_cursor);
996 /* rest handled in motion & release */
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_routeview);
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);
1373 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1374 if (scrubbing_direction == 0) {
1375 /* no drag, just a click */
1376 switch (item_type) {
1378 play_selected_region ();
1384 /* make sure we stop */
1385 session->request_transport_speed (0.0);
1399 switch (mouse_mode) {
1402 switch (item_type) {
1404 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1406 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier|Keyboard::SecondaryModifier))) {
1409 // Button2 click is unused
1422 // x_style_paste (where, 1.0);
1442 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1448 if (last_item_entered != item) {
1449 last_item_entered = item;
1450 last_item_entered_n = 0;
1453 switch (item_type) {
1454 case ControlPointItem:
1455 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1456 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1457 cp->set_visible (true);
1461 at_y = cp->get_y ();
1462 cp->item()->i2w (at_x, at_y);
1466 fraction = 1.0 - (cp->get_y() / cp->line().height());
1468 if (is_drawable() && !_scrubbing) {
1469 track_canvas->get_window()->set_cursor (*fader_cursor);
1472 last_item_entered_n++;
1473 set_verbose_canvas_cursor (cp->line().get_verbose_cursor_string (fraction), at_x, at_y);
1474 if (last_item_entered_n < 10) {
1475 show_verbose_canvas_cursor ();
1481 if (mouse_mode == MouseGain) {
1482 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1484 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredGainLine.get();
1485 if (is_drawable()) {
1486 track_canvas->get_window()->set_cursor (*fader_cursor);
1491 case AutomationLineItem:
1492 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1494 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1496 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredAutomationLine.get();
1498 if (is_drawable()) {
1499 track_canvas->get_window()->set_cursor (*fader_cursor);
1504 case RegionViewNameHighlight:
1505 if (is_drawable() && mouse_mode == MouseObject) {
1506 track_canvas->get_window()->set_cursor (*trimmer_cursor);
1510 case StartSelectionTrimItem:
1511 case EndSelectionTrimItem:
1514 case ImageFrameHandleStartItem:
1515 case ImageFrameHandleEndItem:
1516 case MarkerViewHandleStartItem:
1517 case MarkerViewHandleEndItem:
1520 if (is_drawable()) {
1521 track_canvas->get_window()->set_cursor (*trimmer_cursor);
1525 case PlayheadCursorItem:
1526 if (is_drawable()) {
1527 switch (_edit_point) {
1529 track_canvas->get_window()->set_cursor (*grabber_edit_point_cursor);
1532 track_canvas->get_window()->set_cursor (*grabber_cursor);
1538 case RegionViewName:
1540 /* when the name is not an active item, the entire name highlight is for trimming */
1542 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1543 if (mouse_mode == MouseObject && is_drawable()) {
1544 track_canvas->get_window()->set_cursor (*trimmer_cursor);
1550 case AutomationTrackItem:
1551 if (is_drawable()) {
1552 Gdk::Cursor *cursor;
1553 switch (mouse_mode) {
1555 cursor = selector_cursor;
1558 cursor = zoom_cursor;
1561 cursor = cross_hair_cursor;
1565 track_canvas->get_window()->set_cursor (*cursor);
1567 AutomationTimeAxisView* atv;
1568 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1569 clear_entered_track = false;
1570 set_entered_track (atv);
1576 case RangeMarkerBarItem:
1577 case TransportMarkerBarItem:
1578 case CdMarkerBarItem:
1581 if (is_drawable()) {
1582 track_canvas->get_window()->set_cursor (*timebar_cursor);
1587 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1590 entered_marker = marker;
1591 marker->set_color_rgba (ARDOUR_UI::config()->canvasvar_EnteredMarker.get());
1593 case MeterMarkerItem:
1594 case TempoMarkerItem:
1595 if (is_drawable()) {
1596 track_canvas->get_window()->set_cursor (*timebar_cursor);
1599 case FadeInHandleItem:
1600 case FadeOutHandleItem:
1601 if (mouse_mode == MouseObject) {
1602 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1604 rect->property_fill_color_rgba() = 0;
1605 rect->property_outline_pixels() = 1;
1614 /* second pass to handle entered track status in a comprehensible way.
1617 switch (item_type) {
1619 case AutomationLineItem:
1620 case ControlPointItem:
1621 /* these do not affect the current entered track state */
1622 clear_entered_track = false;
1625 case AutomationTrackItem:
1626 /* handled above already */
1630 set_entered_track (0);
1638 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1647 switch (item_type) {
1648 case ControlPointItem:
1649 cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
1650 if (cp->line().the_list()->interpolation() != AutomationList::Discrete) {
1651 if (cp->line().npoints() > 1 && !cp->selected()) {
1652 cp->set_visible (false);
1656 if (is_drawable()) {
1657 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1660 hide_verbose_canvas_cursor ();
1663 case RegionViewNameHighlight:
1664 case StartSelectionTrimItem:
1665 case EndSelectionTrimItem:
1666 case PlayheadCursorItem:
1669 case ImageFrameHandleStartItem:
1670 case ImageFrameHandleEndItem:
1671 case MarkerViewHandleStartItem:
1672 case MarkerViewHandleEndItem:
1675 if (is_drawable()) {
1676 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1681 case AutomationLineItem:
1682 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1684 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1686 line->property_fill_color_rgba() = al->get_line_color();
1688 if (is_drawable()) {
1689 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1693 case RegionViewName:
1694 /* see enter_handler() for notes */
1695 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1696 if (is_drawable() && mouse_mode == MouseObject) {
1697 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1702 case RangeMarkerBarItem:
1703 case TransportMarkerBarItem:
1704 case CdMarkerBarItem:
1708 if (is_drawable()) {
1709 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1714 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1718 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
1719 location_flags_changed (loc, this);
1722 case MeterMarkerItem:
1723 case TempoMarkerItem:
1725 if (is_drawable()) {
1726 track_canvas->get_window()->set_cursor (*timebar_cursor);
1731 case FadeInHandleItem:
1732 case FadeOutHandleItem:
1733 rv = static_cast<RegionView*>(item->get_data ("regionview"));
1735 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1737 rect->property_fill_color_rgba() = rv->get_fill_color();
1738 rect->property_outline_pixels() = 0;
1743 case AutomationTrackItem:
1744 if (is_drawable()) {
1745 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1746 clear_entered_track = true;
1747 Glib::signal_idle().connect (mem_fun(*this, &Editor::left_automation_track));
1759 Editor::left_automation_track ()
1761 if (clear_entered_track) {
1762 set_entered_track (0);
1763 clear_entered_track = false;
1773 if (scrubbing_direction == 0) {
1775 session->request_locate (_drag->current_pointer_frame(), false);
1776 session->request_transport_speed (0.1);
1777 scrubbing_direction = 1;
1781 if (last_scrub_x > _drag->current_pointer_x()) {
1783 /* pointer moved to the left */
1785 if (scrubbing_direction > 0) {
1787 /* we reversed direction to go backwards */
1790 scrub_reverse_distance += (int) (last_scrub_x - _drag->current_pointer_x());
1794 /* still moving to the left (backwards) */
1796 scrub_reversals = 0;
1797 scrub_reverse_distance = 0;
1799 delta = 0.01 * (last_scrub_x - _drag->current_pointer_x());
1800 session->request_transport_speed (session->transport_speed() - delta);
1804 /* pointer moved to the right */
1806 if (scrubbing_direction < 0) {
1807 /* we reversed direction to go forward */
1810 scrub_reverse_distance += (int) (_drag->current_pointer_x() - last_scrub_x);
1813 /* still moving to the right */
1815 scrub_reversals = 0;
1816 scrub_reverse_distance = 0;
1818 delta = 0.01 * (_drag->current_pointer_x() - last_scrub_x);
1819 session->request_transport_speed (session->transport_speed() + delta);
1823 /* if there have been more than 2 opposite motion moves detected, or one that moves
1824 back more than 10 pixels, reverse direction
1827 if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
1829 if (scrubbing_direction > 0) {
1830 /* was forwards, go backwards */
1831 session->request_transport_speed (-0.1);
1832 scrubbing_direction = -1;
1834 /* was backwards, go forwards */
1835 session->request_transport_speed (0.1);
1836 scrubbing_direction = 1;
1839 scrub_reverse_distance = 0;
1840 scrub_reversals = 0;
1844 last_scrub_x = _drag->current_pointer_x();
1848 Editor::motion_handler (ArdourCanvas::Item* item, GdkEvent* event, bool from_autoscroll)
1850 if (event->motion.is_hint) {
1853 /* We call this so that MOTION_NOTIFY events continue to be
1854 delivered to the canvas. We need to do this because we set
1855 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
1856 the density of the events, at the expense of a round-trip
1857 to the server. Given that this will mostly occur on cases
1858 where DISPLAY = :0.0, and given the cost of what the motion
1859 event might do, its a good tradeoff.
1862 track_canvas->get_pointer (x, y);
1865 if (current_stepping_trackview) {
1866 /* don't keep the persistent stepped trackview if the mouse moves */
1867 current_stepping_trackview = 0;
1868 step_timeout.disconnect ();
1871 if (session && session->actively_recording()) {
1872 /* Sorry. no dragging stuff around while we record */
1876 bool handled = false;
1878 handled = _drag->motion_handler (event, from_autoscroll);
1881 switch (mouse_mode) {
1896 track_canvas_motion (event);
1901 Editor::remove_gain_control_point (ArdourCanvas::Item*item, GdkEvent* event)
1903 ControlPoint* control_point;
1905 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
1906 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
1910 // We shouldn't remove the first or last gain point
1911 if (control_point->line().is_last_point(*control_point) ||
1912 control_point->line().is_first_point(*control_point)) {
1916 control_point->line().remove_point (*control_point);
1920 Editor::remove_control_point (ArdourCanvas::Item* item, GdkEvent* event)
1922 ControlPoint* control_point;
1924 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
1925 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
1929 control_point->line().remove_point (*control_point);
1933 Editor::edit_control_point (ArdourCanvas::Item* item)
1935 ControlPoint* p = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"));
1938 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
1942 ControlPointDialog d (p);
1943 d.set_position (Gtk::WIN_POS_MOUSE);
1946 if (d.run () != RESPONSE_ACCEPT) {
1950 p->line().modify_point_y (*p, d.get_y_fraction ());
1955 Editor::visible_order_range (int* low, int* high) const
1957 *low = TimeAxisView::max_order ();
1960 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
1962 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
1964 if (!rtv->hidden()) {
1966 if (*high < rtv->order()) {
1967 *high = rtv->order ();
1970 if (*low > rtv->order()) {
1971 *low = rtv->order ();
1978 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
1980 /* Either add to or set the set the region selection, unless
1981 this is an alignment click (control used)
1984 if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
1985 TimeAxisView* tv = &rv.get_time_axis_view();
1986 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
1988 if (rtv && rtv->is_track()) {
1989 speed = rtv->get_diskstream()->speed();
1992 nframes64_t where = get_preferred_edit_position();
1996 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
1998 align_region (rv.region(), SyncPoint, (nframes64_t) (where * speed));
2000 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
2002 align_region (rv.region(), End, (nframes64_t) (where * speed));
2006 align_region (rv.region(), Start, (nframes64_t) (where * speed));
2013 Editor::show_verbose_time_cursor (nframes64_t frame, double offset, double xpos, double ypos)
2019 nframes64_t frame_rate;
2028 if (Profile->get_sae() || Profile->get_small_screen()) {
2029 m = ARDOUR_UI::instance()->primary_clock.mode();
2031 m = ARDOUR_UI::instance()->secondary_clock.mode();
2035 case AudioClock::BBT:
2036 session->bbt_time (frame, bbt);
2037 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, bbt.bars, bbt.beats, bbt.ticks);
2040 case AudioClock::SMPTE:
2041 session->smpte_time (frame, smpte);
2042 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
2045 case AudioClock::MinSec:
2046 /* XXX this is copied from show_verbose_duration_cursor() */
2047 frame_rate = session->frame_rate();
2048 hours = frame / (frame_rate * 3600);
2049 frame = frame % (frame_rate * 3600);
2050 mins = frame / (frame_rate * 60);
2051 frame = frame % (frame_rate * 60);
2052 secs = (float) frame / (float) frame_rate;
2053 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
2057 snprintf (buf, sizeof(buf), "%" PRIi64, frame);
2061 if (xpos >= 0 && ypos >=0) {
2062 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
2065 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);
2067 show_verbose_canvas_cursor ();
2071 Editor::show_verbose_duration_cursor (nframes64_t start, nframes64_t end, double offset, double xpos, double ypos)
2078 nframes64_t distance, frame_rate;
2080 Meter meter_at_start(session->tempo_map().meter_at(start));
2088 if (Profile->get_sae() || Profile->get_small_screen()) {
2089 m = ARDOUR_UI::instance()->primary_clock.mode ();
2091 m = ARDOUR_UI::instance()->secondary_clock.mode ();
2095 case AudioClock::BBT:
2096 session->bbt_time (start, sbbt);
2097 session->bbt_time (end, ebbt);
2100 /* XXX this computation won't work well if the
2101 user makes a selection that spans any meter changes.
2104 ebbt.bars -= sbbt.bars;
2105 if (ebbt.beats >= sbbt.beats) {
2106 ebbt.beats -= sbbt.beats;
2109 ebbt.beats = int(meter_at_start.beats_per_bar()) + ebbt.beats - sbbt.beats;
2111 if (ebbt.ticks >= sbbt.ticks) {
2112 ebbt.ticks -= sbbt.ticks;
2115 ebbt.ticks = int(Meter::ticks_per_beat) + ebbt.ticks - sbbt.ticks;
2118 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, ebbt.bars, ebbt.beats, ebbt.ticks);
2121 case AudioClock::SMPTE:
2122 session->smpte_duration (end - start, smpte);
2123 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
2126 case AudioClock::MinSec:
2127 /* XXX this stuff should be elsewhere.. */
2128 distance = end - start;
2129 frame_rate = session->frame_rate();
2130 hours = distance / (frame_rate * 3600);
2131 distance = distance % (frame_rate * 3600);
2132 mins = distance / (frame_rate * 60);
2133 distance = distance % (frame_rate * 60);
2134 secs = (float) distance / (float) frame_rate;
2135 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
2139 snprintf (buf, sizeof(buf), "%" PRIi64, end - start);
2143 if (xpos >= 0 && ypos >=0) {
2144 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
2147 set_verbose_canvas_cursor (buf, _drag->current_pointer_x() + offset, _drag->current_pointer_y() + offset);
2150 show_verbose_canvas_cursor ();
2154 Editor::collect_new_region_view (RegionView* rv)
2156 latest_regionviews.push_back (rv);
2160 Editor::collect_and_select_new_region_view (RegionView* rv)
2163 latest_regionviews.push_back (rv);
2167 Editor::cancel_selection ()
2169 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2170 (*i)->hide_selection ();
2173 selection->clear ();
2174 clicked_selection = 0;
2179 Editor::single_contents_trim (RegionView& rv, nframes64_t frame_delta, bool left_direction, bool swap_direction, bool obey_snap)
2181 boost::shared_ptr<Region> region (rv.region());
2183 if (region->locked()) {
2187 nframes64_t new_bound;
2190 TimeAxisView* tvp = clicked_axisview;
2191 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2193 if (tv && tv->is_track()) {
2194 speed = tv->get_diskstream()->speed();
2197 if (left_direction) {
2198 if (swap_direction) {
2199 new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
2201 new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
2204 if (swap_direction) {
2205 new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
2207 new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
2212 snap_to (new_bound);
2214 region->trim_start ((nframes64_t) (new_bound * speed), this);
2215 rv.region_changed (StartChanged);
2219 Editor::single_start_trim (RegionView& rv, nframes64_t frame_delta, bool left_direction, bool obey_snap, bool no_overlap)
2221 boost::shared_ptr<Region> region (rv.region());
2223 if (region->locked()) {
2227 nframes64_t new_bound;
2230 TimeAxisView* tvp = clicked_axisview;
2231 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2233 if (tv && tv->is_track()) {
2234 speed = tv->get_diskstream()->speed();
2237 if (left_direction) {
2238 new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
2240 new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
2244 snap_to (new_bound, (left_direction ? 0 : 1));
2247 nframes64_t pre_trim_first_frame = region->first_frame();
2249 region->trim_front ((nframes64_t) (new_bound * speed), this);
2252 //Get the next region on the left of this region and shrink/expand it.
2253 boost::shared_ptr<Playlist> playlist (region->playlist());
2254 boost::shared_ptr<Region> region_left = playlist->find_next_region (pre_trim_first_frame, End, 0);
2256 bool regions_touching = false;
2258 if (region_left != 0 && (pre_trim_first_frame == region_left->last_frame() + 1)){
2259 regions_touching = true;
2262 //Only trim region on the left if the first frame has gone beyond the left region's last frame.
2263 if (region_left != 0 &&
2264 (region_left->last_frame() > region->first_frame() || regions_touching))
2266 region_left->trim_end(region->first_frame(), this);
2272 rv.region_changed (Change (LengthChanged|PositionChanged|StartChanged));
2276 Editor::single_end_trim (RegionView& rv, nframes64_t frame_delta, bool left_direction, bool obey_snap, bool no_overlap)
2278 boost::shared_ptr<Region> region (rv.region());
2280 if (region->locked()) {
2284 nframes64_t new_bound;
2287 TimeAxisView* tvp = clicked_axisview;
2288 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2290 if (tv && tv->is_track()) {
2291 speed = tv->get_diskstream()->speed();
2294 if (left_direction) {
2295 new_bound = (nframes64_t) ((region->last_frame() + 1)/speed) - frame_delta;
2297 new_bound = (nframes64_t) ((region->last_frame() + 1)/speed) + frame_delta;
2301 snap_to (new_bound);
2304 nframes64_t pre_trim_last_frame = region->last_frame();
2306 region->trim_end ((nframes64_t) (new_bound * speed), this);
2309 //Get the next region on the right of this region and shrink/expand it.
2310 boost::shared_ptr<Playlist> playlist (region->playlist());
2311 boost::shared_ptr<Region> region_right = playlist->find_next_region (pre_trim_last_frame, Start, 1);
2313 bool regions_touching = false;
2315 if (region_right != 0 && (pre_trim_last_frame == region_right->first_frame() - 1)){
2316 regions_touching = true;
2319 //Only trim region on the right if the last frame has gone beyond the right region's first frame.
2320 if (region_right != 0 &&
2321 (region_right->first_frame() < region->last_frame() || regions_touching))
2323 region_right->trim_front(region->last_frame() + 1, this);
2326 rv.region_changed (Change (LengthChanged|PositionChanged|StartChanged));
2329 rv.region_changed (LengthChanged);
2335 Editor::point_trim (GdkEvent* event)
2337 RegionView* rv = clicked_regionview;
2339 nframes64_t new_bound = _drag->current_pointer_frame();
2341 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2342 snap_to (new_bound);
2345 /* Choose action dependant on which button was pressed */
2346 switch (event->button.button) {
2348 begin_reversible_command (_("Start point trim"));
2350 if (selection->selected (rv)) {
2351 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
2352 i != selection->regions.by_layer().end(); ++i)
2355 cerr << "region view contains null region" << endl;
2358 if (!(*i)->region()->locked()) {
2359 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
2360 XMLNode &before = pl->get_state();
2362 (*i)->region()->trim_front (new_bound, this);
2364 XMLNode &after = pl->get_state();
2365 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
2370 if (!rv->region()->locked()) {
2371 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
2372 XMLNode &before = pl->get_state();
2373 rv->region()->trim_front (new_bound, this);
2374 XMLNode &after = pl->get_state();
2375 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
2379 commit_reversible_command();
2383 begin_reversible_command (_("End point trim"));
2385 if (selection->selected (rv)) {
2387 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
2389 if (!(*i)->region()->locked()) {
2390 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
2391 XMLNode &before = pl->get_state();
2392 (*i)->region()->trim_end (new_bound, this);
2393 XMLNode &after = pl->get_state();
2394 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
2400 if (!rv->region()->locked()) {
2401 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
2402 XMLNode &before = pl->get_state();
2403 rv->region()->trim_end (new_bound, this);
2404 XMLNode &after = pl->get_state();
2405 session->add_command (new MementoCommand<Playlist>(*pl.get(), &before, &after));
2409 commit_reversible_command();
2418 Editor::thaw_region_after_trim (RegionView& rv)
2420 boost::shared_ptr<Region> region (rv.region());
2422 if (region->locked()) {
2426 region->thaw (_("trimmed region"));
2427 XMLNode &after = region->playlist()->get_state();
2428 session->add_command (new MementoCommand<Playlist>(*(region->playlist()), 0, &after));
2430 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(&rv);
2432 arv->unhide_envelope ();
2436 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* event)
2441 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2442 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2446 Location* location = find_location_from_marker (marker, is_start);
2447 location->set_hidden (true, this);
2452 Editor::reposition_zoom_rect (nframes64_t start, nframes64_t end)
2454 double x1 = frame_to_pixel (start);
2455 double x2 = frame_to_pixel (end);
2456 double y2 = full_canvas_height - 1.0;
2458 zoom_rect->property_x1() = x1;
2459 zoom_rect->property_y1() = 1.0;
2460 zoom_rect->property_x2() = x2;
2461 zoom_rect->property_y2() = y2;
2466 Editor::mouse_rename_region (ArdourCanvas::Item* item, GdkEvent* event)
2468 using namespace Gtkmm2ext;
2470 ArdourPrompter prompter (false);
2472 prompter.set_prompt (_("Name for region:"));
2473 prompter.set_initial_text (clicked_regionview->region()->name());
2474 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
2475 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
2476 prompter.show_all ();
2477 switch (prompter.run ()) {
2478 case Gtk::RESPONSE_ACCEPT:
2480 prompter.get_result(str);
2482 clicked_regionview->region()->set_name (str);
2491 Editor::mouse_brush_insert_region (RegionView* rv, nframes64_t pos)
2493 /* no brushing without a useful snap setting */
2495 switch (snap_mode) {
2497 return; /* can't work because it allows region to be placed anywhere */
2502 switch (snap_type) {
2510 /* don't brush a copy over the original */
2512 if (pos == rv->region()->position()) {
2516 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
2518 if (rtv == 0 || !rtv->is_track()) {
2522 boost::shared_ptr<Playlist> playlist = rtv->playlist();
2523 double speed = rtv->get_diskstream()->speed();
2525 XMLNode &before = playlist->get_state();
2526 playlist->add_region (RegionFactory::create (rv->region()), (nframes64_t) (pos * speed));
2527 XMLNode &after = playlist->get_state();
2528 session->add_command(new MementoCommand<Playlist>(*playlist.get(), &before, &after));
2530 // playlist is frozen, so we have to update manually
2532 playlist->Modified(); /* EMIT SIGNAL */
2536 Editor::track_height_step_timeout ()
2538 if (get_microseconds() - last_track_height_step_timestamp < 250000) {
2539 current_stepping_trackview = 0;
2546 Editor::start_region_grab (ArdourCanvas::Item* item, GdkEvent* event, RegionView* region_view)
2548 assert (region_view);
2550 _region_motion_group->raise_to_top ();
2552 assert (_drag == 0);
2554 if (Config->get_edit_mode() == Splice) {
2555 _drag = new RegionSpliceDrag (this, item, region_view, selection->regions.by_layer());
2557 _drag = new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), false, false);
2560 _drag->start_grab (event);
2562 begin_reversible_command (_("move region(s)"));
2564 /* sync the canvas to what we think is its current state */
2565 track_canvas->update_now();
2569 Editor::start_region_copy_grab (ArdourCanvas::Item* item, GdkEvent* event, RegionView* region_view)
2571 assert (region_view);
2572 assert (_drag == 0);
2574 _region_motion_group->raise_to_top ();
2575 _drag = new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), false, true);
2576 _drag->start_grab(event);
2580 Editor::start_region_brush_grab (ArdourCanvas::Item* item, GdkEvent* event, RegionView* region_view)
2582 assert (region_view);
2583 assert (_drag == 0);
2585 if (Config->get_edit_mode() == Splice) {
2589 _drag = new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), true, false);
2590 _drag->start_grab (event);
2592 begin_reversible_command (_("Drag region brush"));
2595 /** Start a grab where a time range is selected, track(s) are selected, and the
2596 * user clicks and drags a region with a modifier in order to create a new region containing
2597 * the section of the clicked region that lies within the time range.
2600 Editor::start_selection_grab (ArdourCanvas::Item* item, GdkEvent* event)
2602 if (clicked_regionview == 0) {
2606 /* lets try to create new Region for the selection */
2608 vector<boost::shared_ptr<Region> > new_regions;
2609 create_region_from_selection (new_regions);
2611 if (new_regions.empty()) {
2615 /* XXX fix me one day to use all new regions */
2617 boost::shared_ptr<Region> region (new_regions.front());
2619 /* add it to the current stream/playlist.
2621 tricky: the streamview for the track will add a new regionview. we will
2622 catch the signal it sends when it creates the regionview to
2623 set the regionview we want to then drag.
2626 latest_regionviews.clear();
2627 sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
2629 /* A selection grab currently creates two undo/redo operations, one for
2630 creating the new region and another for moving it.
2633 begin_reversible_command (_("selection grab"));
2635 boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
2637 XMLNode *before = &(playlist->get_state());
2638 clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
2639 XMLNode *after = &(playlist->get_state());
2640 session->add_command(new MementoCommand<Playlist>(*playlist, before, after));
2642 commit_reversible_command ();
2646 if (latest_regionviews.empty()) {
2647 /* something went wrong */
2651 /* we need to deselect all other regionviews, and select this one
2652 i'm ignoring undo stuff, because the region creation will take care of it
2654 selection->set (latest_regionviews);
2656 assert (_drag == 0);
2657 _drag = new RegionMoveDrag (this, latest_regionviews.front()->get_canvas_group(), latest_regionviews.front(), latest_regionviews, false, false);
2658 _drag->start_grab (event);
2662 Editor::break_drag ()
2665 _drag->break_drag ();