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)
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:
825 RegionSelection s = get_equivalent_regions (selection->regions, RouteGroup::Edit);
826 _drag = new FadeInDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), s);
827 _drag->start_grab (event);
831 case FadeOutHandleItem:
834 RegionSelection s = get_equivalent_regions (selection->regions, RouteGroup::Edit);
835 _drag = new FadeOutDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), s);
836 _drag->start_grab (event);
841 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
842 start_region_copy_grab (item, event, clicked_regionview);
843 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
844 start_region_brush_grab (item, event, clicked_regionview);
846 start_region_grab (item, event, clicked_regionview);
850 case RegionViewNameHighlight:
853 RegionSelection s = get_equivalent_regions (selection->regions, RouteGroup::Edit);
854 _drag = new TrimDrag (this, item, clicked_regionview, s.by_layer());
855 _drag->start_grab (event);
862 /* rename happens on edit clicks */
864 RegionSelection s = get_equivalent_regions (selection->regions, RouteGroup::Edit);
865 _drag = new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, s.by_layer());
866 _drag->start_grab (event);
871 case ControlPointItem:
873 _drag = new ControlPointDrag (this, item);
874 _drag->start_grab (event);
878 case AutomationLineItem:
880 _drag = new LineDrag (this, item);
881 _drag->start_grab (event);
886 case AutomationTrackItem:
888 _drag = new RubberbandSelectDrag (this, item);
889 _drag->start_grab (event);
893 case ImageFrameHandleStartItem:
894 imageframe_start_handle_op(item, event) ;
897 case ImageFrameHandleEndItem:
898 imageframe_end_handle_op(item, event) ;
901 case MarkerViewHandleStartItem:
902 markerview_item_start_handle_op(item, event) ;
905 case MarkerViewHandleEndItem:
906 markerview_item_end_handle_op(item, event) ;
910 start_markerview_grab(item, event) ;
913 start_imageframe_grab(item, event) ;
931 /* start a grab so that if we finish after moving
932 we can tell what happened.
935 _drag = new RegionGainDrag (this, item);
936 _drag->start_grab (event, current_canvas_cursor);
941 _drag = new LineDrag (this, item);
942 _drag->start_grab (event);
945 case ControlPointItem:
947 _drag = new ControlPointDrag (this, item);
948 _drag->start_grab (event);
959 case ControlPointItem:
961 _drag = new ControlPointDrag (this, item);
962 _drag->start_grab (event);
965 case AutomationLineItem:
967 _drag = new LineDrag (this, item);
968 _drag->start_grab (event);
972 // XXX need automation mode to identify which
974 // start_line_grab_from_regionview (item, event);
984 if (event->type == GDK_BUTTON_PRESS) {
986 _drag = new MouseZoomDrag (this, item);
987 _drag->start_grab (event);
994 if (item_type == RegionItem) {
996 _drag = new TimeFXDrag (this, item, clicked_regionview, selection->regions.by_layer());
997 _drag->start_grab (event);
1002 _drag = new ScrubDrag (this, item);
1003 _drag->start_grab (event);
1004 scrub_reversals = 0;
1005 scrub_reverse_distance = 0;
1006 last_scrub_x = event->button.x;
1007 scrubbing_direction = 0;
1008 track_canvas->get_window()->set_cursor (*transparent_cursor);
1012 assert (_drag == 0);
1013 _drag = new RegionCreateDrag (this, item, clicked_axisview);
1014 _drag->start_grab (event);
1023 switch (mouse_mode) {
1025 if (event->type == GDK_BUTTON_PRESS) {
1026 switch (item_type) {
1028 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
1029 start_region_copy_grab (item, event, clicked_regionview);
1031 start_region_grab (item, event, clicked_regionview);
1035 case ControlPointItem:
1036 assert (_drag == 0);
1037 _drag = new ControlPointDrag (this, item);
1038 _drag->start_grab (event);
1048 switch (item_type) {
1049 case RegionViewNameHighlight:
1050 assert (_drag == 0);
1051 _drag = new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer());
1052 _drag->start_grab (event);
1056 case RegionViewName:
1057 assert (_drag == 0);
1058 _drag = new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer());
1059 _drag->start_grab (event);
1070 if (event->type == GDK_BUTTON_PRESS) {
1071 /* relax till release */
1078 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1079 temporal_zoom_session();
1081 temporal_zoom_to_frame (true, event_frame(event));
1104 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1106 nframes64_t where = event_frame (event, 0, 0);
1107 AutomationTimeAxisView* atv = 0;
1109 /* no action if we're recording */
1111 if (session && session->actively_recording()) {
1115 /* first, see if we're finishing a drag ... */
1117 bool were_dragging = false;
1119 bool const r = _drag->end_grab (event);
1123 /* grab dragged, so do nothing else */
1127 were_dragging = true;
1130 button_selection (item, event, item_type);
1132 /* edit events get handled here */
1134 if (_drag == 0 && Keyboard::is_edit_event (&event->button)) {
1135 switch (item_type) {
1140 case TempoMarkerItem:
1141 edit_tempo_marker (item);
1144 case MeterMarkerItem:
1145 edit_meter_marker (item);
1148 case RegionViewName:
1149 if (clicked_regionview->name_active()) {
1150 return mouse_rename_region (item, event);
1154 case ControlPointItem:
1155 edit_control_point (item);
1164 /* context menu events get handled here */
1166 if (Keyboard::is_context_menu_event (&event->button)) {
1170 /* no matter which button pops up the context menu, tell the menu
1171 widget to use button 1 to drive menu selection.
1174 switch (item_type) {
1176 case FadeInHandleItem:
1178 case FadeOutHandleItem:
1179 popup_fade_context_menu (1, event->button.time, item, item_type);
1183 popup_track_context_menu (1, event->button.time, item_type, false, where);
1187 case RegionViewNameHighlight:
1188 case RegionViewName:
1189 popup_track_context_menu (1, event->button.time, item_type, false, where);
1193 popup_track_context_menu (1, event->button.time, item_type, true, where);
1196 case AutomationTrackItem:
1197 popup_track_context_menu (1, event->button.time, item_type, false, where);
1201 case RangeMarkerBarItem:
1202 case TransportMarkerBarItem:
1203 case CdMarkerBarItem:
1206 popup_ruler_menu (where, item_type);
1210 marker_context_menu (&event->button, item);
1213 case TempoMarkerItem:
1214 tm_marker_context_menu (&event->button, item);
1217 case MeterMarkerItem:
1218 tm_marker_context_menu (&event->button, item);
1221 case CrossfadeViewItem:
1222 popup_track_context_menu (1, event->button.time, item_type, false, where);
1226 case ImageFrameItem:
1227 popup_imageframe_edit_menu(1, event->button.time, item, true) ;
1229 case ImageFrameTimeAxisItem:
1230 popup_imageframe_edit_menu(1, event->button.time, item, false) ;
1232 case MarkerViewItem:
1233 popup_marker_time_axis_edit_menu(1, event->button.time, item, true) ;
1235 case MarkerTimeAxisItem:
1236 popup_marker_time_axis_edit_menu(1, event->button.time, item, false) ;
1248 /* delete events get handled here */
1250 if (_drag == 0 && Keyboard::is_delete_event (&event->button)) {
1252 switch (item_type) {
1253 case TempoMarkerItem:
1254 remove_tempo_marker (item);
1257 case MeterMarkerItem:
1258 remove_meter_marker (item);
1262 remove_marker (*item, event);
1266 if (mouse_mode == MouseObject) {
1267 remove_clicked_region ();
1271 case ControlPointItem:
1272 if (mouse_mode == MouseGain) {
1273 remove_gain_control_point (item, event);
1275 remove_control_point (item, event);
1285 switch (event->button.button) {
1288 switch (item_type) {
1289 /* see comments in button_press_handler */
1290 case PlayheadCursorItem:
1293 case AutomationLineItem:
1294 case StartSelectionTrimItem:
1295 case EndSelectionTrimItem:
1299 if (!_dragging_playhead) {
1300 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1301 snap_to (where, 0, true);
1303 mouse_add_new_marker (where);
1307 case CdMarkerBarItem:
1308 if (!_dragging_playhead) {
1309 // if we get here then a dragged range wasn't done
1310 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1311 snap_to (where, 0, true);
1313 mouse_add_new_marker (where, true);
1318 if (!_dragging_playhead) {
1319 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1322 mouse_add_new_tempo_event (where);
1327 if (!_dragging_playhead) {
1328 mouse_add_new_meter_event (pixel_to_frame (event->button.x));
1337 switch (mouse_mode) {
1339 switch (item_type) {
1340 case AutomationTrackItem:
1341 atv = dynamic_cast<AutomationTimeAxisView*>(clicked_axisview);
1343 atv->add_automation_event (item, event, where, event->button.y);
1355 // Gain only makes sense for audio regions
1357 if (!dynamic_cast<AudioRegionView*>(clicked_regionview)) {
1361 switch (item_type) {
1363 /* check that we didn't drag before releasing, since
1364 its really annoying to create new control
1365 points when doing this.
1367 if (were_dragging) {
1368 dynamic_cast<AudioRegionView*>(clicked_regionview)->add_gain_point_event (item, event);
1373 case AutomationTrackItem:
1374 dynamic_cast<AutomationTimeAxisView*>(clicked_axisview)->
1375 add_automation_event (item, event, where, event->button.y);
1384 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1385 if (scrubbing_direction == 0) {
1386 /* no drag, just a click */
1387 switch (item_type) {
1389 play_selected_region ();
1395 /* make sure we stop */
1396 session->request_transport_speed (0.0);
1410 switch (mouse_mode) {
1413 switch (item_type) {
1415 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1417 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier|Keyboard::SecondaryModifier))) {
1420 // Button2 click is unused
1433 // x_style_paste (where, 1.0);
1453 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* /*event*/, ItemType item_type)
1459 if (last_item_entered != item) {
1460 last_item_entered = item;
1461 last_item_entered_n = 0;
1464 switch (item_type) {
1465 case ControlPointItem:
1466 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1467 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1468 cp->set_visible (true);
1472 at_y = cp->get_y ();
1473 cp->item()->i2w (at_x, at_y);
1477 fraction = 1.0 - (cp->get_y() / cp->line().height());
1479 if (is_drawable() && dynamic_cast<ScrubDrag*> (_drag) == 0) {
1480 track_canvas->get_window()->set_cursor (*fader_cursor);
1483 last_item_entered_n++;
1484 set_verbose_canvas_cursor (cp->line().get_verbose_cursor_string (fraction), at_x, at_y);
1485 if (last_item_entered_n < 10) {
1486 show_verbose_canvas_cursor ();
1492 if (mouse_mode == MouseGain) {
1493 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1495 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredGainLine.get();
1496 if (is_drawable()) {
1497 track_canvas->get_window()->set_cursor (*fader_cursor);
1502 case AutomationLineItem:
1503 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1505 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1507 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredAutomationLine.get();
1509 if (is_drawable()) {
1510 track_canvas->get_window()->set_cursor (*fader_cursor);
1515 case RegionViewNameHighlight:
1516 if (is_drawable() && mouse_mode == MouseObject) {
1517 track_canvas->get_window()->set_cursor (*trimmer_cursor);
1521 case StartSelectionTrimItem:
1522 case EndSelectionTrimItem:
1525 case ImageFrameHandleStartItem:
1526 case ImageFrameHandleEndItem:
1527 case MarkerViewHandleStartItem:
1528 case MarkerViewHandleEndItem:
1531 if (is_drawable()) {
1532 track_canvas->get_window()->set_cursor (*trimmer_cursor);
1536 case PlayheadCursorItem:
1537 if (is_drawable()) {
1538 switch (_edit_point) {
1540 track_canvas->get_window()->set_cursor (*grabber_edit_point_cursor);
1543 track_canvas->get_window()->set_cursor (*grabber_cursor);
1549 case RegionViewName:
1551 /* when the name is not an active item, the entire name highlight is for trimming */
1553 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1554 if (mouse_mode == MouseObject && is_drawable()) {
1555 track_canvas->get_window()->set_cursor (*trimmer_cursor);
1561 case AutomationTrackItem:
1562 if (is_drawable()) {
1563 Gdk::Cursor *cursor;
1564 switch (mouse_mode) {
1566 cursor = selector_cursor;
1569 cursor = zoom_cursor;
1572 cursor = cross_hair_cursor;
1576 track_canvas->get_window()->set_cursor (*cursor);
1578 AutomationTimeAxisView* atv;
1579 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1580 clear_entered_track = false;
1581 set_entered_track (atv);
1587 case RangeMarkerBarItem:
1588 case TransportMarkerBarItem:
1589 case CdMarkerBarItem:
1592 if (is_drawable()) {
1593 track_canvas->get_window()->set_cursor (*timebar_cursor);
1598 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1601 entered_marker = marker;
1602 marker->set_color_rgba (ARDOUR_UI::config()->canvasvar_EnteredMarker.get());
1604 case MeterMarkerItem:
1605 case TempoMarkerItem:
1606 if (is_drawable()) {
1607 track_canvas->get_window()->set_cursor (*timebar_cursor);
1610 case FadeInHandleItem:
1611 case FadeOutHandleItem:
1612 if (mouse_mode == MouseObject) {
1613 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1615 rect->property_fill_color_rgba() = 0;
1616 rect->property_outline_pixels() = 1;
1625 /* second pass to handle entered track status in a comprehensible way.
1628 switch (item_type) {
1630 case AutomationLineItem:
1631 case ControlPointItem:
1632 /* these do not affect the current entered track state */
1633 clear_entered_track = false;
1636 case AutomationTrackItem:
1637 /* handled above already */
1641 set_entered_track (0);
1649 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent* /*event*/, ItemType item_type)
1658 switch (item_type) {
1659 case ControlPointItem:
1660 cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
1661 if (cp->line().the_list()->interpolation() != AutomationList::Discrete) {
1662 if (cp->line().npoints() > 1 && !cp->selected()) {
1663 cp->set_visible (false);
1667 if (is_drawable()) {
1668 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1671 hide_verbose_canvas_cursor ();
1674 case RegionViewNameHighlight:
1675 case StartSelectionTrimItem:
1676 case EndSelectionTrimItem:
1677 case PlayheadCursorItem:
1680 case ImageFrameHandleStartItem:
1681 case ImageFrameHandleEndItem:
1682 case MarkerViewHandleStartItem:
1683 case MarkerViewHandleEndItem:
1686 if (is_drawable()) {
1687 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1692 case AutomationLineItem:
1693 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1695 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1697 line->property_fill_color_rgba() = al->get_line_color();
1699 if (is_drawable()) {
1700 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1704 case RegionViewName:
1705 /* see enter_handler() for notes */
1706 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1707 if (is_drawable() && mouse_mode == MouseObject) {
1708 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1713 case RangeMarkerBarItem:
1714 case TransportMarkerBarItem:
1715 case CdMarkerBarItem:
1719 if (is_drawable()) {
1720 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1725 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1729 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
1730 location_flags_changed (loc, this);
1733 case MeterMarkerItem:
1734 case TempoMarkerItem:
1736 if (is_drawable()) {
1737 track_canvas->get_window()->set_cursor (*timebar_cursor);
1742 case FadeInHandleItem:
1743 case FadeOutHandleItem:
1744 rv = static_cast<RegionView*>(item->get_data ("regionview"));
1746 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1748 rect->property_fill_color_rgba() = rv->get_fill_color();
1749 rect->property_outline_pixels() = 0;
1754 case AutomationTrackItem:
1755 if (is_drawable()) {
1756 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1757 clear_entered_track = true;
1758 Glib::signal_idle().connect (mem_fun(*this, &Editor::left_automation_track));
1770 Editor::left_automation_track ()
1772 if (clear_entered_track) {
1773 set_entered_track (0);
1774 clear_entered_track = false;
1784 if (scrubbing_direction == 0) {
1786 session->request_locate (_drag->current_pointer_frame(), false);
1787 session->request_transport_speed (0.1);
1788 scrubbing_direction = 1;
1792 if (last_scrub_x > _drag->current_pointer_x()) {
1794 /* pointer moved to the left */
1796 if (scrubbing_direction > 0) {
1798 /* we reversed direction to go backwards */
1801 scrub_reverse_distance += (int) (last_scrub_x - _drag->current_pointer_x());
1805 /* still moving to the left (backwards) */
1807 scrub_reversals = 0;
1808 scrub_reverse_distance = 0;
1810 delta = 0.01 * (last_scrub_x - _drag->current_pointer_x());
1811 session->request_transport_speed (session->transport_speed() - delta);
1815 /* pointer moved to the right */
1817 if (scrubbing_direction < 0) {
1818 /* we reversed direction to go forward */
1821 scrub_reverse_distance += (int) (_drag->current_pointer_x() - last_scrub_x);
1824 /* still moving to the right */
1826 scrub_reversals = 0;
1827 scrub_reverse_distance = 0;
1829 delta = 0.01 * (_drag->current_pointer_x() - last_scrub_x);
1830 session->request_transport_speed (session->transport_speed() + delta);
1834 /* if there have been more than 2 opposite motion moves detected, or one that moves
1835 back more than 10 pixels, reverse direction
1838 if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
1840 if (scrubbing_direction > 0) {
1841 /* was forwards, go backwards */
1842 session->request_transport_speed (-0.1);
1843 scrubbing_direction = -1;
1845 /* was backwards, go forwards */
1846 session->request_transport_speed (0.1);
1847 scrubbing_direction = 1;
1850 scrub_reverse_distance = 0;
1851 scrub_reversals = 0;
1855 last_scrub_x = _drag->current_pointer_x();
1859 Editor::motion_handler (ArdourCanvas::Item* /*item*/, GdkEvent* event, bool from_autoscroll)
1861 if (event->motion.is_hint) {
1864 /* We call this so that MOTION_NOTIFY events continue to be
1865 delivered to the canvas. We need to do this because we set
1866 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
1867 the density of the events, at the expense of a round-trip
1868 to the server. Given that this will mostly occur on cases
1869 where DISPLAY = :0.0, and given the cost of what the motion
1870 event might do, its a good tradeoff.
1873 track_canvas->get_pointer (x, y);
1876 if (current_stepping_trackview) {
1877 /* don't keep the persistent stepped trackview if the mouse moves */
1878 current_stepping_trackview = 0;
1879 step_timeout.disconnect ();
1882 if (session && session->actively_recording()) {
1883 /* Sorry. no dragging stuff around while we record */
1887 bool handled = false;
1889 handled = _drag->motion_handler (event, from_autoscroll);
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"));
2428 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(&rv);
2431 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 RegionSelection s = get_equivalent_regions (selection->regions, RouteGroup::Edit);
2558 _drag = new RegionMoveDrag (this, item, region_view, s.by_layer(), false, false);
2561 _drag->start_grab (event);
2563 begin_reversible_command (_("move region(s)"));
2565 /* sync the canvas to what we think is its current state */
2566 update_canvas_now();
2570 Editor::start_region_copy_grab (ArdourCanvas::Item* item, GdkEvent* event, RegionView* region_view)
2572 assert (region_view);
2573 assert (_drag == 0);
2575 _region_motion_group->raise_to_top ();
2577 RegionSelection s = get_equivalent_regions (selection->regions, RouteGroup::Edit);
2578 _drag = new RegionMoveDrag (this, item, region_view, s.by_layer(), false, true);
2579 _drag->start_grab(event);
2583 Editor::start_region_brush_grab (ArdourCanvas::Item* item, GdkEvent* event, RegionView* region_view)
2585 assert (region_view);
2586 assert (_drag == 0);
2588 if (Config->get_edit_mode() == Splice) {
2592 RegionSelection s = get_equivalent_regions (selection->regions, RouteGroup::Edit);
2593 _drag = new RegionMoveDrag (this, item, region_view, s.by_layer(), true, false);
2594 _drag->start_grab (event);
2596 begin_reversible_command (_("Drag region brush"));
2599 /** Start a grab where a time range is selected, track(s) are selected, and the
2600 * user clicks and drags a region with a modifier in order to create a new region containing
2601 * the section of the clicked region that lies within the time range.
2604 Editor::start_selection_grab (ArdourCanvas::Item* /*item*/, GdkEvent* event)
2606 if (clicked_regionview == 0) {
2610 /* lets try to create new Region for the selection */
2612 vector<boost::shared_ptr<Region> > new_regions;
2613 create_region_from_selection (new_regions);
2615 if (new_regions.empty()) {
2619 /* XXX fix me one day to use all new regions */
2621 boost::shared_ptr<Region> region (new_regions.front());
2623 /* add it to the current stream/playlist.
2625 tricky: the streamview for the track will add a new regionview. we will
2626 catch the signal it sends when it creates the regionview to
2627 set the regionview we want to then drag.
2630 latest_regionviews.clear();
2631 sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
2633 /* A selection grab currently creates two undo/redo operations, one for
2634 creating the new region and another for moving it.
2637 begin_reversible_command (_("selection grab"));
2639 boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
2641 XMLNode *before = &(playlist->get_state());
2642 clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
2643 XMLNode *after = &(playlist->get_state());
2644 session->add_command(new MementoCommand<Playlist>(*playlist, before, after));
2646 commit_reversible_command ();
2650 if (latest_regionviews.empty()) {
2651 /* something went wrong */
2655 /* we need to deselect all other regionviews, and select this one
2656 i'm ignoring undo stuff, because the region creation will take care of it
2658 selection->set (latest_regionviews);
2660 assert (_drag == 0);
2661 _drag = new RegionMoveDrag (this, latest_regionviews.front()->get_canvas_group(), latest_regionviews.front(), latest_regionviews, false, false);
2662 _drag->start_grab (event);
2666 Editor::break_drag ()
2669 _drag->break_drag ();