2 Copyright (C) 2000-2001 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
28 #include "pbd/error.h"
29 #include <gtkmm2ext/utils.h>
30 #include <gtkmm2ext/tearoff.h>
31 #include "pbd/memento_command.h"
32 #include "pbd/basename.h"
34 #include "ardour_ui.h"
36 #include "time_axis_view.h"
37 #include "audio_time_axis.h"
38 #include "audio_region_view.h"
39 #include "midi_region_view.h"
41 #include "streamview.h"
42 #include "region_gain_line.h"
43 #include "automation_time_axis.h"
44 #include "control_point.h"
47 #include "selection.h"
50 #include "rgb_macros.h"
51 #include "control_point_dialog.h"
52 #include "editor_drag.h"
54 #include "ardour/types.h"
55 #include "ardour/profile.h"
56 #include "ardour/route.h"
57 #include "ardour/audio_track.h"
58 #include "ardour/audio_diskstream.h"
59 #include "ardour/midi_diskstream.h"
60 #include "ardour/playlist.h"
61 #include "ardour/audioplaylist.h"
62 #include "ardour/audioregion.h"
63 #include "ardour/midi_region.h"
64 #include "ardour/dB.h"
65 #include "ardour/utils.h"
66 #include "ardour/region_factory.h"
67 #include "ardour/source_factory.h"
74 using namespace ARDOUR;
78 using namespace Editing;
81 Editor::mouse_frame (nframes64_t& where, bool& in_track_canvas) const
85 Gdk::ModifierType mask;
86 Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->track_canvas->get_window();
87 Glib::RefPtr<const Gdk::Window> pointer_window;
93 pointer_window = canvas_window->get_pointer (x, y, mask);
95 if (pointer_window == track_canvas->get_bin_window()) {
98 in_track_canvas = true;
101 in_track_canvas = false;
106 event.type = GDK_BUTTON_RELEASE;
110 where = event_frame (&event, 0, 0);
115 Editor::event_frame (GdkEvent const * event, double* pcx, double* pcy) const
129 switch (event->type) {
130 case GDK_BUTTON_RELEASE:
131 case GDK_BUTTON_PRESS:
132 case GDK_2BUTTON_PRESS:
133 case GDK_3BUTTON_PRESS:
135 *pcx = event->button.x;
136 *pcy = event->button.y;
137 _trackview_group->w2i(*pcx, *pcy);
139 case GDK_MOTION_NOTIFY:
141 *pcx = event->motion.x;
142 *pcy = event->motion.y;
143 _trackview_group->w2i(*pcx, *pcy);
145 case GDK_ENTER_NOTIFY:
146 case GDK_LEAVE_NOTIFY:
147 track_canvas->w2c(event->crossing.x, event->crossing.y, *pcx, *pcy);
150 case GDK_KEY_RELEASE:
151 // track_canvas->w2c(event->key.x, event->key.y, *pcx, *pcy);
154 warning << string_compose (_("Editor::event_frame() used on unhandled event type %1"), event->type) << endmsg;
158 /* note that pixel_to_frame() never returns less than zero, so even if the pixel
159 position is negative (as can be the case with motion events in particular),
160 the frame location is always positive.
163 return pixel_to_frame (*pcx);
167 Editor::mouse_mode_toggled (MouseMode m)
169 if (ignore_mouse_mode_toggle) {
175 if (mouse_select_button.get_active()) {
181 if (mouse_move_button.get_active()) {
187 if (mouse_gain_button.get_active()) {
193 if (mouse_zoom_button.get_active()) {
199 if (mouse_timefx_button.get_active()) {
205 if (mouse_audition_button.get_active()) {
211 if (mouse_note_button.get_active()) {
222 Editor::which_grabber_cursor ()
224 switch (_edit_point) {
226 return grabber_edit_point_cursor;
231 return grabber_cursor;
235 Editor::set_canvas_cursor ()
237 switch (mouse_mode) {
239 current_canvas_cursor = selector_cursor;
243 current_canvas_cursor = which_grabber_cursor();
247 current_canvas_cursor = cross_hair_cursor;
251 current_canvas_cursor = zoom_cursor;
255 current_canvas_cursor = time_fx_cursor; // just use playhead
259 current_canvas_cursor = speaker_cursor;
263 set_midi_edit_cursor (current_midi_edit_mode());
268 track_canvas->get_window()->set_cursor(*current_canvas_cursor);
273 Editor::set_mouse_mode (MouseMode m, bool force)
279 if (!force && m == mouse_mode) {
287 if (mouse_mode != MouseRange) {
289 /* in all modes except range, hide the range selection,
290 show the object (region) selection.
293 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
294 (*i)->set_should_show_selection (true);
296 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
297 (*i)->hide_selection ();
303 in range mode,show the range selection.
306 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
307 if ((*i)->get_selected()) {
308 (*i)->show_selection (selection->time);
313 /* XXX the hack of unsetting all other buttons should go
314 away once GTK2 allows us to use regular radio buttons drawn like
315 normal buttons, rather than my silly GroupedButton hack.
318 ignore_mouse_mode_toggle = true;
320 switch (mouse_mode) {
322 mouse_select_button.set_active (true);
326 mouse_move_button.set_active (true);
330 mouse_gain_button.set_active (true);
334 mouse_zoom_button.set_active (true);
338 mouse_timefx_button.set_active (true);
342 mouse_audition_button.set_active (true);
346 mouse_note_button.set_active (true);
347 set_midi_edit_cursor (current_midi_edit_mode());
351 if (midi_tools_tearoff) {
352 if (mouse_mode == MouseNote) {
353 midi_tools_tearoff->show();
355 midi_tools_tearoff->hide();
359 ignore_mouse_mode_toggle = false;
361 set_canvas_cursor ();
365 Editor::step_mouse_mode (bool next)
367 switch (current_mouse_mode()) {
370 if (Profile->get_sae()) {
371 set_mouse_mode (MouseZoom);
373 set_mouse_mode (MouseRange);
376 set_mouse_mode (MouseTimeFX);
381 if (next) set_mouse_mode (MouseZoom);
382 else set_mouse_mode (MouseObject);
387 if (Profile->get_sae()) {
388 set_mouse_mode (MouseTimeFX);
390 set_mouse_mode (MouseGain);
393 if (Profile->get_sae()) {
394 set_mouse_mode (MouseObject);
396 set_mouse_mode (MouseRange);
402 if (next) set_mouse_mode (MouseTimeFX);
403 else set_mouse_mode (MouseZoom);
408 set_mouse_mode (MouseAudition);
410 if (Profile->get_sae()) {
411 set_mouse_mode (MouseZoom);
413 set_mouse_mode (MouseGain);
419 if (next) set_mouse_mode (MouseObject);
420 else set_mouse_mode (MouseTimeFX);
424 if (next) set_mouse_mode (MouseObject);
425 else set_mouse_mode (MouseAudition);
431 Editor::midi_edit_mode_toggled (MidiEditMode m)
433 if (ignore_midi_edit_mode_toggle) {
439 if (midi_tool_pencil_button.get_active())
440 set_midi_edit_mode (m);
444 if (midi_tool_select_button.get_active())
445 set_midi_edit_mode (m);
449 if (midi_tool_resize_button.get_active())
450 set_midi_edit_mode (m);
454 if (midi_tool_erase_button.get_active())
455 set_midi_edit_mode (m);
462 set_midi_edit_cursor(m);
467 Editor::set_midi_edit_mode (MidiEditMode m, bool force)
473 if (!force && m == midi_edit_mode) {
481 ignore_midi_edit_mode_toggle = true;
483 switch (midi_edit_mode) {
485 midi_tool_pencil_button.set_active (true);
489 midi_tool_select_button.set_active (true);
493 midi_tool_resize_button.set_active (true);
497 midi_tool_erase_button.set_active (true);
501 ignore_midi_edit_mode_toggle = false;
503 set_midi_edit_cursor (current_midi_edit_mode());
506 track_canvas->get_window()->set_cursor(*current_canvas_cursor);
511 Editor::set_midi_edit_cursor (MidiEditMode m)
513 switch (midi_edit_mode) {
515 current_canvas_cursor = midi_pencil_cursor;
519 current_canvas_cursor = midi_select_cursor;
523 current_canvas_cursor = midi_resize_cursor;
527 current_canvas_cursor = midi_erase_cursor;
533 Editor::button_selection (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
535 /* in object/audition/timefx/gain-automation mode,
536 any button press sets the selection if the object
537 can be selected. this is a bit of hack, because
538 we want to avoid this if the mouse operation is a
541 note: not dbl-click or triple-click
544 if (((mouse_mode != MouseObject) &&
545 (mouse_mode != MouseAudition || item_type != RegionItem) &&
546 (mouse_mode != MouseTimeFX || item_type != RegionItem) &&
547 (mouse_mode != MouseGain) &&
548 (mouse_mode != MouseRange)) ||
550 ((event->type != GDK_BUTTON_PRESS && event->type != GDK_BUTTON_RELEASE) || event->button.button > 3)) {
555 if (event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) {
557 if ((event->button.state & Keyboard::RelevantModifierKeyMask) && event->button.button != 1) {
559 /* almost no selection action on modified button-2 or button-3 events */
561 if (item_type != RegionItem && event->button.button != 2) {
567 Selection::Operation op = Keyboard::selection_type (event->button.state);
568 bool press = (event->type == GDK_BUTTON_PRESS);
570 // begin_reversible_command (_("select on click"));
574 if (mouse_mode != MouseRange) {
575 set_selected_regionview_from_click (press, op, true);
576 } else if (event->type == GDK_BUTTON_PRESS) {
577 set_selected_track_as_side_effect ();
581 case RegionViewNameHighlight:
583 if (mouse_mode != MouseRange) {
584 set_selected_regionview_from_click (press, op, true);
585 } else if (event->type == GDK_BUTTON_PRESS) {
586 set_selected_track_as_side_effect ();
591 case FadeInHandleItem:
593 case FadeOutHandleItem:
595 if (mouse_mode != MouseRange) {
596 set_selected_regionview_from_click (press, op, true);
597 } else if (event->type == GDK_BUTTON_PRESS) {
598 set_selected_track_as_side_effect ();
602 case ControlPointItem:
603 set_selected_track_as_side_effect ();
604 if (mouse_mode != MouseRange) {
605 set_selected_control_point_from_click (op, false);
610 /* for context click or range selection, select track */
611 if (event->button.button == 3) {
612 set_selected_track_as_side_effect ();
613 } else if (event->type == GDK_BUTTON_PRESS && mouse_mode == MouseRange) {
614 set_selected_track_as_side_effect ();
618 case AutomationTrackItem:
619 set_selected_track_as_side_effect (true);
628 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
630 Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->track_canvas->get_window();
633 Glib::RefPtr<const Gdk::Window> pointer_window;
636 Gdk::ModifierType mask;
638 pointer_window = canvas_window->get_pointer (x, y, mask);
640 if (pointer_window == track_canvas->get_bin_window()) {
641 track_canvas->window_to_world (x, y, wx, wy);
642 allow_vertical_scroll = true;
644 allow_vertical_scroll = false;
648 track_canvas->grab_focus();
650 if (session && session->actively_recording()) {
654 button_selection (item, event, item_type);
657 (Keyboard::is_delete_event (&event->button) ||
658 Keyboard::is_context_menu_event (&event->button) ||
659 Keyboard::is_edit_event (&event->button))) {
661 /* handled by button release */
665 switch (event->button.button) {
668 if (event->type == GDK_BUTTON_PRESS) {
671 _drag->item()->ungrab (event->button.time);
674 /* single mouse clicks on any of these item types operate
675 independent of mouse mode, mostly because they are
676 not on the main track canvas or because we want
681 case PlayheadCursorItem:
683 _drag = new CursorDrag (this, item, true);
684 _drag->start_grab (event);
688 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
689 hide_marker (item, event);
692 _drag = new MarkerDrag (this, item);
693 _drag->start_grab (event);
697 case TempoMarkerItem:
699 _drag = new TempoMarkerDrag (
702 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
704 _drag->start_grab (event);
707 case MeterMarkerItem:
710 _drag = new MeterMarkerDrag (
713 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
716 _drag->start_grab (event);
722 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
724 _drag = new CursorDrag (this, &playhead_cursor->canvas_item, false);
725 _drag->start_grab (event);
731 case RangeMarkerBarItem:
733 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
734 _drag = new CursorDrag (this, &playhead_cursor->canvas_item, false);
736 _drag = new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateRangeMarker);
738 _drag->start_grab (event);
742 case CdMarkerBarItem:
744 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
745 _drag = new CursorDrag (this, &playhead_cursor->canvas_item, false);
747 _drag = new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateCDMarker);
749 _drag->start_grab (event);
753 case TransportMarkerBarItem:
755 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
756 _drag = new CursorDrag (this, &playhead_cursor->canvas_item, false);
758 _drag = new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateTransportMarker);
760 _drag->start_grab (event);
769 switch (mouse_mode) {
772 case StartSelectionTrimItem:
774 _drag = new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim);
775 _drag->start_grab (event);
778 case EndSelectionTrimItem:
780 _drag = new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim);
781 _drag->start_grab (event);
785 if (Keyboard::modifier_state_contains
786 (event->button.state, Keyboard::ModifierMask(Keyboard::SecondaryModifier))) {
787 // contains and not equals because I can't use alt as a modifier alone.
788 start_selection_grab (item, event);
789 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
790 /* grab selection for moving */
792 _drag = new SelectionDrag (this, item, SelectionDrag::SelectionMove);
793 _drag->start_grab (event);
795 /* this was debated, but decided the more common action was to
796 make a new selection */
798 _drag = new SelectionDrag (this, item, SelectionDrag::CreateSelection);
799 _drag->start_grab (event);
805 _drag = new SelectionDrag (this, item, SelectionDrag::CreateSelection);
806 _drag->start_grab (event);
812 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)) &&
813 event->type == GDK_BUTTON_PRESS) {
816 _drag = new RubberbandSelectDrag (this, item);
817 _drag->start_grab (event);
819 } else if (event->type == GDK_BUTTON_PRESS) {
822 case FadeInHandleItem:
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 AutomationTimeAxisViewPtr atv;
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 = boost::dynamic_pointer_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 boost::dynamic_pointer_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 AutomationTimeAxisViewPtr atv = boost::dynamic_pointer_cast<AutomationTimeAxisView> (
1580 static_cast<TimeAxisView*> (item->get_data ("trackview"))
1585 clear_entered_track = false;
1586 set_entered_track (atv);
1592 case RangeMarkerBarItem:
1593 case TransportMarkerBarItem:
1594 case CdMarkerBarItem:
1597 if (is_drawable()) {
1598 track_canvas->get_window()->set_cursor (*timebar_cursor);
1603 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1606 entered_marker = marker;
1607 marker->set_color_rgba (ARDOUR_UI::config()->canvasvar_EnteredMarker.get());
1609 case MeterMarkerItem:
1610 case TempoMarkerItem:
1611 if (is_drawable()) {
1612 track_canvas->get_window()->set_cursor (*timebar_cursor);
1615 case FadeInHandleItem:
1616 case FadeOutHandleItem:
1617 if (mouse_mode == MouseObject) {
1618 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1620 rect->property_fill_color_rgba() = 0;
1621 rect->property_outline_pixels() = 1;
1630 /* second pass to handle entered track status in a comprehensible way.
1633 switch (item_type) {
1635 case AutomationLineItem:
1636 case ControlPointItem:
1637 /* these do not affect the current entered track state */
1638 clear_entered_track = false;
1641 case AutomationTrackItem:
1642 /* handled above already */
1646 set_entered_track (TimeAxisViewPtr ());
1654 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1663 switch (item_type) {
1664 case ControlPointItem:
1665 cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
1666 if (cp->line().the_list()->interpolation() != AutomationList::Discrete) {
1667 if (cp->line().npoints() > 1 && !cp->selected()) {
1668 cp->set_visible (false);
1672 if (is_drawable()) {
1673 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1676 hide_verbose_canvas_cursor ();
1679 case RegionViewNameHighlight:
1680 case StartSelectionTrimItem:
1681 case EndSelectionTrimItem:
1682 case PlayheadCursorItem:
1685 case ImageFrameHandleStartItem:
1686 case ImageFrameHandleEndItem:
1687 case MarkerViewHandleStartItem:
1688 case MarkerViewHandleEndItem:
1691 if (is_drawable()) {
1692 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1697 case AutomationLineItem:
1698 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1700 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1702 line->property_fill_color_rgba() = al->get_line_color();
1704 if (is_drawable()) {
1705 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1709 case RegionViewName:
1710 /* see enter_handler() for notes */
1711 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1712 if (is_drawable() && mouse_mode == MouseObject) {
1713 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1718 case RangeMarkerBarItem:
1719 case TransportMarkerBarItem:
1720 case CdMarkerBarItem:
1724 if (is_drawable()) {
1725 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1730 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1734 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
1735 location_flags_changed (loc, this);
1738 case MeterMarkerItem:
1739 case TempoMarkerItem:
1741 if (is_drawable()) {
1742 track_canvas->get_window()->set_cursor (*timebar_cursor);
1747 case FadeInHandleItem:
1748 case FadeOutHandleItem:
1749 rv = static_cast<RegionView*>(item->get_data ("regionview"));
1751 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1753 rect->property_fill_color_rgba() = rv->get_fill_color();
1754 rect->property_outline_pixels() = 0;
1759 case AutomationTrackItem:
1760 if (is_drawable()) {
1761 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1762 clear_entered_track = true;
1763 Glib::signal_idle().connect (mem_fun(*this, &Editor::left_automation_track));
1775 Editor::left_automation_track ()
1777 if (clear_entered_track) {
1778 set_entered_track (TimeAxisViewPtr ());
1779 clear_entered_track = false;
1789 if (scrubbing_direction == 0) {
1791 session->request_locate (_drag->current_pointer_frame(), false);
1792 session->request_transport_speed (0.1);
1793 scrubbing_direction = 1;
1797 if (last_scrub_x > _drag->current_pointer_x()) {
1799 /* pointer moved to the left */
1801 if (scrubbing_direction > 0) {
1803 /* we reversed direction to go backwards */
1806 scrub_reverse_distance += (int) (last_scrub_x - _drag->current_pointer_x());
1810 /* still moving to the left (backwards) */
1812 scrub_reversals = 0;
1813 scrub_reverse_distance = 0;
1815 delta = 0.01 * (last_scrub_x - _drag->current_pointer_x());
1816 session->request_transport_speed (session->transport_speed() - delta);
1820 /* pointer moved to the right */
1822 if (scrubbing_direction < 0) {
1823 /* we reversed direction to go forward */
1826 scrub_reverse_distance += (int) (_drag->current_pointer_x() - last_scrub_x);
1829 /* still moving to the right */
1831 scrub_reversals = 0;
1832 scrub_reverse_distance = 0;
1834 delta = 0.01 * (_drag->current_pointer_x() - last_scrub_x);
1835 session->request_transport_speed (session->transport_speed() + delta);
1839 /* if there have been more than 2 opposite motion moves detected, or one that moves
1840 back more than 10 pixels, reverse direction
1843 if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
1845 if (scrubbing_direction > 0) {
1846 /* was forwards, go backwards */
1847 session->request_transport_speed (-0.1);
1848 scrubbing_direction = -1;
1850 /* was backwards, go forwards */
1851 session->request_transport_speed (0.1);
1852 scrubbing_direction = 1;
1855 scrub_reverse_distance = 0;
1856 scrub_reversals = 0;
1860 last_scrub_x = _drag->current_pointer_x();
1864 Editor::motion_handler (ArdourCanvas::Item* item, GdkEvent* event, bool from_autoscroll)
1866 if (event->motion.is_hint) {
1869 /* We call this so that MOTION_NOTIFY events continue to be
1870 delivered to the canvas. We need to do this because we set
1871 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
1872 the density of the events, at the expense of a round-trip
1873 to the server. Given that this will mostly occur on cases
1874 where DISPLAY = :0.0, and given the cost of what the motion
1875 event might do, its a good tradeoff.
1878 track_canvas->get_pointer (x, y);
1881 if (current_stepping_trackview) {
1882 /* don't keep the persistent stepped trackview if the mouse moves */
1883 current_stepping_trackview.reset ();
1884 step_timeout.disconnect ();
1887 if (session && session->actively_recording()) {
1888 /* Sorry. no dragging stuff around while we record */
1892 bool handled = false;
1894 handled = _drag->motion_handler (event, from_autoscroll);
1901 track_canvas_motion (event);
1906 Editor::remove_gain_control_point (ArdourCanvas::Item*item, GdkEvent* event)
1908 ControlPoint* control_point;
1910 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
1911 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
1915 // We shouldn't remove the first or last gain point
1916 if (control_point->line().is_last_point(*control_point) ||
1917 control_point->line().is_first_point(*control_point)) {
1921 control_point->line().remove_point (*control_point);
1925 Editor::remove_control_point (ArdourCanvas::Item* item, GdkEvent* event)
1927 ControlPoint* control_point;
1929 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
1930 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
1934 control_point->line().remove_point (*control_point);
1938 Editor::edit_control_point (ArdourCanvas::Item* item)
1940 ControlPoint* p = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"));
1943 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
1947 ControlPointDialog d (p);
1948 d.set_position (Gtk::WIN_POS_MOUSE);
1951 if (d.run () != RESPONSE_ACCEPT) {
1955 p->line().modify_point_y (*p, d.get_y_fraction ());
1960 Editor::visible_order_range (int* low, int* high) const
1962 *low = TimeAxisView::max_order ();
1965 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
1967 RouteTimeAxisViewPtr rtv = boost::dynamic_pointer_cast<RouteTimeAxisView> (*i);
1969 if (!rtv->hidden()) {
1971 if (*high < rtv->order()) {
1972 *high = rtv->order ();
1975 if (*low > rtv->order()) {
1976 *low = rtv->order ();
1983 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
1985 /* Either add to or set the set the region selection, unless
1986 this is an alignment click (control used)
1989 if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
1990 TimeAxisViewPtr tv = rv.get_time_axis_view();
1991 RouteTimeAxisViewPtr rtv = boost::dynamic_pointer_cast<RouteTimeAxisView> (tv);
1993 if (rtv && rtv->is_track()) {
1994 speed = rtv->get_diskstream()->speed();
1997 nframes64_t where = get_preferred_edit_position();
2001 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
2003 align_region (rv.region(), SyncPoint, (nframes64_t) (where * speed));
2005 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
2007 align_region (rv.region(), End, (nframes64_t) (where * speed));
2011 align_region (rv.region(), Start, (nframes64_t) (where * speed));
2018 Editor::show_verbose_time_cursor (nframes64_t frame, double offset, double xpos, double ypos)
2024 nframes64_t frame_rate;
2033 if (Profile->get_sae() || Profile->get_small_screen()) {
2034 m = ARDOUR_UI::instance()->primary_clock.mode();
2036 m = ARDOUR_UI::instance()->secondary_clock.mode();
2040 case AudioClock::BBT:
2041 session->bbt_time (frame, bbt);
2042 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, bbt.bars, bbt.beats, bbt.ticks);
2045 case AudioClock::SMPTE:
2046 session->smpte_time (frame, smpte);
2047 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
2050 case AudioClock::MinSec:
2051 /* XXX this is copied from show_verbose_duration_cursor() */
2052 frame_rate = session->frame_rate();
2053 hours = frame / (frame_rate * 3600);
2054 frame = frame % (frame_rate * 3600);
2055 mins = frame / (frame_rate * 60);
2056 frame = frame % (frame_rate * 60);
2057 secs = (float) frame / (float) frame_rate;
2058 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
2062 snprintf (buf, sizeof(buf), "%" PRIi64, frame);
2066 if (xpos >= 0 && ypos >=0) {
2067 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
2070 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);
2072 show_verbose_canvas_cursor ();
2076 Editor::show_verbose_duration_cursor (nframes64_t start, nframes64_t end, double offset, double xpos, double ypos)
2083 nframes64_t distance, frame_rate;
2085 Meter meter_at_start(session->tempo_map().meter_at(start));
2093 if (Profile->get_sae() || Profile->get_small_screen()) {
2094 m = ARDOUR_UI::instance()->primary_clock.mode ();
2096 m = ARDOUR_UI::instance()->secondary_clock.mode ();
2100 case AudioClock::BBT:
2101 session->bbt_time (start, sbbt);
2102 session->bbt_time (end, ebbt);
2105 /* XXX this computation won't work well if the
2106 user makes a selection that spans any meter changes.
2109 ebbt.bars -= sbbt.bars;
2110 if (ebbt.beats >= sbbt.beats) {
2111 ebbt.beats -= sbbt.beats;
2114 ebbt.beats = int(meter_at_start.beats_per_bar()) + ebbt.beats - sbbt.beats;
2116 if (ebbt.ticks >= sbbt.ticks) {
2117 ebbt.ticks -= sbbt.ticks;
2120 ebbt.ticks = int(Meter::ticks_per_beat) + ebbt.ticks - sbbt.ticks;
2123 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, ebbt.bars, ebbt.beats, ebbt.ticks);
2126 case AudioClock::SMPTE:
2127 session->smpte_duration (end - start, smpte);
2128 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
2131 case AudioClock::MinSec:
2132 /* XXX this stuff should be elsewhere.. */
2133 distance = end - start;
2134 frame_rate = session->frame_rate();
2135 hours = distance / (frame_rate * 3600);
2136 distance = distance % (frame_rate * 3600);
2137 mins = distance / (frame_rate * 60);
2138 distance = distance % (frame_rate * 60);
2139 secs = (float) distance / (float) frame_rate;
2140 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
2144 snprintf (buf, sizeof(buf), "%" PRIi64, end - start);
2148 if (xpos >= 0 && ypos >=0) {
2149 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
2152 set_verbose_canvas_cursor (buf, _drag->current_pointer_x() + offset, _drag->current_pointer_y() + offset);
2155 show_verbose_canvas_cursor ();
2159 Editor::collect_new_region_view (RegionView* rv)
2161 latest_regionviews.push_back (rv);
2165 Editor::collect_and_select_new_region_view (RegionView* rv)
2168 latest_regionviews.push_back (rv);
2172 Editor::cancel_selection ()
2174 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2175 (*i)->hide_selection ();
2178 selection->clear ();
2179 clicked_selection = 0;
2184 Editor::single_contents_trim (RegionView& rv, nframes64_t frame_delta, bool left_direction, bool swap_direction, bool obey_snap)
2186 boost::shared_ptr<Region> region (rv.region());
2188 if (region->locked()) {
2192 nframes64_t new_bound;
2195 TimeAxisViewPtr tvp = clicked_axisview;
2196 RouteTimeAxisViewPtr tv = boost::dynamic_pointer_cast<RouteTimeAxisView>(tvp);
2198 if (tv && tv->is_track()) {
2199 speed = tv->get_diskstream()->speed();
2202 if (left_direction) {
2203 if (swap_direction) {
2204 new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
2206 new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
2209 if (swap_direction) {
2210 new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
2212 new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
2217 snap_to (new_bound);
2219 region->trim_start ((nframes64_t) (new_bound * speed), this);
2220 rv.region_changed (StartChanged);
2224 Editor::single_start_trim (RegionView& rv, nframes64_t frame_delta, bool left_direction, bool obey_snap, bool no_overlap)
2226 boost::shared_ptr<Region> region (rv.region());
2228 if (region->locked()) {
2232 nframes64_t new_bound;
2235 TimeAxisViewPtr tvp = clicked_axisview;
2236 RouteTimeAxisViewPtr tv = boost::dynamic_pointer_cast<RouteTimeAxisView>(tvp);
2238 if (tv && tv->is_track()) {
2239 speed = tv->get_diskstream()->speed();
2242 if (left_direction) {
2243 new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
2245 new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
2249 snap_to (new_bound, (left_direction ? 0 : 1));
2252 nframes64_t pre_trim_first_frame = region->first_frame();
2254 region->trim_front ((nframes64_t) (new_bound * speed), this);
2257 //Get the next region on the left of this region and shrink/expand it.
2258 boost::shared_ptr<Playlist> playlist (region->playlist());
2259 boost::shared_ptr<Region> region_left = playlist->find_next_region (pre_trim_first_frame, End, 0);
2261 bool regions_touching = false;
2263 if (region_left != 0 && (pre_trim_first_frame == region_left->last_frame() + 1)){
2264 regions_touching = true;
2267 //Only trim region on the left if the first frame has gone beyond the left region's last frame.
2268 if (region_left != 0 &&
2269 (region_left->last_frame() > region->first_frame() || regions_touching))
2271 region_left->trim_end(region->first_frame(), this);
2277 rv.region_changed (Change (LengthChanged|PositionChanged|StartChanged));
2281 Editor::single_end_trim (RegionView& rv, nframes64_t frame_delta, bool left_direction, bool obey_snap, bool no_overlap)
2283 boost::shared_ptr<Region> region (rv.region());
2285 if (region->locked()) {
2289 nframes64_t new_bound;
2292 TimeAxisViewPtr tvp = clicked_axisview;
2293 RouteTimeAxisViewPtr tv = boost::dynamic_pointer_cast<RouteTimeAxisView>(tvp);
2295 if (tv && tv->is_track()) {
2296 speed = tv->get_diskstream()->speed();
2299 if (left_direction) {
2300 new_bound = (nframes64_t) ((region->last_frame() + 1)/speed) - frame_delta;
2302 new_bound = (nframes64_t) ((region->last_frame() + 1)/speed) + frame_delta;
2306 snap_to (new_bound);
2309 nframes64_t pre_trim_last_frame = region->last_frame();
2311 region->trim_end ((nframes64_t) (new_bound * speed), this);
2314 //Get the next region on the right of this region and shrink/expand it.
2315 boost::shared_ptr<Playlist> playlist (region->playlist());
2316 boost::shared_ptr<Region> region_right = playlist->find_next_region (pre_trim_last_frame, Start, 1);
2318 bool regions_touching = false;
2320 if (region_right != 0 && (pre_trim_last_frame == region_right->first_frame() - 1)){
2321 regions_touching = true;
2324 //Only trim region on the right if the last frame has gone beyond the right region's first frame.
2325 if (region_right != 0 &&
2326 (region_right->first_frame() < region->last_frame() || regions_touching))
2328 region_right->trim_front(region->last_frame() + 1, this);
2331 rv.region_changed (Change (LengthChanged|PositionChanged|StartChanged));
2334 rv.region_changed (LengthChanged);
2340 Editor::point_trim (GdkEvent* event)
2342 RegionView* rv = clicked_regionview;
2344 nframes64_t new_bound = _drag->current_pointer_frame();
2346 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2347 snap_to (new_bound);
2350 /* Choose action dependant on which button was pressed */
2351 switch (event->button.button) {
2353 begin_reversible_command (_("Start point trim"));
2355 if (selection->selected (rv)) {
2356 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
2357 i != selection->regions.by_layer().end(); ++i)
2360 cerr << "region view contains null region" << endl;
2363 if (!(*i)->region()->locked()) {
2364 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
2365 XMLNode &before = pl->get_state();
2367 (*i)->region()->trim_front (new_bound, this);
2369 XMLNode &after = pl->get_state();
2370 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
2375 if (!rv->region()->locked()) {
2376 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
2377 XMLNode &before = pl->get_state();
2378 rv->region()->trim_front (new_bound, this);
2379 XMLNode &after = pl->get_state();
2380 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
2384 commit_reversible_command();
2388 begin_reversible_command (_("End point trim"));
2390 if (selection->selected (rv)) {
2392 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
2394 if (!(*i)->region()->locked()) {
2395 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
2396 XMLNode &before = pl->get_state();
2397 (*i)->region()->trim_end (new_bound, this);
2398 XMLNode &after = pl->get_state();
2399 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
2405 if (!rv->region()->locked()) {
2406 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
2407 XMLNode &before = pl->get_state();
2408 rv->region()->trim_end (new_bound, this);
2409 XMLNode &after = pl->get_state();
2410 session->add_command (new MementoCommand<Playlist>(*pl.get(), &before, &after));
2414 commit_reversible_command();
2423 Editor::thaw_region_after_trim (RegionView& rv)
2425 boost::shared_ptr<Region> region (rv.region());
2427 if (region->locked()) {
2431 region->thaw (_("trimmed region"));
2433 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(&rv);
2436 arv->unhide_envelope ();
2441 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* event)
2446 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2447 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2451 Location* location = find_location_from_marker (marker, is_start);
2452 location->set_hidden (true, this);
2457 Editor::reposition_zoom_rect (nframes64_t start, nframes64_t end)
2459 double x1 = frame_to_pixel (start);
2460 double x2 = frame_to_pixel (end);
2461 double y2 = full_canvas_height - 1.0;
2463 zoom_rect->property_x1() = x1;
2464 zoom_rect->property_y1() = 1.0;
2465 zoom_rect->property_x2() = x2;
2466 zoom_rect->property_y2() = y2;
2471 Editor::mouse_rename_region (ArdourCanvas::Item* item, GdkEvent* event)
2473 using namespace Gtkmm2ext;
2475 ArdourPrompter prompter (false);
2477 prompter.set_prompt (_("Name for region:"));
2478 prompter.set_initial_text (clicked_regionview->region()->name());
2479 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
2480 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
2481 prompter.show_all ();
2482 switch (prompter.run ()) {
2483 case Gtk::RESPONSE_ACCEPT:
2485 prompter.get_result(str);
2487 clicked_regionview->region()->set_name (str);
2496 Editor::mouse_brush_insert_region (RegionView* rv, nframes64_t pos)
2498 /* no brushing without a useful snap setting */
2500 switch (snap_mode) {
2502 return; /* can't work because it allows region to be placed anywhere */
2507 switch (snap_type) {
2515 /* don't brush a copy over the original */
2517 if (pos == rv->region()->position()) {
2521 RouteTimeAxisViewPtr rtv = boost::dynamic_pointer_cast<RouteTimeAxisView> (rv->get_time_axis_view());
2523 if (rtv == 0 || !rtv->is_track()) {
2527 boost::shared_ptr<Playlist> playlist = rtv->playlist();
2528 double speed = rtv->get_diskstream()->speed();
2530 XMLNode &before = playlist->get_state();
2531 playlist->add_region (RegionFactory::create (rv->region()), (nframes64_t) (pos * speed));
2532 XMLNode &after = playlist->get_state();
2533 session->add_command(new MementoCommand<Playlist>(*playlist.get(), &before, &after));
2535 // playlist is frozen, so we have to update manually
2537 playlist->Modified(); /* EMIT SIGNAL */
2541 Editor::track_height_step_timeout ()
2543 if (get_microseconds() - last_track_height_step_timestamp < 250000) {
2544 current_stepping_trackview.reset ();
2551 Editor::start_region_grab (ArdourCanvas::Item* item, GdkEvent* event, RegionView* region_view)
2553 assert (region_view);
2555 _region_motion_group->raise_to_top ();
2557 assert (_drag == 0);
2559 if (Config->get_edit_mode() == Splice) {
2560 _drag = new RegionSpliceDrag (this, item, region_view, selection->regions.by_layer());
2562 RegionSelection s = get_equivalent_regions (selection->regions, RouteGroup::Edit);
2563 _drag = new RegionMoveDrag (this, item, region_view, s.by_layer(), false, false);
2566 _drag->start_grab (event);
2568 begin_reversible_command (_("move region(s)"));
2570 /* sync the canvas to what we think is its current state */
2571 update_canvas_now();
2575 Editor::start_region_copy_grab (ArdourCanvas::Item* item, GdkEvent* event, RegionView* region_view)
2577 assert (region_view);
2578 assert (_drag == 0);
2580 _region_motion_group->raise_to_top ();
2582 RegionSelection s = get_equivalent_regions (selection->regions, RouteGroup::Edit);
2583 _drag = new RegionMoveDrag (this, item, region_view, s.by_layer(), false, true);
2584 _drag->start_grab(event);
2588 Editor::start_region_brush_grab (ArdourCanvas::Item* item, GdkEvent* event, RegionView* region_view)
2590 assert (region_view);
2591 assert (_drag == 0);
2593 if (Config->get_edit_mode() == Splice) {
2597 RegionSelection s = get_equivalent_regions (selection->regions, RouteGroup::Edit);
2598 _drag = new RegionMoveDrag (this, item, region_view, s.by_layer(), true, false);
2599 _drag->start_grab (event);
2601 begin_reversible_command (_("Drag region brush"));
2604 /** Start a grab where a time range is selected, track(s) are selected, and the
2605 * user clicks and drags a region with a modifier in order to create a new region containing
2606 * the section of the clicked region that lies within the time range.
2609 Editor::start_selection_grab (ArdourCanvas::Item* item, GdkEvent* event)
2611 if (clicked_regionview == 0) {
2615 /* lets try to create new Region for the selection */
2617 vector<boost::shared_ptr<Region> > new_regions;
2618 create_region_from_selection (new_regions);
2620 if (new_regions.empty()) {
2624 /* XXX fix me one day to use all new regions */
2626 boost::shared_ptr<Region> region (new_regions.front());
2628 /* add it to the current stream/playlist.
2630 tricky: the streamview for the track will add a new regionview. we will
2631 catch the signal it sends when it creates the regionview to
2632 set the regionview we want to then drag.
2635 latest_regionviews.clear();
2636 sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
2638 /* A selection grab currently creates two undo/redo operations, one for
2639 creating the new region and another for moving it.
2642 begin_reversible_command (_("selection grab"));
2644 boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
2646 XMLNode *before = &(playlist->get_state());
2647 clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
2648 XMLNode *after = &(playlist->get_state());
2649 session->add_command(new MementoCommand<Playlist>(*playlist, before, after));
2651 commit_reversible_command ();
2655 if (latest_regionviews.empty()) {
2656 /* something went wrong */
2660 /* we need to deselect all other regionviews, and select this one
2661 i'm ignoring undo stuff, because the region creation will take care of it
2663 selection->set (latest_regionviews);
2665 assert (_drag == 0);
2666 _drag = new RegionMoveDrag (this, latest_regionviews.front()->get_canvas_group(), latest_regionviews.front(), latest_regionviews, false, false);
2667 _drag->start_grab (event);
2671 Editor::break_drag ()
2674 _drag->break_drag ();