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>
31 #include "ardour_ui.h"
33 #include "time_axis_view.h"
34 #include "audio_time_axis.h"
35 #include "regionview.h"
37 #include "streamview.h"
38 #include "region_gain_line.h"
39 #include "automation_time_axis.h"
42 #include "selection.h"
45 #include "rgb_macros.h"
47 #include <ardour/types.h>
48 #include <ardour/route.h>
49 #include <ardour/audio_track.h>
50 #include <ardour/diskstream.h>
51 #include <ardour/playlist.h>
52 #include <ardour/audioplaylist.h>
53 #include <ardour/audioregion.h>
54 #include <ardour/dB.h>
55 #include <ardour/utils.h>
56 #include <ardour/region_factory.h>
63 using namespace ARDOUR;
66 using namespace Editing;
69 Editor::event_frame (GdkEvent* event, double* pcx, double* pcy)
83 switch (event->type) {
84 case GDK_BUTTON_RELEASE:
85 case GDK_BUTTON_PRESS:
86 case GDK_2BUTTON_PRESS:
87 case GDK_3BUTTON_PRESS:
88 track_canvas.w2c(event->button.x, event->button.y, *pcx, *pcy);
90 case GDK_MOTION_NOTIFY:
91 track_canvas.w2c(event->motion.x, event->motion.y, *pcx, *pcy);
93 case GDK_ENTER_NOTIFY:
94 case GDK_LEAVE_NOTIFY:
95 track_canvas.w2c(event->crossing.x, event->crossing.y, *pcx, *pcy);
100 // track_canvas.w2c(event->key.x, event->key.y, *pcx, *pcy);
103 warning << string_compose (_("Editor::event_frame() used on unhandled event type %1"), event->type) << endmsg;
107 /* note that pixel_to_frame() never returns less than zero, so even if the pixel
108 position is negative (as can be the case with motion events in particular),
109 the frame location is always positive.
112 return pixel_to_frame (*pcx);
116 Editor::mouse_mode_toggled (MouseMode m)
118 if (ignore_mouse_mode_toggle) {
124 if (mouse_select_button.get_active()) {
130 if (mouse_move_button.get_active()) {
136 if (mouse_gain_button.get_active()) {
142 if (mouse_zoom_button.get_active()) {
148 if (mouse_timefx_button.get_active()) {
154 if (mouse_audition_button.get_active()) {
165 Editor::set_mouse_mode (MouseMode m, bool force)
167 if (drag_info.item) {
171 if (!force && m == mouse_mode) {
179 if (mouse_mode != MouseRange) {
181 /* in all modes except range, hide the range selection,
182 show the object (region) selection.
185 for (AudioRegionSelection::iterator i = selection->audio_regions.begin(); i != selection->audio_regions.end(); ++i) {
186 (*i)->set_should_show_selection (true);
188 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
189 (*i)->hide_selection ();
195 in range mode,show the range selection.
198 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
199 if ((*i)->selected()) {
200 (*i)->show_selection (selection->time);
205 /* XXX the hack of unsetting all other buttongs should go
206 away once GTK2 allows us to use regular radio buttons drawn like
207 normal buttons, rather than my silly GroupedButton hack.
210 ignore_mouse_mode_toggle = true;
212 switch (mouse_mode) {
214 mouse_select_button.set_active (true);
215 current_canvas_cursor = selector_cursor;
219 mouse_move_button.set_active (true);
220 current_canvas_cursor = grabber_cursor;
224 mouse_gain_button.set_active (true);
225 current_canvas_cursor = cross_hair_cursor;
229 mouse_zoom_button.set_active (true);
230 current_canvas_cursor = zoom_cursor;
234 mouse_timefx_button.set_active (true);
235 current_canvas_cursor = time_fx_cursor; // just use playhead
239 mouse_audition_button.set_active (true);
240 current_canvas_cursor = speaker_cursor;
244 ignore_mouse_mode_toggle = false;
247 track_canvas.get_window()->set_cursor(*current_canvas_cursor);
252 Editor::step_mouse_mode (bool next)
254 switch (current_mouse_mode()) {
256 if (next) set_mouse_mode (MouseRange);
257 else set_mouse_mode (MouseTimeFX);
261 if (next) set_mouse_mode (MouseZoom);
262 else set_mouse_mode (MouseObject);
266 if (next) set_mouse_mode (MouseGain);
267 else set_mouse_mode (MouseRange);
271 if (next) set_mouse_mode (MouseTimeFX);
272 else set_mouse_mode (MouseZoom);
276 if (next) set_mouse_mode (MouseAudition);
277 else set_mouse_mode (MouseGain);
281 if (next) set_mouse_mode (MouseObject);
282 else set_mouse_mode (MouseTimeFX);
288 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
290 jack_nframes_t where = event_frame (event, 0, 0);
292 track_canvas.grab_focus();
294 if (session && session->actively_recording()) {
298 /* in object/audition/timefx mode, any button press sets
299 the selection if the object can be selected. this is a
300 bit of hack, because we want to avoid this if the
301 mouse operation is a region alignment.
304 if (((mouse_mode == MouseObject) ||
305 (mouse_mode == MouseAudition && item_type == RegionItem) ||
306 (mouse_mode == MouseTimeFX && item_type == RegionItem)) &&
307 event->type == GDK_BUTTON_PRESS &&
308 event->button.button <= 3) {
313 /* not dbl-click or triple-click */
317 set_selected_regionview_from_click (Keyboard::selection_type (event->button.state), true);
320 case AudioRegionViewNameHighlight:
321 case AudioRegionViewName:
322 if ((rv = static_cast<AudioRegionView *> (item->get_data ("regionview"))) != 0) {
323 set_selected_regionview_from_click (Keyboard::selection_type (event->button.state), true);
327 case GainAutomationControlPointItem:
328 case PanAutomationControlPointItem:
329 case RedirectAutomationControlPointItem:
330 if ((cp = static_cast<ControlPoint *> (item->get_data ("control_point"))) != 0) {
331 set_selected_control_point_from_click (Keyboard::selection_type (event->button.state), false);
336 set_selected_track_from_click (Keyboard::selection_type (event->button.state), true, true);
339 case AutomationTrackItem:
347 #define SELECT_TRACK_FROM_CANVAS_IN_RANGE_MODE
348 #ifdef SELECT_TRACK_FROM_CANVAS_IN_RANGE_MODE
349 /* in range mode, button 1/2/3 press potentially selects a track */
351 if (mouse_mode == MouseRange &&
352 event->type == GDK_BUTTON_PRESS &&
353 event->button.button <= 3) {
360 case AutomationTrackItem:
361 set_selected_track_from_click (Keyboard::selection_type (event->button.state), true, true);
364 case AudioRegionViewNameHighlight:
365 case AudioRegionViewName:
366 rv = reinterpret_cast<AudioRegionView *> (item->get_data ("regionview"));
373 if (drag_info.item == 0 &&
374 (Keyboard::is_delete_event (&event->button) ||
375 Keyboard::is_context_menu_event (&event->button) ||
376 Keyboard::is_edit_event (&event->button))) {
378 /* handled by button release */
382 switch (event->button.button) {
385 if (event->type == GDK_BUTTON_PRESS) {
387 if (drag_info.item) {
388 drag_info.item->ungrab (event->button.time);
391 /* single mouse clicks on any of these item types operate
392 independent of mouse mode, mostly because they are
393 not on the main track canvas or because we want
399 case PlayheadCursorItem:
400 start_cursor_grab (item, event);
404 if (Keyboard::modifier_state_equals (event->button.state,
405 Keyboard::ModifierMask(Keyboard::Control|Keyboard::Shift))) {
406 hide_marker (item, event);
408 start_marker_grab (item, event);
412 case TempoMarkerItem:
413 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
414 start_tempo_marker_copy_grab (item, event);
416 start_tempo_marker_grab (item, event);
420 case MeterMarkerItem:
421 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
422 start_meter_marker_copy_grab (item, event);
424 start_meter_marker_grab (item, event);
434 case RangeMarkerBarItem:
435 start_range_markerbar_op (item, event, CreateRangeMarker);
438 case TransportMarkerBarItem:
439 start_range_markerbar_op (item, event, CreateTransportMarker);
448 switch (mouse_mode) {
451 case StartSelectionTrimItem:
452 start_selection_op (item, event, SelectionStartTrim);
455 case EndSelectionTrimItem:
456 start_selection_op (item, event, SelectionEndTrim);
460 if (Keyboard::modifier_state_contains
461 (event->button.state, Keyboard::ModifierMask(Keyboard::Alt))) {
462 // contains and not equals because I can't use alt as a modifier alone.
463 start_selection_grab (item, event);
464 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
465 /* grab selection for moving */
466 start_selection_op (item, event, SelectionMove);
469 /* this was debated, but decided the more common action was to
470 make a new selection */
471 start_selection_op (item, event, CreateSelection);
476 start_selection_op (item, event, CreateSelection);
482 if (Keyboard::modifier_state_contains (event->button.state,
483 Keyboard::ModifierMask(Keyboard::Control|Keyboard::Alt))
484 && event->type == GDK_BUTTON_PRESS) {
486 start_rubberband_select (item, event);
488 } else if (event->type == GDK_BUTTON_PRESS) {
491 case FadeInHandleItem:
492 start_fade_in_grab (item, event);
495 case FadeOutHandleItem:
496 start_fade_out_grab (item, event);
500 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
501 start_region_copy_grab (item, event);
502 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
503 start_region_brush_grab (item, event);
505 start_region_grab (item, event);
509 case AudioRegionViewNameHighlight:
510 start_trim (item, event);
514 case AudioRegionViewName:
515 /* rename happens on edit clicks */
516 start_trim (clicked_regionview->get_name_highlight(), event);
520 case GainAutomationControlPointItem:
521 case PanAutomationControlPointItem:
522 case RedirectAutomationControlPointItem:
523 start_control_point_grab (item, event);
527 case GainAutomationLineItem:
528 case PanAutomationLineItem:
529 case RedirectAutomationLineItem:
530 start_line_grab_from_line (item, event);
535 case AutomationTrackItem:
536 start_rubberband_select (item, event);
539 /* <CMT Additions> */
540 case ImageFrameHandleStartItem:
541 imageframe_start_handle_op(item, event) ;
544 case ImageFrameHandleEndItem:
545 imageframe_end_handle_op(item, event) ;
548 case MarkerViewHandleStartItem:
549 markerview_item_start_handle_op(item, event) ;
552 case MarkerViewHandleEndItem:
553 markerview_item_end_handle_op(item, event) ;
556 /* </CMT Additions> */
558 /* <CMT Additions> */
560 start_markerview_grab(item, event) ;
563 start_imageframe_grab(item, event) ;
565 /* </CMT Additions> */
577 // start_line_grab_from_regionview (item, event);
580 case GainControlPointItem:
581 start_control_point_grab (item, event);
585 start_line_grab_from_line (item, event);
588 case GainAutomationControlPointItem:
589 case PanAutomationControlPointItem:
590 case RedirectAutomationControlPointItem:
591 start_control_point_grab (item, event);
602 case GainAutomationControlPointItem:
603 case PanAutomationControlPointItem:
604 case RedirectAutomationControlPointItem:
605 start_control_point_grab (item, event);
608 case GainAutomationLineItem:
609 case PanAutomationLineItem:
610 case RedirectAutomationLineItem:
611 start_line_grab_from_line (item, event);
615 // XXX need automation mode to identify which
617 // start_line_grab_from_regionview (item, event);
627 if (event->type == GDK_BUTTON_PRESS) {
628 start_mouse_zoom (item, event);
635 if (item_type == RegionItem) {
636 start_time_fx (item, event);
641 /* handled in release */
650 switch (mouse_mode) {
652 if (event->type == GDK_BUTTON_PRESS) {
655 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
656 start_region_copy_grab (item, event);
658 start_region_grab (item, event);
662 case GainAutomationControlPointItem:
663 case PanAutomationControlPointItem:
664 case RedirectAutomationControlPointItem:
665 start_control_point_grab (item, event);
676 case AudioRegionViewNameHighlight:
677 start_trim (item, event);
681 case AudioRegionViewName:
682 start_trim (clicked_regionview->get_name_highlight(), event);
693 if (event->type == GDK_BUTTON_PRESS) {
694 /* relax till release */
701 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
702 temporal_zoom_session();
704 temporal_zoom_to_frame (true, event_frame(event));
719 switch (mouse_mode) {
721 //temporal_zoom_to_frame (true, where);
722 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
723 temporal_zoom_to_frame (true, where);
726 temporal_zoom_step (true);
731 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::Alt))) {
732 scroll_backward (0.6f);
735 else if (Keyboard::no_modifier_keys_pressed (&event->button)) {
736 scroll_tracks_up_line ();
738 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
739 if (clicked_trackview) {
740 if (!current_stepping_trackview) {
741 step_timeout = Glib::signal_timeout().connect (mem_fun(*this, &Editor::track_height_step_timeout), 500);
742 current_stepping_trackview = clicked_trackview;
744 gettimeofday (&last_track_height_step_timestamp, 0);
745 current_stepping_trackview->step_height (true);
748 else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
749 temporal_zoom_to_frame (true, where);
756 switch (mouse_mode) {
758 // temporal_zoom_to_frame (false, where);
759 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
760 temporal_zoom_to_frame (false, where);
763 temporal_zoom_step (false);
768 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::Alt))) {
769 scroll_forward (0.6f);
772 else if (Keyboard::no_modifier_keys_pressed (&event->button)) {
773 scroll_tracks_down_line ();
775 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
776 if (clicked_trackview) {
777 if (!current_stepping_trackview) {
778 step_timeout = Glib::signal_timeout().connect (mem_fun(*this, &Editor::track_height_step_timeout), 500);
779 current_stepping_trackview = clicked_trackview;
781 gettimeofday (&last_track_height_step_timestamp, 0);
782 current_stepping_trackview->step_height (false);
784 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
785 temporal_zoom_to_frame (false, where);
800 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
802 jack_nframes_t where = event_frame (event, 0, 0);
804 /* no action if we're recording */
806 if (session && session->actively_recording()) {
810 /* first, see if we're finishing a drag ... */
812 if (drag_info.item) {
813 if (end_grab (item, event)) {
814 /* grab dragged, so do nothing else */
819 /* edit events get handled here */
821 if (drag_info.item == 0 && Keyboard::is_edit_event (&event->button)) {
827 case TempoMarkerItem:
828 edit_tempo_marker (item);
831 case MeterMarkerItem:
832 edit_meter_marker (item);
835 case AudioRegionViewName:
836 if (clicked_regionview->name_active()) {
837 return mouse_rename_region (item, event);
847 /* context menu events get handled here */
849 if (Keyboard::is_context_menu_event (&event->button)) {
851 if (drag_info.item == 0) {
853 /* no matter which button pops up the context menu, tell the menu
854 widget to use button 1 to drive menu selection.
859 case FadeInHandleItem:
861 case FadeOutHandleItem:
862 popup_fade_context_menu (1, event->button.time, item, item_type);
866 popup_track_context_menu (1, event->button.time, item_type, false, where);
870 case AudioRegionViewNameHighlight:
871 case AudioRegionViewName:
872 popup_track_context_menu (1, event->button.time, item_type, false, where);
876 popup_track_context_menu (1, event->button.time, item_type, true, where);
879 case AutomationTrackItem:
880 popup_track_context_menu (1, event->button.time, item_type, false, where);
884 case RangeMarkerBarItem:
885 case TransportMarkerBarItem:
888 popup_ruler_menu (pixel_to_frame(event->button.x), item_type);
892 marker_context_menu (&event->button, item);
895 case TempoMarkerItem:
896 tm_marker_context_menu (&event->button, item);
899 case MeterMarkerItem:
900 tm_marker_context_menu (&event->button, item);
903 case CrossfadeViewItem:
904 popup_track_context_menu (1, event->button.time, item_type, false, where);
907 /* <CMT Additions> */
909 popup_imageframe_edit_menu(1, event->button.time, item, true) ;
911 case ImageFrameTimeAxisItem:
912 popup_imageframe_edit_menu(1, event->button.time, item, false) ;
915 popup_marker_time_axis_edit_menu(1, event->button.time, item, true) ;
917 case MarkerTimeAxisItem:
918 popup_marker_time_axis_edit_menu(1, event->button.time, item, false) ;
920 /* <CMT Additions> */
931 /* delete events get handled here */
933 if (drag_info.item == 0 && Keyboard::is_delete_event (&event->button)) {
936 case TempoMarkerItem:
937 remove_tempo_marker (item);
940 case MeterMarkerItem:
941 remove_meter_marker (item);
945 remove_marker (*item, event);
949 if (mouse_mode == MouseObject) {
950 remove_clicked_region ();
954 case GainControlPointItem:
955 if (mouse_mode == MouseGain) {
956 remove_gain_control_point (item, event);
960 case GainAutomationControlPointItem:
961 case PanAutomationControlPointItem:
962 case RedirectAutomationControlPointItem:
963 remove_control_point (item, event);
972 switch (event->button.button) {
976 /* see comments in button_press_handler */
978 case PlayheadCursorItem:
981 case GainAutomationLineItem:
982 case PanAutomationLineItem:
983 case RedirectAutomationLineItem:
984 case StartSelectionTrimItem:
985 case EndSelectionTrimItem:
989 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
990 snap_to (where, 0, true);
992 mouse_add_new_marker (where);
996 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
999 mouse_add_new_tempo_event (where);
1003 mouse_add_new_meter_event (pixel_to_frame (event->button.x));
1011 switch (mouse_mode) {
1013 switch (item_type) {
1014 case AutomationTrackItem:
1015 dynamic_cast<AutomationTimeAxisView*>(clicked_trackview)->add_automation_event
1029 switch (item_type) {
1031 clicked_regionview->add_gain_point_event (item, event);
1035 case AutomationTrackItem:
1036 dynamic_cast<AutomationTimeAxisView*>(clicked_trackview)->
1037 add_automation_event (item, event, where, event->button.y);
1046 switch (item_type) {
1048 audition_selected_region ();
1065 switch (mouse_mode) {
1068 switch (item_type) {
1070 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
1072 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::Shift|Keyboard::Alt))) {
1075 // Button2 click is unused
1088 // x_style_paste (where, 1.0);
1108 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1114 switch (item_type) {
1115 case GainControlPointItem:
1116 if (mouse_mode == MouseGain) {
1117 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1118 cp->set_visible (true);
1122 at_y = cp->get_y ();
1123 cp->item->i2w (at_x, at_y);
1127 fraction = 1.0 - (cp->get_y() / cp->line.height());
1129 set_verbose_canvas_cursor (cp->line.get_verbose_cursor_string (fraction), at_x, at_y);
1130 show_verbose_canvas_cursor ();
1132 if (is_drawable()) {
1133 track_canvas.get_window()->set_cursor (*fader_cursor);
1138 case GainAutomationControlPointItem:
1139 case PanAutomationControlPointItem:
1140 case RedirectAutomationControlPointItem:
1141 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1142 cp->set_visible (true);
1146 at_y = cp->get_y ();
1147 cp->item->i2w (at_x, at_y);
1151 fraction = 1.0 - (cp->get_y() / cp->line.height());
1153 set_verbose_canvas_cursor (cp->line.get_verbose_cursor_string (fraction), at_x, at_y);
1154 show_verbose_canvas_cursor ();
1156 if (is_drawable()) {
1157 track_canvas.get_window()->set_cursor (*fader_cursor);
1162 if (mouse_mode == MouseGain) {
1163 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1165 line->property_fill_color_rgba() = color_map[cEnteredGainLine];
1166 if (is_drawable()) {
1167 track_canvas.get_window()->set_cursor (*fader_cursor);
1172 case GainAutomationLineItem:
1173 case RedirectAutomationLineItem:
1174 case PanAutomationLineItem:
1176 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1178 line->property_fill_color_rgba() = color_map[cEnteredAutomationLine];
1180 if (is_drawable()) {
1181 track_canvas.get_window()->set_cursor (*fader_cursor);
1185 case AudioRegionViewNameHighlight:
1186 if (is_drawable() && mouse_mode == MouseObject) {
1187 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1191 case StartSelectionTrimItem:
1192 case EndSelectionTrimItem:
1193 /* <CMT Additions> */
1194 case ImageFrameHandleStartItem:
1195 case ImageFrameHandleEndItem:
1196 case MarkerViewHandleStartItem:
1197 case MarkerViewHandleEndItem:
1198 /* </CMT Additions> */
1200 if (is_drawable()) {
1201 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1205 case EditCursorItem:
1206 case PlayheadCursorItem:
1207 if (is_drawable()) {
1208 track_canvas.get_window()->set_cursor (*grabber_cursor);
1212 case AudioRegionViewName:
1214 /* when the name is not an active item, the entire name highlight is for trimming */
1216 if (!reinterpret_cast<AudioRegionView *> (item->get_data ("regionview"))->name_active()) {
1217 if (mouse_mode == MouseObject && is_drawable()) {
1218 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1224 case AutomationTrackItem:
1225 if (is_drawable()) {
1226 Gdk::Cursor *cursor;
1227 switch (mouse_mode) {
1229 cursor = selector_cursor;
1232 cursor = zoom_cursor;
1235 cursor = cross_hair_cursor;
1239 track_canvas.get_window()->set_cursor (*cursor);
1241 AutomationTimeAxisView* atv;
1242 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1243 clear_entered_track = false;
1244 set_entered_track (atv);
1250 case RangeMarkerBarItem:
1251 case TransportMarkerBarItem:
1254 if (is_drawable()) {
1255 time_canvas.get_window()->set_cursor (*timebar_cursor);
1260 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1263 marker->set_color_rgba (color_map[cEnteredMarker]);
1265 case MeterMarkerItem:
1266 case TempoMarkerItem:
1267 if (is_drawable()) {
1268 time_canvas.get_window()->set_cursor (*timebar_cursor);
1271 case FadeInHandleItem:
1272 case FadeOutHandleItem:
1273 if (mouse_mode == MouseObject) {
1274 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1276 rect->property_fill_color_rgba() = 0;
1277 rect->property_outline_pixels() = 1;
1286 /* second pass to handle entered track status in a comprehensible way.
1289 switch (item_type) {
1291 case GainAutomationLineItem:
1292 case RedirectAutomationLineItem:
1293 case PanAutomationLineItem:
1294 case GainControlPointItem:
1295 case GainAutomationControlPointItem:
1296 case PanAutomationControlPointItem:
1297 case RedirectAutomationControlPointItem:
1298 /* these do not affect the current entered track state */
1299 clear_entered_track = false;
1302 case AutomationTrackItem:
1303 /* handled above already */
1307 set_entered_track (0);
1315 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1321 AudioRegionView* rv;
1324 switch (item_type) {
1325 case GainControlPointItem:
1326 case GainAutomationControlPointItem:
1327 case PanAutomationControlPointItem:
1328 case RedirectAutomationControlPointItem:
1329 cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
1330 if (cp->line.npoints() > 1) {
1331 if (!cp->selected) {
1332 cp->set_visible (false);
1336 if (is_drawable()) {
1337 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1340 hide_verbose_canvas_cursor ();
1343 case AudioRegionViewNameHighlight:
1344 case StartSelectionTrimItem:
1345 case EndSelectionTrimItem:
1346 case EditCursorItem:
1347 case PlayheadCursorItem:
1348 /* <CMT Additions> */
1349 case ImageFrameHandleStartItem:
1350 case ImageFrameHandleEndItem:
1351 case MarkerViewHandleStartItem:
1352 case MarkerViewHandleEndItem:
1353 /* </CMT Additions> */
1354 if (is_drawable()) {
1355 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1360 case GainAutomationLineItem:
1361 case RedirectAutomationLineItem:
1362 case PanAutomationLineItem:
1363 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1365 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1367 line->property_fill_color_rgba() = al->get_line_color();
1369 if (is_drawable()) {
1370 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1374 case AudioRegionViewName:
1375 /* see enter_handler() for notes */
1376 if (!reinterpret_cast<AudioRegionView *> (item->get_data ("regionview"))->name_active()) {
1377 if (is_drawable() && mouse_mode == MouseObject) {
1378 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1383 case RangeMarkerBarItem:
1384 case TransportMarkerBarItem:
1388 if (is_drawable()) {
1389 time_canvas.get_window()->set_cursor (*timebar_cursor);
1394 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1397 loc = find_location_from_marker (marker, is_start);
1398 if (loc) location_flags_changed (loc, this);
1400 case MeterMarkerItem:
1401 case TempoMarkerItem:
1403 if (is_drawable()) {
1404 time_canvas.get_window()->set_cursor (*timebar_cursor);
1409 case FadeInHandleItem:
1410 case FadeOutHandleItem:
1411 rv = static_cast<AudioRegionView*>(item->get_data ("regionview"));
1413 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1415 rect->property_fill_color_rgba() = rv->get_fill_color();
1416 rect->property_outline_pixels() = 0;
1421 case AutomationTrackItem:
1422 if (is_drawable()) {
1423 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1424 clear_entered_track = true;
1425 Glib::signal_idle().connect (mem_fun(*this, &Editor::left_automation_track));
1437 Editor::left_automation_track ()
1439 if (clear_entered_track) {
1440 set_entered_track (0);
1441 clear_entered_track = false;
1447 Editor::motion_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type, bool from_autoscroll)
1451 /* We call this so that MOTION_NOTIFY events continue to be
1452 delivered to the canvas. We need to do this because we set
1453 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
1454 the density of the events, at the expense of a round-trip
1455 to the server. Given that this will mostly occur on cases
1456 where DISPLAY = :0.0, and given the cost of what the motion
1457 event might do, its a good tradeoff.
1460 track_canvas.get_pointer (x, y);
1462 if (current_stepping_trackview) {
1463 /* don't keep the persistent stepped trackview if the mouse moves */
1464 current_stepping_trackview = 0;
1465 step_timeout.disconnect ();
1468 if (session && session->actively_recording()) {
1469 /* Sorry. no dragging stuff around while we record */
1473 drag_info.item_type = item_type;
1474 drag_info.current_pointer_frame = event_frame (event, &drag_info.current_pointer_x,
1475 &drag_info.current_pointer_y);
1477 if (!from_autoscroll && drag_info.item) {
1478 /* item != 0 is the best test i can think of for dragging.
1480 if (!drag_info.move_threshold_passsed) {
1482 drag_info.move_threshold_passsed = (abs ((int) (drag_info.current_pointer_x - drag_info.grab_x)) > 4);
1484 // and change the initial grab loc/frame if this drag info wants us to
1486 if (drag_info.want_move_threshold && drag_info.move_threshold_passsed) {
1487 drag_info.grab_frame = drag_info.current_pointer_frame;
1488 drag_info.grab_x = drag_info.current_pointer_x;
1489 drag_info.grab_y = drag_info.current_pointer_y;
1490 drag_info.last_pointer_frame = drag_info.grab_frame;
1491 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
1496 switch (item_type) {
1497 case PlayheadCursorItem:
1498 case EditCursorItem:
1500 case GainControlPointItem:
1501 case RedirectAutomationControlPointItem:
1502 case GainAutomationControlPointItem:
1503 case PanAutomationControlPointItem:
1504 case TempoMarkerItem:
1505 case MeterMarkerItem:
1506 case AudioRegionViewNameHighlight:
1507 case StartSelectionTrimItem:
1508 case EndSelectionTrimItem:
1511 case RedirectAutomationLineItem:
1512 case GainAutomationLineItem:
1513 case PanAutomationLineItem:
1514 case FadeInHandleItem:
1515 case FadeOutHandleItem:
1516 /* <CMT Additions> */
1517 case ImageFrameHandleStartItem:
1518 case ImageFrameHandleEndItem:
1519 case MarkerViewHandleStartItem:
1520 case MarkerViewHandleEndItem:
1521 /* </CMT Additions> */
1522 if (drag_info.item && (event->motion.state & Gdk::BUTTON1_MASK ||
1523 (event->motion.state & Gdk::BUTTON2_MASK))) {
1524 if (!from_autoscroll) {
1525 maybe_autoscroll (event);
1527 (this->*(drag_info.motion_callback)) (item, event);
1536 switch (mouse_mode) {
1541 if (drag_info.item && (event->motion.state & GDK_BUTTON1_MASK ||
1542 (event->motion.state & GDK_BUTTON2_MASK))) {
1543 if (!from_autoscroll) {
1544 maybe_autoscroll (event);
1546 (this->*(drag_info.motion_callback)) (item, event);
1557 track_canvas_motion (event);
1565 Editor::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
1567 if (drag_info.item == 0) {
1568 fatal << _("programming error: start_grab called without drag item") << endmsg;
1574 cursor = grabber_cursor;
1577 // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
1579 if (event->button.button == 2) {
1580 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Alt)) {
1581 drag_info.y_constrained = true;
1582 drag_info.x_constrained = false;
1584 drag_info.y_constrained = false;
1585 drag_info.x_constrained = true;
1588 drag_info.x_constrained = false;
1589 drag_info.y_constrained = false;
1592 drag_info.grab_frame = event_frame(event, &drag_info.grab_x, &drag_info.grab_y);
1593 drag_info.last_pointer_frame = drag_info.grab_frame;
1594 drag_info.current_pointer_frame = drag_info.grab_frame;
1595 drag_info.current_pointer_x = drag_info.grab_x;
1596 drag_info.current_pointer_y = drag_info.grab_y;
1597 drag_info.cumulative_x_drag = 0;
1598 drag_info.cumulative_y_drag = 0;
1599 drag_info.first_move = true;
1600 drag_info.move_threshold_passsed = false;
1601 drag_info.want_move_threshold = false;
1602 drag_info.pointer_frame_offset = 0;
1603 drag_info.brushing = false;
1604 drag_info.copied_location = 0;
1606 drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK,
1608 event->button.time);
1610 if (session && session->transport_rolling()) {
1611 drag_info.was_rolling = true;
1613 drag_info.was_rolling = false;
1616 switch (snap_type) {
1617 case SnapToRegionStart:
1618 case SnapToRegionEnd:
1619 case SnapToRegionSync:
1620 case SnapToRegionBoundary:
1621 build_region_boundary_cache ();
1629 Editor::end_grab (ArdourCanvas::Item* item, GdkEvent* event)
1631 bool did_drag = false;
1633 stop_canvas_autoscroll ();
1635 if (drag_info.item == 0) {
1639 drag_info.item->ungrab (event->button.time);
1641 if (drag_info.finished_callback) {
1642 (this->*(drag_info.finished_callback)) (item, event);
1645 did_drag = !drag_info.first_move;
1647 hide_verbose_canvas_cursor();
1650 drag_info.copy = false;
1651 drag_info.motion_callback = 0;
1652 drag_info.finished_callback = 0;
1653 drag_info.last_trackview = 0;
1654 drag_info.last_frame_position = 0;
1655 drag_info.grab_frame = 0;
1656 drag_info.last_pointer_frame = 0;
1657 drag_info.current_pointer_frame = 0;
1658 drag_info.brushing = false;
1660 if (drag_info.copied_location) {
1661 delete drag_info.copied_location;
1662 drag_info.copied_location = 0;
1669 Editor::set_edit_cursor (GdkEvent* event)
1671 jack_nframes_t pointer_frame = event_frame (event);
1673 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1674 if (snap_type != SnapToEditCursor) {
1675 snap_to (pointer_frame);
1679 edit_cursor->set_position (pointer_frame);
1680 edit_cursor_clock.set (pointer_frame);
1684 Editor::set_playhead_cursor (GdkEvent* event)
1686 jack_nframes_t pointer_frame = event_frame (event);
1688 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1689 snap_to (pointer_frame);
1693 session->request_locate (pointer_frame, session->transport_rolling());
1698 Editor::start_fade_in_grab (ArdourCanvas::Item* item, GdkEvent* event)
1700 drag_info.item = item;
1701 drag_info.motion_callback = &Editor::fade_in_drag_motion_callback;
1702 drag_info.finished_callback = &Editor::fade_in_drag_finished_callback;
1706 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
1707 fatal << _("programming error: fade in canvas item has no regionview data pointer!") << endmsg;
1711 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1713 drag_info.pointer_frame_offset = drag_info.grab_frame - ((jack_nframes_t) arv->region.fade_in().back()->when + arv->region.position());
1717 Editor::fade_in_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1719 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1721 jack_nframes_t fade_length;
1723 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1724 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1730 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1734 if (pos < (arv->region.position() + 64)) {
1735 fade_length = 64; // this should be a minimum defined somewhere
1736 } else if (pos > arv->region.last_frame()) {
1737 fade_length = arv->region.length();
1739 fade_length = pos - arv->region.position();
1742 arv->reset_fade_in_shape_width (fade_length);
1744 show_verbose_duration_cursor (arv->region.position(), arv->region.position() + fade_length, 10);
1746 drag_info.first_move = false;
1750 Editor::fade_in_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1752 if (drag_info.first_move) return;
1754 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1756 jack_nframes_t fade_length;
1758 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1759 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1765 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1769 if (pos < (arv->region.position() + 64)) {
1770 fade_length = 64; // this should be a minimum defined somewhere
1772 else if (pos > arv->region.last_frame()) {
1773 fade_length = arv->region.length();
1776 fade_length = pos - arv->region.position();
1779 begin_reversible_command (_("change fade in length"));
1780 session->add_undo (arv->region.get_memento());
1781 arv->region.set_fade_in_length (fade_length);
1782 session->add_redo_no_execute (arv->region.get_memento());
1783 commit_reversible_command ();
1784 fade_in_drag_motion_callback (item, event);
1788 Editor::start_fade_out_grab (ArdourCanvas::Item* item, GdkEvent* event)
1790 drag_info.item = item;
1791 drag_info.motion_callback = &Editor::fade_out_drag_motion_callback;
1792 drag_info.finished_callback = &Editor::fade_out_drag_finished_callback;
1796 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
1797 fatal << _("programming error: fade out canvas item has no regionview data pointer!") << endmsg;
1801 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1803 drag_info.pointer_frame_offset = drag_info.grab_frame - (arv->region.length() - (jack_nframes_t) arv->region.fade_out().back()->when + arv->region.position());
1807 Editor::fade_out_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1809 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1811 jack_nframes_t fade_length;
1813 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1814 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1820 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1824 if (pos > (arv->region.last_frame() - 64)) {
1825 fade_length = 64; // this should really be a minimum fade defined somewhere
1827 else if (pos < arv->region.position()) {
1828 fade_length = arv->region.length();
1831 fade_length = arv->region.last_frame() - pos;
1834 arv->reset_fade_out_shape_width (fade_length);
1836 show_verbose_duration_cursor (arv->region.last_frame() - fade_length, arv->region.last_frame(), 10);
1838 drag_info.first_move = false;
1842 Editor::fade_out_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1844 if (drag_info.first_move) return;
1846 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1848 jack_nframes_t fade_length;
1850 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1851 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1857 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1861 if (pos > (arv->region.last_frame() - 64)) {
1862 fade_length = 64; // this should really be a minimum fade defined somewhere
1864 else if (pos < arv->region.position()) {
1865 fade_length = arv->region.length();
1868 fade_length = arv->region.last_frame() - pos;
1871 begin_reversible_command (_("change fade out length"));
1872 session->add_undo (arv->region.get_memento());
1873 arv->region.set_fade_out_length (fade_length);
1874 session->add_redo_no_execute (arv->region.get_memento());
1875 commit_reversible_command ();
1877 fade_out_drag_motion_callback (item, event);
1881 Editor::start_cursor_grab (ArdourCanvas::Item* item, GdkEvent* event)
1883 drag_info.item = item;
1884 drag_info.motion_callback = &Editor::cursor_drag_motion_callback;
1885 drag_info.finished_callback = &Editor::cursor_drag_finished_callback;
1889 if ((drag_info.data = (item->get_data ("cursor"))) == 0) {
1890 fatal << _("programming error: cursor canvas item has no cursor data pointer!") << endmsg;
1894 Cursor* cursor = (Cursor *) drag_info.data;
1896 if (session && cursor == playhead_cursor) {
1897 if (drag_info.was_rolling) {
1898 session->request_stop ();
1902 drag_info.pointer_frame_offset = drag_info.grab_frame - cursor->current_frame;
1904 show_verbose_time_cursor (cursor->current_frame, 10);
1908 Editor::cursor_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1910 Cursor* cursor = (Cursor *) drag_info.data;
1911 jack_nframes_t adjusted_frame;
1913 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1914 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1920 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1921 if (cursor != edit_cursor || snap_type != SnapToEditCursor) {
1922 snap_to (adjusted_frame);
1926 if (adjusted_frame == drag_info.last_pointer_frame) return;
1928 cursor->set_position (adjusted_frame);
1930 if (cursor == edit_cursor) {
1931 edit_cursor_clock.set (cursor->current_frame);
1934 show_verbose_time_cursor (cursor->current_frame, 10);
1936 drag_info.last_pointer_frame = adjusted_frame;
1937 drag_info.first_move = false;
1941 Editor::cursor_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1943 if (drag_info.first_move) return;
1945 cursor_drag_motion_callback (item, event);
1947 if (item == &playhead_cursor->canvas_item) {
1949 session->request_locate (playhead_cursor->current_frame, drag_info.was_rolling);
1951 } else if (item == &edit_cursor->canvas_item) {
1952 edit_cursor->set_position (edit_cursor->current_frame);
1953 edit_cursor_clock.set (edit_cursor->current_frame);
1958 Editor::update_marker_drag_item (Location *location)
1960 double x1 = frame_to_pixel (location->start());
1961 double x2 = frame_to_pixel (location->end());
1963 if (location->is_mark()) {
1964 marker_drag_line_points.front().set_x(x1);
1965 marker_drag_line_points.back().set_x(x1);
1966 marker_drag_line->property_points() = marker_drag_line_points;
1969 range_marker_drag_rect->property_x1() = x1;
1970 range_marker_drag_rect->property_x2() = x2;
1975 Editor::start_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
1979 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1980 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
1986 Location *location = find_location_from_marker (marker, is_start);
1988 drag_info.item = item;
1989 drag_info.data = marker;
1990 drag_info.motion_callback = &Editor::marker_drag_motion_callback;
1991 drag_info.finished_callback = &Editor::marker_drag_finished_callback;
1995 drag_info.copied_location = new Location (*location);
1996 drag_info.pointer_frame_offset = drag_info.grab_frame - (is_start ? location->start() : location->end());
1998 update_marker_drag_item (location);
2000 if (location->is_mark()) {
2001 marker_drag_line->show();
2002 marker_drag_line->raise_to_top();
2005 range_marker_drag_rect->show();
2006 range_marker_drag_rect->raise_to_top();
2009 if (is_start) show_verbose_time_cursor (location->start(), 10);
2010 else show_verbose_time_cursor (location->end(), 10);
2014 Editor::marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2016 jack_nframes_t f_delta;
2017 Marker* marker = (Marker *) drag_info.data;
2018 Location *real_location;
2019 Location *copy_location;
2021 bool move_both = false;
2023 jack_nframes_t newframe;
2024 if (drag_info.pointer_frame_offset <= (long) drag_info.current_pointer_frame) {
2025 newframe = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2031 jack_nframes_t next = newframe;
2033 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2034 snap_to (newframe, 0, true);
2037 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
2039 /* call this to find out if its the start or end */
2041 real_location = find_location_from_marker (marker, is_start);
2043 /* use the copy that we're "dragging" around */
2045 copy_location = drag_info.copied_location;
2047 f_delta = copy_location->end() - copy_location->start();
2049 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
2053 if (is_start) { // start marker
2056 copy_location->set_start (newframe);
2057 copy_location->set_end (newframe + f_delta);
2058 } else if (newframe < copy_location->end()) {
2059 copy_location->set_start (newframe);
2061 snap_to (next, 1, true);
2062 copy_location->set_end (next);
2063 copy_location->set_start (newframe);
2066 } else { // end marker
2069 copy_location->set_end (newframe);
2070 copy_location->set_start (newframe - f_delta);
2071 } else if (newframe > copy_location->start()) {
2072 copy_location->set_end (newframe);
2074 } else if (newframe > 0) {
2075 snap_to (next, -1, true);
2076 copy_location->set_start (next);
2077 copy_location->set_end (newframe);
2081 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
2082 drag_info.first_move = false;
2084 update_marker_drag_item (copy_location);
2086 LocationMarkers* lm = find_location_markers (real_location);
2087 lm->set_position (copy_location->start(), copy_location->end());
2089 show_verbose_time_cursor (newframe, 10);
2093 Editor::marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2095 if (drag_info.first_move) {
2096 marker_drag_motion_callback (item, event);
2100 Marker* marker = (Marker *) drag_info.data;
2105 begin_reversible_command ( _("move marker") );
2106 session->add_undo( session->locations()->get_memento() );
2108 Location * location = find_location_from_marker (marker, is_start);
2111 location->set (drag_info.copied_location->start(), drag_info.copied_location->end());
2114 session->add_redo_no_execute( session->locations()->get_memento() );
2115 commit_reversible_command ();
2117 marker_drag_line->hide();
2118 range_marker_drag_rect->hide();
2122 Editor::start_meter_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2125 MeterMarker* meter_marker;
2127 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2128 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2132 meter_marker = dynamic_cast<MeterMarker*> (marker);
2134 MetricSection& section (meter_marker->meter());
2136 if (!section.movable()) {
2140 drag_info.item = item;
2141 drag_info.data = marker;
2142 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2143 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2147 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2149 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2153 Editor::start_meter_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2156 MeterMarker* meter_marker;
2158 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2159 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2163 meter_marker = dynamic_cast<MeterMarker*> (marker);
2165 // create a dummy marker for visual representation of moving the copy.
2166 // The actual copying is not done before we reach the finish callback.
2168 snprintf (name, sizeof(name), "%g/%g", meter_marker->meter().beats_per_bar(), meter_marker->meter().note_divisor ());
2169 MeterMarker* new_marker = new MeterMarker(*this, *meter_group, color_map[cMeterMarker], name,
2170 *new MeterSection(meter_marker->meter()));
2172 drag_info.item = &new_marker->the_item();
2173 drag_info.copy = true;
2174 drag_info.data = new_marker;
2175 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2176 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2180 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2182 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2186 Editor::meter_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2188 MeterMarker* marker = (MeterMarker *) drag_info.data;
2189 jack_nframes_t adjusted_frame;
2191 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2192 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2198 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2199 snap_to (adjusted_frame);
2202 if (adjusted_frame == drag_info.last_pointer_frame) return;
2204 marker->set_position (adjusted_frame);
2207 drag_info.last_pointer_frame = adjusted_frame;
2208 drag_info.first_move = false;
2210 show_verbose_time_cursor (adjusted_frame, 10);
2214 Editor::meter_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2216 if (drag_info.first_move) return;
2218 meter_marker_drag_motion_callback (drag_info.item, event);
2220 MeterMarker* marker = (MeterMarker *) drag_info.data;
2223 TempoMap& map (session->tempo_map());
2224 map.bbt_time (drag_info.last_pointer_frame, when);
2226 if (drag_info.copy == true) {
2227 begin_reversible_command (_("copy meter mark"));
2228 session->add_undo (map.get_memento());
2229 map.add_meter (marker->meter(), when);
2230 session->add_redo_no_execute (map.get_memento());
2231 commit_reversible_command ();
2233 // delete the dummy marker we used for visual representation of copying.
2234 // a new visual marker will show up automatically.
2237 begin_reversible_command (_("move meter mark"));
2238 session->add_undo (map.get_memento());
2239 map.move_meter (marker->meter(), when);
2240 session->add_redo_no_execute (map.get_memento());
2241 commit_reversible_command ();
2246 Editor::start_tempo_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2249 TempoMarker* tempo_marker;
2251 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2252 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2256 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2257 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2261 MetricSection& section (tempo_marker->tempo());
2263 if (!section.movable()) {
2267 drag_info.item = item;
2268 drag_info.data = marker;
2269 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2270 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2274 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2275 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2279 Editor::start_tempo_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2282 TempoMarker* tempo_marker;
2284 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2285 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2289 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2290 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2294 // create a dummy marker for visual representation of moving the copy.
2295 // The actual copying is not done before we reach the finish callback.
2297 snprintf (name, sizeof (name), "%.2f", tempo_marker->tempo().beats_per_minute());
2298 TempoMarker* new_marker = new TempoMarker(*this, *tempo_group, color_map[cTempoMarker], name,
2299 *new TempoSection(tempo_marker->tempo()));
2301 drag_info.item = &new_marker->the_item();
2302 drag_info.copy = true;
2303 drag_info.data = new_marker;
2304 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2305 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2309 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2311 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2315 Editor::tempo_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2317 TempoMarker* marker = (TempoMarker *) drag_info.data;
2318 jack_nframes_t adjusted_frame;
2320 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2321 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2327 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2328 snap_to (adjusted_frame);
2331 if (adjusted_frame == drag_info.last_pointer_frame) return;
2333 /* OK, we've moved far enough to make it worth actually move the thing. */
2335 marker->set_position (adjusted_frame);
2337 show_verbose_time_cursor (adjusted_frame, 10);
2339 drag_info.last_pointer_frame = adjusted_frame;
2340 drag_info.first_move = false;
2344 Editor::tempo_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2346 if (drag_info.first_move) return;
2348 tempo_marker_drag_motion_callback (drag_info.item, event);
2350 TempoMarker* marker = (TempoMarker *) drag_info.data;
2353 TempoMap& map (session->tempo_map());
2354 map.bbt_time (drag_info.last_pointer_frame, when);
2356 if (drag_info.copy == true) {
2357 begin_reversible_command (_("copy tempo mark"));
2358 session->add_undo (map.get_memento());
2359 map.add_tempo (marker->tempo(), when);
2360 session->add_redo_no_execute (map.get_memento());
2361 commit_reversible_command ();
2363 // delete the dummy marker we used for visual representation of copying.
2364 // a new visual marker will show up automatically.
2367 begin_reversible_command (_("move tempo mark"));
2368 session->add_undo (map.get_memento());
2369 map.move_tempo (marker->tempo(), when);
2370 session->add_redo_no_execute (map.get_memento());
2371 commit_reversible_command ();
2376 Editor::remove_gain_control_point (ArdourCanvas::Item*item, GdkEvent* event)
2378 ControlPoint* control_point;
2380 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2381 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2385 // We shouldn't remove the first or last gain point
2386 if (control_point->line.is_last_point(*control_point) ||
2387 control_point->line.is_first_point(*control_point)) {
2391 control_point->line.remove_point (*control_point);
2395 Editor::remove_control_point (ArdourCanvas::Item*item, GdkEvent* event)
2397 ControlPoint* control_point;
2399 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2400 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2404 control_point->line.remove_point (*control_point);
2408 Editor::start_control_point_grab (ArdourCanvas::Item* item, GdkEvent* event)
2410 ControlPoint* control_point;
2412 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2413 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2417 drag_info.item = item;
2418 drag_info.data = control_point;
2419 drag_info.motion_callback = &Editor::control_point_drag_motion_callback;
2420 drag_info.finished_callback = &Editor::control_point_drag_finished_callback;
2422 start_grab (event, fader_cursor);
2424 control_point->line.start_drag (control_point, 0);
2426 float fraction = 1.0 - (control_point->get_y() / control_point->line.height());
2427 set_verbose_canvas_cursor (control_point->line.get_verbose_cursor_string (fraction),
2428 drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
2430 show_verbose_canvas_cursor ();
2434 Editor::control_point_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2436 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2438 double cx = drag_info.current_pointer_x;
2439 double cy = drag_info.current_pointer_y;
2441 drag_info.cumulative_x_drag = cx - drag_info.grab_x ;
2442 drag_info.cumulative_y_drag = cy - drag_info.grab_y ;
2444 if (drag_info.x_constrained) {
2445 cx = drag_info.grab_x;
2447 if (drag_info.y_constrained) {
2448 cy = drag_info.grab_y;
2451 cp->line.parent_group().w2i (cx, cy);
2455 cy = min ((double) cp->line.height(), cy);
2457 //translate cx to frames
2458 jack_nframes_t cx_frames = unit_to_frame (cx);
2460 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier()) && !drag_info.x_constrained) {
2461 snap_to (cx_frames);
2464 float fraction = 1.0 - (cy / cp->line.height());
2468 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
2474 cp->line.point_drag (*cp, cx_frames , fraction, push);
2476 set_verbose_canvas_cursor_text (cp->line.get_verbose_cursor_string (fraction));
2478 drag_info.first_move = false;
2482 Editor::control_point_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2484 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2486 if (drag_info.first_move) {
2490 if ((event->type == GDK_BUTTON_RELEASE) && (event->button.button == 1) && Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
2491 reset_point_selection ();
2495 control_point_drag_motion_callback (item, event);
2497 cp->line.end_drag (cp);
2501 Editor::start_line_grab_from_regionview (ArdourCanvas::Item* item, GdkEvent* event)
2503 switch (mouse_mode) {
2505 start_line_grab (clicked_regionview->get_gain_line(), event);
2513 Editor::start_line_grab_from_line (ArdourCanvas::Item* item, GdkEvent* event)
2517 if ((al = reinterpret_cast<AutomationLine*> (item->get_data ("line"))) == 0) {
2518 fatal << _("programming error: line canvas item has no line pointer!") << endmsg;
2522 start_line_grab (al, event);
2526 Editor::start_line_grab (AutomationLine* line, GdkEvent* event)
2530 jack_nframes_t frame_within_region;
2532 /* need to get x coordinate in terms of parent (TimeAxisItemView)
2536 cx = event->button.x;
2537 cy = event->button.y;
2538 line->parent_group().w2i (cx, cy);
2539 frame_within_region = (jack_nframes_t) floor (cx * frames_per_unit);
2541 if (!line->control_points_adjacent (frame_within_region, current_line_drag_info.before,
2542 current_line_drag_info.after)) {
2543 /* no adjacent points */
2547 drag_info.item = &line->grab_item();
2548 drag_info.data = line;
2549 drag_info.motion_callback = &Editor::line_drag_motion_callback;
2550 drag_info.finished_callback = &Editor::line_drag_finished_callback;
2552 start_grab (event, fader_cursor);
2554 double fraction = 1.0 - (cy / line->height());
2556 line->start_drag (0, fraction);
2558 set_verbose_canvas_cursor (line->get_verbose_cursor_string (fraction),
2559 drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
2560 show_verbose_canvas_cursor ();
2564 Editor::line_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2566 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
2567 double cx = drag_info.current_pointer_x;
2568 double cy = drag_info.current_pointer_y;
2570 line->parent_group().w2i (cx, cy);
2573 fraction = 1.0 - (cy / line->height());
2577 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
2583 line->line_drag (current_line_drag_info.before, current_line_drag_info.after, fraction, push);
2585 set_verbose_canvas_cursor_text (line->get_verbose_cursor_string (fraction));
2589 Editor::line_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2591 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
2592 line_drag_motion_callback (item, event);
2597 Editor::start_region_grab (ArdourCanvas::Item* item, GdkEvent* event)
2599 if (selection->audio_regions.empty() || clicked_regionview == 0) {
2603 drag_info.copy = false;
2604 drag_info.item = item;
2605 drag_info.data = clicked_regionview;
2606 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2607 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2612 TimeAxisView* tvp = clicked_trackview;
2613 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
2615 if (tv && tv->is_audio_track()) {
2616 speed = tv->get_diskstream()->speed();
2619 drag_info.last_frame_position = (jack_nframes_t) (clicked_regionview->region.position() / speed);
2620 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2621 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2622 // we want a move threshold
2623 drag_info.want_move_threshold = true;
2625 show_verbose_time_cursor (drag_info.last_frame_position, 10);
2627 begin_reversible_command (_("move region(s)"));
2631 Editor::start_region_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2633 if (selection->audio_regions.empty() || clicked_regionview == 0) {
2637 /* this is committed in the grab finished callback. */
2639 begin_reversible_command (_("Drag region copy"));
2641 /* duplicate the region(s) */
2643 vector<AudioRegionView*> new_regionviews;
2645 set<Playlist*> affected_playlists;
2646 pair<set<Playlist*>::iterator,bool> insert_result;
2648 for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin(); i != selection->audio_regions.by_layer().end(); ++i) {
2649 AudioRegionView* rv;
2653 Playlist* to_playlist = rv->region.playlist();
2654 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*>(&rv->get_time_axis_view());
2656 insert_result = affected_playlists.insert (to_playlist);
2657 if (insert_result.second) {
2658 session->add_undo (to_playlist->get_memento ());
2661 latest_regionview = 0;
2663 sigc::connection c = atv->view->AudioRegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
2665 /* create a new region with the same name.
2668 AudioRegion* newregion = new AudioRegion (rv->region);
2670 /* if the original region was locked, we don't care */
2672 newregion->set_locked (false);
2674 to_playlist->add_region (*newregion, (jack_nframes_t) (rv->region.position() * atv->get_diskstream()->speed()));
2678 if (latest_regionview) {
2679 new_regionviews.push_back (latest_regionview);
2683 if (new_regionviews.empty()) {
2687 /* reset selection to new regionviews */
2689 selection->set (new_regionviews);
2691 /* reset drag_info data to reflect the fact that we are dragging the copies */
2693 drag_info.data = new_regionviews.front();
2694 drag_info.item = new_regionviews.front()->get_canvas_group ();
2696 drag_info.copy = true;
2697 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2698 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2702 TimeAxisView* tv = &clicked_regionview->get_time_axis_view();
2703 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*>(tv);
2706 if (atv && atv->is_audio_track()) {
2707 speed = atv->get_diskstream()->speed();
2710 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2711 drag_info.last_frame_position = (jack_nframes_t) (clicked_regionview->region.position() / speed);
2712 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2713 // we want a move threshold
2714 drag_info.want_move_threshold = true;
2716 show_verbose_time_cursor (drag_info.last_frame_position, 10);
2718 //begin_reversible_command (_("copy region(s)"));
2722 Editor::start_region_brush_grab (ArdourCanvas::Item* item, GdkEvent* event)
2724 if (selection->audio_regions.empty() || clicked_regionview == 0) {
2728 drag_info.copy = false;
2729 drag_info.item = item;
2730 drag_info.data = clicked_regionview;
2731 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2732 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2737 TimeAxisView* tvp = clicked_trackview;
2738 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
2740 if (tv && tv->is_audio_track()) {
2741 speed = tv->get_diskstream()->speed();
2744 drag_info.last_frame_position = (jack_nframes_t) (clicked_regionview->region.position() / speed);
2745 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2746 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2747 // we want a move threshold
2748 drag_info.want_move_threshold = true;
2749 drag_info.brushing = true;
2751 begin_reversible_command (_("Drag region brush"));
2755 Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2759 AudioRegionView *rv = reinterpret_cast<AudioRegionView*> (drag_info.data);
2760 jack_nframes_t pending_region_position = 0;
2761 int32_t pointer_y_span = 0, canvas_pointer_y_span = 0, original_pointer_order;
2762 int32_t visible_y_high = 0, visible_y_low = 512; //high meaning higher numbered.. not the height on the screen
2763 bool clamp_y_axis = false;
2764 vector<int32_t> height_list(512) ;
2765 vector<int32_t>::iterator j;
2767 /* Which trackview is this ? */
2769 TimeAxisView* tvp = trackview_by_y_position (drag_info.current_pointer_y);
2770 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
2772 /* The region motion is only processed if the pointer is over
2776 if (!tv || !tv->is_audio_track()) {
2777 /* To make sure we hide the verbose canvas cursor when the mouse is
2778 not held over and audiotrack.
2780 hide_verbose_canvas_cursor ();
2784 original_pointer_order = drag_info.last_trackview->order;
2786 /************************************************************
2788 ************************************************************/
2790 if (drag_info.brushing) {
2791 clamp_y_axis = true;
2796 if ((pointer_y_span = (drag_info.last_trackview->order - tv->order)) != 0) {
2798 int32_t children = 0, numtracks = 0;
2799 // XXX hard coding track limit, oh my, so very very bad
2800 bitset <1024> tracks (0x00);
2801 /* get a bitmask representing the visible tracks */
2803 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2804 TimeAxisView *tracklist_timeview;
2805 tracklist_timeview = (*i);
2806 AudioTimeAxisView* atv2 = dynamic_cast<AudioTimeAxisView*>(tracklist_timeview);
2807 list<TimeAxisView*> children_list;
2809 /* zeroes are audio tracks. ones are other types. */
2811 if (!atv2->hidden()) {
2813 if (visible_y_high < atv2->order) {
2814 visible_y_high = atv2->order;
2816 if (visible_y_low > atv2->order) {
2817 visible_y_low = atv2->order;
2820 if (!atv2->is_audio_track()) {
2821 tracks = tracks |= (0x01 << atv2->order);
2824 height_list[atv2->order] = (*i)->height;
2826 if ((children_list = atv2->get_child_list()).size() > 0) {
2827 for (list<TimeAxisView*>::iterator j = children_list.begin(); j != children_list.end(); ++j) {
2828 tracks = tracks |= (0x01 << (atv2->order + children));
2829 height_list[atv2->order + children] = (*j)->height;
2837 /* find the actual span according to the canvas */
2839 canvas_pointer_y_span = pointer_y_span;
2840 if (drag_info.last_trackview->order >= tv->order) {
2842 for (y = tv->order; y < drag_info.last_trackview->order; y++) {
2843 if (height_list[y] == 0 ) {
2844 canvas_pointer_y_span--;
2849 for (y = drag_info.last_trackview->order;y <= tv->order; y++) {
2850 if ( height_list[y] == 0 ) {
2851 canvas_pointer_y_span++;
2856 for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin(); i != selection->audio_regions.by_layer().end(); ++i) {
2857 AudioRegionView* rv2;
2859 double ix1, ix2, iy1, iy2;
2862 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
2863 rv2->get_canvas_group()->i2w (ix1, iy1);
2864 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
2865 AudioTimeAxisView* atv2 = dynamic_cast<AudioTimeAxisView*>(tvp2);
2867 if (atv2->order != original_pointer_order) {
2868 /* this isn't the pointer track */
2870 if (canvas_pointer_y_span > 0) {
2872 /* moving up the canvas */
2873 if ((atv2->order - canvas_pointer_y_span) >= visible_y_low) {
2875 int32_t visible_tracks = 0;
2876 while (visible_tracks < canvas_pointer_y_span ) {
2879 while (height_list[atv2->order - (visible_tracks - n)] == 0) {
2880 /* we're passing through a hidden track */
2885 if (tracks[atv2->order - (canvas_pointer_y_span - n)] != 0x00) {
2886 clamp_y_axis = true;
2890 clamp_y_axis = true;
2893 } else if (canvas_pointer_y_span < 0) {
2895 /*moving down the canvas*/
2897 if ((atv2->order - (canvas_pointer_y_span - n)) <= visible_y_high) { // we will overflow
2900 int32_t visible_tracks = 0;
2902 while (visible_tracks > canvas_pointer_y_span ) {
2905 while (height_list[atv2->order - (visible_tracks - n)] == 0) {
2909 if ( tracks[atv2->order - ( canvas_pointer_y_span - n)] != 0x00) {
2910 clamp_y_axis = true;
2915 clamp_y_axis = true;
2921 /* this is the pointer's track */
2922 if ((atv2->order - pointer_y_span) > visible_y_high) { // we will overflow
2923 clamp_y_axis = true;
2924 } else if ((atv2->order - pointer_y_span) < visible_y_low) { // we will underflow
2925 clamp_y_axis = true;
2933 } else if (drag_info.last_trackview == tv) {
2934 clamp_y_axis = true;
2938 if (!clamp_y_axis) {
2939 drag_info.last_trackview = tv;
2942 /************************************************************
2944 ************************************************************/
2946 /* compute the amount of pointer motion in frames, and where
2947 the region would be if we moved it by that much.
2950 if (drag_info.move_threshold_passsed) {
2952 if ((int32_t)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2954 jack_nframes_t sync_frame;
2955 jack_nframes_t sync_offset;
2958 pending_region_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2960 sync_offset = rv->region.sync_offset (sync_dir);
2961 sync_frame = rv->region.adjust_to_sync (pending_region_position);
2963 /* we snap if the snap modifier is not enabled.
2966 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2967 snap_to (sync_frame);
2970 if (sync_frame - sync_offset <= sync_frame) {
2971 pending_region_position = sync_frame - (sync_dir*sync_offset);
2973 pending_region_position = 0;
2977 pending_region_position = 0;
2980 if (pending_region_position > max_frames - rv->region.length()) {
2981 pending_region_position = drag_info.last_frame_position;
2984 // printf ("3: pending_region_position= %lu %lu\n", pending_region_position, drag_info.last_frame_position );
2986 if (pending_region_position != drag_info.last_frame_position && !drag_info.x_constrained) {
2988 /* now compute the canvas unit distance we need to move the regiondrag_info.last_trackview->order
2989 to make it appear at the new location.
2992 if (pending_region_position > drag_info.last_frame_position) {
2993 x_delta = ((double) (pending_region_position - drag_info.last_frame_position) / frames_per_unit);
2995 x_delta = -((double) (drag_info.last_frame_position - pending_region_position) / frames_per_unit);
2998 drag_info.last_frame_position = pending_region_position;
3005 /* threshold not passed */
3010 /*************************************************************
3012 ************************************************************/
3014 if (x_delta == 0 && (pointer_y_span == 0)) {
3015 /* haven't reached next snap point, and we're not switching
3016 trackviews. nothing to do.
3022 for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin(); i != selection->audio_regions.by_layer().end(); ++i) {
3024 AudioRegionView* rv2;
3027 /* if any regionview is at zero, we need to know so we can
3028 stop further leftward motion.
3031 double ix1, ix2, iy1, iy2;
3032 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3033 rv2->get_canvas_group()->i2w (ix1, iy1);
3042 /*************************************************************
3044 ************************************************************/
3046 pair<set<Playlist*>::iterator,bool> insert_result;
3047 const list<AudioRegionView*>& layered_regions = selection->audio_regions.by_layer();
3049 for (list<AudioRegionView*>::const_iterator i = layered_regions.begin(); i != layered_regions.end(); ++i) {
3051 AudioRegionView* rv;
3053 double ix1, ix2, iy1, iy2;
3054 int32_t temp_pointer_y_span = pointer_y_span;
3056 /* get item BBox, which will be relative to parent. so we have
3057 to query on a child, then convert to world coordinates using
3061 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3062 rv->get_canvas_group()->i2w (ix1, iy1);
3063 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3064 AudioTimeAxisView* canvas_atv = dynamic_cast<AudioTimeAxisView*>(tvp2);
3065 AudioTimeAxisView* temp_atv;
3067 if ((pointer_y_span != 0) && !clamp_y_axis) {
3070 for (j = height_list.begin(); j!= height_list.end(); j++) {
3071 if (x == canvas_atv->order) {
3072 /* we found the track the region is on */
3073 if (x != original_pointer_order) {
3074 /*this isn't from the same track we're dragging from */
3075 temp_pointer_y_span = canvas_pointer_y_span;
3077 while (temp_pointer_y_span > 0) {
3078 /* we're moving up canvas-wise,
3079 so we need to find the next track height
3081 if (j != height_list.begin()) {
3084 if (x != original_pointer_order) {
3085 /* we're not from the dragged track, so ignore hidden tracks. */
3087 temp_pointer_y_span++;
3091 temp_pointer_y_span--;
3093 while (temp_pointer_y_span < 0) {
3095 if (x != original_pointer_order) {
3097 temp_pointer_y_span--;
3101 if (j != height_list.end()) {
3104 temp_pointer_y_span++;
3106 /* find out where we'll be when we move and set height accordingly */
3108 tvp2 = trackview_by_y_position (iy1 + y_delta);
3109 temp_atv = dynamic_cast<AudioTimeAxisView*>(tvp2);
3110 rv->set_height (temp_atv->height);
3112 /* if you un-comment the following, the region colours will follow the track colours whilst dragging,
3113 personally, i think this can confuse things, but never mind.
3116 //const GdkColor& col (temp_atv->view->get_region_color());
3117 //rv->set_color (const_cast<GdkColor&>(col));
3124 /* prevent the regionview from being moved to before
3125 the zero position on the canvas.
3130 if (-x_delta > ix1) {
3133 } else if ((x_delta > 0) &&(rv->region.last_frame() > max_frames - x_delta)) {
3134 x_delta = max_frames - rv->region.last_frame();
3137 if (drag_info.first_move) {
3139 /* hide any dependent views */
3141 // rv->get_time_axis_view().hide_dependent_views (*rv);
3143 /* this is subtle. raising the regionview itself won't help,
3144 because raise_to_top() just puts the item on the top of
3145 its parent's stack. so, we need to put the trackview canvas_display group
3146 on the top, since its parent is the whole canvas.
3149 rv->get_canvas_group()->raise_to_top();
3150 rv->get_time_axis_view().canvas_display->raise_to_top();
3151 cursor_group->raise_to_top();
3153 /* freeze the playlists from notifying till
3157 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*> (&rv->get_time_axis_view());
3158 if (atv && atv->is_audio_track()) {
3159 AudioPlaylist* pl = atv->get_diskstream()->playlist();
3161 /* only freeze and capture state once */
3163 insert_result = motion_frozen_playlists.insert (pl);
3164 if (insert_result.second) {
3166 session->add_undo(pl->get_memento());
3172 if (drag_info.brushing) {
3173 mouse_brush_insert_region (rv, pending_region_position);
3175 rv->move (x_delta, y_delta);
3179 if (drag_info.first_move) {
3180 cursor_group->raise_to_top();
3183 drag_info.first_move = false;
3185 if (x_delta != 0 && !drag_info.brushing) {
3186 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3192 Editor::region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3194 jack_nframes_t where;
3195 AudioRegionView* rv = reinterpret_cast<AudioRegionView *> (drag_info.data);
3196 pair<set<Playlist*>::iterator,bool> insert_result;
3197 bool nocommit = true;
3199 AudioTimeAxisView* atv;
3200 bool regionview_y_movement;
3201 bool regionview_x_movement;
3203 /* first_move is set to false if the regionview has been moved in the
3207 if (drag_info.first_move) {
3214 /* The regionview has been moved at some stage during the grab so we need
3215 to account for any mouse movement between this event and the last one.
3218 region_drag_motion_callback (item, event);
3220 if (drag_info.brushing) {
3221 /* all changes were made during motion event handlers */
3225 /* adjust for track speed */
3228 atv = dynamic_cast<AudioTimeAxisView*> (drag_info.last_trackview);
3229 if (atv && atv->get_diskstream()) {
3230 speed = atv->get_diskstream()->speed();
3233 regionview_x_movement = (drag_info.last_frame_position != (jack_nframes_t) (rv->region.position()/speed));
3234 regionview_y_movement = (drag_info.last_trackview != &rv->get_time_axis_view());
3236 //printf ("last_frame: %s position is %lu %g\n", rv->get_time_axis_view().name().c_str(), drag_info.last_frame_position, speed);
3237 //printf ("last_rackview: %s \n", drag_info.last_trackview->name().c_str());
3239 if (regionview_y_movement) {
3241 /* motion between tracks */
3243 list<AudioRegionView*> new_selection;
3245 /* moved to a different audio track. */
3247 for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin(); i != selection->audio_regions.by_layer().end(); ) {
3249 AudioRegionView* rv2 = (*i);
3251 /* the region that used to be in the old playlist is not
3252 moved to the new one - we make a copy of it. as a result,
3253 any existing editor for the region should no longer be
3257 if (!drag_info.copy) {
3258 rv2->hide_region_editor();
3260 new_selection.push_back (rv2);
3264 /* first, freeze the target tracks */
3266 for (list<AudioRegionView*>::const_iterator i = new_selection.begin(); i != new_selection.end();i++ ) {
3268 Playlist* from_playlist;
3269 Playlist* to_playlist;
3271 double ix1, ix2, iy1, iy2;
3273 (*i)->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3274 (*i)->get_canvas_group()->i2w (ix1, iy1);
3275 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3276 AudioTimeAxisView* atv2 = dynamic_cast<AudioTimeAxisView*>(tvp2);
3278 from_playlist = (*i)->region.playlist();
3279 to_playlist = atv2->playlist();
3281 /* the from_playlist was frozen in the "first_move" case
3282 of the motion handler. the insert can fail,
3283 but that doesn't matter. it just means
3284 we already have the playlist in the list.
3287 motion_frozen_playlists.insert (from_playlist);
3289 /* only freeze the to_playlist once */
3291 insert_result = motion_frozen_playlists.insert(to_playlist);
3292 if (insert_result.second) {
3293 to_playlist->freeze();
3294 session->add_undo(to_playlist->get_memento());
3299 /* now do it again with the actual operations */
3301 for (list<AudioRegionView*>::const_iterator i = new_selection.begin(); i != new_selection.end();i++ ) {
3303 Playlist* from_playlist;
3304 Playlist* to_playlist;
3306 double ix1, ix2, iy1, iy2;
3308 (*i)->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3309 (*i)->get_canvas_group()->i2w (ix1, iy1);
3310 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3311 AudioTimeAxisView* atv2 = dynamic_cast<AudioTimeAxisView*>(tvp2);
3313 from_playlist = (*i)->region.playlist();
3314 to_playlist = atv2->playlist();
3316 latest_regionview = 0;
3318 where = (jack_nframes_t) (unit_to_frame (ix1) * speed);
3319 Region* new_region = createRegion ((*i)->region);
3321 from_playlist->remove_region (&((*i)->region));
3323 sigc::connection c = atv2->view->AudioRegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3324 to_playlist->add_region (*new_region, where);
3327 if (latest_regionview) {
3328 selection->add (latest_regionview);
3334 /* motion within a single track */
3336 for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin(); i != selection->audio_regions.by_layer().end(); ++i) {
3340 if (rv->region.locked()) {
3344 if (regionview_x_movement) {
3345 double ownspeed = 1.0;
3346 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*> (&(rv->get_time_axis_view()));
3348 if (atv && atv->get_diskstream()) {
3349 ownspeed = atv->get_diskstream()->speed();
3352 /* base the new region position on the current position of the regionview.*/
3354 double ix1, ix2, iy1, iy2;
3356 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3357 rv->get_canvas_group()->i2w (ix1, iy1);
3358 where = (jack_nframes_t) (unit_to_frame (ix1) * ownspeed);
3362 where = rv->region.position();
3365 rv->get_time_axis_view().reveal_dependent_views (*rv);
3367 /* no need to add an undo here, we did that when we added this playlist to motion_frozen playlists */
3369 rv->region.set_position (where, (void *) this);
3374 for (set<Playlist*>::iterator p = motion_frozen_playlists.begin(); p != motion_frozen_playlists.end(); ++p) {
3376 session->add_redo_no_execute ((*p)->get_memento());
3379 motion_frozen_playlists.clear ();
3382 commit_reversible_command ();
3387 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
3389 /* Either add to or set the set the region selection, unless
3390 this is an alignment click (control used)
3393 if (Keyboard::modifier_state_contains (event->state, Keyboard::Control)) {
3394 TimeAxisView* tv = &rv.get_time_axis_view();
3395 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*>(tv);
3397 if (atv && atv->is_audio_track()) {
3398 speed = atv->get_diskstream()->speed();
3401 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Alt))) {
3403 align_region (rv.region, SyncPoint, (jack_nframes_t) (edit_cursor->current_frame * speed));
3405 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Shift))) {
3407 align_region (rv.region, End, (jack_nframes_t) (edit_cursor->current_frame * speed));
3411 align_region (rv.region, Start, (jack_nframes_t) (edit_cursor->current_frame * speed));
3417 Editor::show_verbose_time_cursor (jack_nframes_t frame, double offset, double xpos, double ypos)
3428 switch (ARDOUR_UI::instance()->secondary_clock.mode ()) {
3429 case AudioClock::BBT:
3430 session->bbt_time (frame, bbt);
3431 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, bbt.bars, bbt.beats, bbt.ticks);
3434 case AudioClock::SMPTE:
3435 session->smpte_time (frame, smpte);
3436 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
3439 case AudioClock::MinSec:
3440 /* XXX fix this to compute min/sec properly */
3441 session->smpte_time (frame, smpte);
3442 secs = smpte.seconds + ((float) smpte.frames / session->smpte_frames_per_second);
3443 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", smpte.hours, smpte.minutes, secs);
3447 snprintf (buf, sizeof(buf), "%u", frame);
3451 if (xpos >= 0 && ypos >=0) {
3452 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
3455 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
3457 show_verbose_canvas_cursor ();
3461 Editor::show_verbose_duration_cursor (jack_nframes_t start, jack_nframes_t end, double offset, double xpos, double ypos)
3468 Meter meter_at_start(session->tempo_map().meter_at(start));
3474 switch (ARDOUR_UI::instance()->secondary_clock.mode ()) {
3475 case AudioClock::BBT:
3476 session->bbt_time (start, sbbt);
3477 session->bbt_time (end, ebbt);
3480 /* XXX this computation won't work well if the
3481 user makes a selection that spans any meter changes.
3484 ebbt.bars -= sbbt.bars;
3485 if (ebbt.beats >= sbbt.beats) {
3486 ebbt.beats -= sbbt.beats;
3489 ebbt.beats = int(meter_at_start.beats_per_bar()) + ebbt.beats - sbbt.beats;
3491 if (ebbt.ticks >= sbbt.ticks) {
3492 ebbt.ticks -= sbbt.ticks;
3495 ebbt.ticks = int(Meter::ticks_per_beat) + ebbt.ticks - sbbt.ticks;
3498 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, ebbt.bars, ebbt.beats, ebbt.ticks);
3501 case AudioClock::SMPTE:
3502 session->smpte_duration (end - start, smpte);
3503 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
3506 case AudioClock::MinSec:
3507 /* XXX fix this to compute min/sec properly */
3508 session->smpte_duration (end - start, smpte);
3509 secs = smpte.seconds + ((float) smpte.frames / session->smpte_frames_per_second);
3510 snprintf (buf, sizeof (buf), "%02ld:%02ld:%.4f", smpte.hours, smpte.minutes, secs);
3514 snprintf (buf, sizeof(buf), "%u", end - start);
3518 if (xpos >= 0 && ypos >=0) {
3519 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
3522 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
3524 show_verbose_canvas_cursor ();
3528 Editor::collect_new_region_view (AudioRegionView* rv)
3530 latest_regionview = rv;
3534 Editor::start_selection_grab (ArdourCanvas::Item* item, GdkEvent* event)
3536 if (clicked_regionview == 0) {
3540 /* lets try to create new Region for the selection */
3542 vector<AudioRegion*> new_regions;
3543 create_region_from_selection (new_regions);
3545 if (new_regions.empty()) {
3549 /* XXX fix me one day to use all new regions */
3551 Region* region = new_regions.front();
3553 /* add it to the current stream/playlist.
3555 tricky: the streamview for the track will add a new regionview. we will
3556 catch the signal it sends when it creates the regionview to
3557 set the regionview we want to then drag.
3560 latest_regionview = 0;
3561 sigc::connection c = clicked_audio_trackview->view->AudioRegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3563 /* A selection grab currently creates two undo/redo operations, one for
3564 creating the new region and another for moving it.
3567 begin_reversible_command (_("selection grab"));
3569 Playlist* playlist = clicked_trackview->playlist();
3571 session->add_undo (playlist->get_memento ());
3572 clicked_trackview->playlist()->add_region (*region, selection->time[clicked_selection].start);
3573 session->add_redo_no_execute (playlist->get_memento ());
3575 commit_reversible_command ();
3579 if (latest_regionview == 0) {
3580 /* something went wrong */
3584 /* we need to deselect all other regionviews, and select this one
3585 i'm ignoring undo stuff, because the region creation will take care of it */
3586 selection->set (latest_regionview);
3588 drag_info.item = latest_regionview->get_canvas_group();
3589 drag_info.data = latest_regionview;
3590 drag_info.motion_callback = &Editor::region_drag_motion_callback;
3591 drag_info.finished_callback = &Editor::region_drag_finished_callback;
3595 drag_info.last_trackview = clicked_trackview;
3596 drag_info.last_frame_position = latest_regionview->region.position();
3597 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
3599 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3603 Editor::cancel_selection ()
3605 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
3606 (*i)->hide_selection ();
3608 begin_reversible_command (_("cancel selection"));
3609 selection->clear ();
3610 clicked_selection = 0;
3611 commit_reversible_command ();
3615 Editor::start_selection_op (ArdourCanvas::Item* item, GdkEvent* event, SelectionOp op)
3617 jack_nframes_t start = 0;
3618 jack_nframes_t end = 0;
3624 drag_info.item = item;
3625 drag_info.motion_callback = &Editor::drag_selection;
3626 drag_info.finished_callback = &Editor::end_selection_op;
3631 case CreateSelection:
3633 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
3634 drag_info.copy = true;
3636 drag_info.copy = false;
3638 start_grab (event, selector_cursor);
3641 case SelectionStartTrim:
3642 clicked_trackview->order_selection_trims (item, true);
3643 start_grab (event, trimmer_cursor);
3644 start = selection->time[clicked_selection].start;
3645 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
3648 case SelectionEndTrim:
3649 clicked_trackview->order_selection_trims (item, false);
3650 start_grab (event, trimmer_cursor);
3651 end = selection->time[clicked_selection].end;
3652 drag_info.pointer_frame_offset = drag_info.grab_frame - end;
3656 start = selection->time[clicked_selection].start;
3658 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
3662 if (selection_op == SelectionMove) {
3663 show_verbose_time_cursor(start, 10);
3665 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
3670 Editor::drag_selection (ArdourCanvas::Item* item, GdkEvent* event)
3672 jack_nframes_t start = 0;
3673 jack_nframes_t end = 0;
3674 jack_nframes_t length;
3675 jack_nframes_t pending_position;
3677 if ((int32_t) drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
3678 pending_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
3681 pending_position = 0;
3684 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3685 snap_to (pending_position);
3688 /* only alter selection if the current frame is
3689 different from the last frame position (adjusted)
3692 if (pending_position == drag_info.last_pointer_frame) return;
3694 switch (selection_op) {
3695 case CreateSelection:
3697 if (drag_info.first_move) {
3698 snap_to (drag_info.grab_frame);
3701 if (pending_position < drag_info.grab_frame) {
3702 start = pending_position;
3703 end = drag_info.grab_frame;
3705 end = pending_position;
3706 start = drag_info.grab_frame;
3709 /* first drag: Either add to the selection
3710 or create a new selection->
3713 if (drag_info.first_move) {
3715 begin_reversible_command (_("range selection"));
3717 if (drag_info.copy) {
3718 /* adding to the selection */
3719 clicked_selection = selection->add (start, end);
3720 drag_info.copy = false;
3722 /* new selection-> */
3723 clicked_selection = selection->set (clicked_trackview, start, end);
3728 case SelectionStartTrim:
3730 if (drag_info.first_move) {
3731 begin_reversible_command (_("trim selection start"));
3734 start = selection->time[clicked_selection].start;
3735 end = selection->time[clicked_selection].end;
3737 if (pending_position > end) {
3740 start = pending_position;
3744 case SelectionEndTrim:
3746 if (drag_info.first_move) {
3747 begin_reversible_command (_("trim selection end"));
3750 start = selection->time[clicked_selection].start;
3751 end = selection->time[clicked_selection].end;
3753 if (pending_position < start) {
3756 end = pending_position;
3763 if (drag_info.first_move) {
3764 begin_reversible_command (_("move selection"));
3767 start = selection->time[clicked_selection].start;
3768 end = selection->time[clicked_selection].end;
3770 length = end - start;
3772 start = pending_position;
3775 end = start + length;
3780 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
3781 start_canvas_autoscroll (1);
3785 selection->replace (clicked_selection, start, end);
3788 drag_info.last_pointer_frame = pending_position;
3789 drag_info.first_move = false;
3791 if (selection_op == SelectionMove) {
3792 show_verbose_time_cursor(start, 10);
3794 show_verbose_time_cursor(pending_position, 10);
3799 Editor::end_selection_op (ArdourCanvas::Item* item, GdkEvent* event)
3801 if (!drag_info.first_move) {
3802 drag_selection (item, event);
3803 /* XXX this is not object-oriented programming at all. ick */
3804 if (selection->time.consolidate()) {
3805 selection->TimeChanged ();
3807 commit_reversible_command ();
3809 /* just a click, no pointer movement.*/
3811 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
3813 selection->clear_time();
3818 /* XXX what happens if its a music selection? */
3819 session->set_audio_range (selection->time);
3820 stop_canvas_autoscroll ();
3824 Editor::start_trim (ArdourCanvas::Item* item, GdkEvent* event)
3827 TimeAxisView* tvp = clicked_trackview;
3828 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
3830 if (tv && tv->is_audio_track()) {
3831 speed = tv->get_diskstream()->speed();
3834 jack_nframes_t region_start = (jack_nframes_t) (clicked_regionview->region.position() / speed);
3835 jack_nframes_t region_end = (jack_nframes_t) (clicked_regionview->region.last_frame() / speed);
3836 jack_nframes_t region_length = (jack_nframes_t) (clicked_regionview->region.length() / speed);
3838 motion_frozen_playlists.clear();
3840 //drag_info.item = clicked_regionview->get_name_highlight();
3841 drag_info.item = item;
3842 drag_info.motion_callback = &Editor::trim_motion_callback;
3843 drag_info.finished_callback = &Editor::trim_finished_callback;
3845 start_grab (event, trimmer_cursor);
3847 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
3848 trim_op = ContentsTrim;
3850 /* These will get overridden for a point trim.*/
3851 if (drag_info.current_pointer_frame < (region_start + region_length/2)) {
3852 /* closer to start */
3853 trim_op = StartTrim;
3854 } else if (drag_info.current_pointer_frame > (region_end - region_length/2)) {
3862 show_verbose_time_cursor(region_start, 10);
3865 show_verbose_time_cursor(region_end, 10);
3868 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
3874 Editor::trim_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
3876 AudioRegionView* rv = clicked_regionview;
3877 jack_nframes_t frame_delta = 0;
3878 bool left_direction;
3879 bool obey_snap = !Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier());
3881 /* snap modifier works differently here..
3882 its' current state has to be passed to the
3883 various trim functions in order to work properly
3887 TimeAxisView* tvp = clicked_trackview;
3888 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
3889 pair<set<Playlist*>::iterator,bool> insert_result;
3891 if (tv && tv->is_audio_track()) {
3892 speed = tv->get_diskstream()->speed();
3895 if (drag_info.last_pointer_frame > drag_info.current_pointer_frame) {
3896 left_direction = true;
3898 left_direction = false;
3902 snap_to (drag_info.current_pointer_frame);
3905 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
3909 if (drag_info.first_move) {
3915 trim_type = "Region start trim";
3918 trim_type = "Region end trim";
3921 trim_type = "Region content trim";
3925 begin_reversible_command (trim_type);
3927 for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin(); i != selection->audio_regions.by_layer().end(); ++i) {
3928 (*i)->region.freeze ();
3929 (*i)->temporarily_hide_envelope ();
3931 Playlist * pl = (*i)->region.playlist();
3932 insert_result = motion_frozen_playlists.insert (pl);
3933 if (insert_result.second) {
3934 session->add_undo (pl->get_memento());
3939 if (left_direction) {
3940 frame_delta = (drag_info.last_pointer_frame - drag_info.current_pointer_frame);
3942 frame_delta = (drag_info.current_pointer_frame - drag_info.last_pointer_frame);
3947 if ((left_direction == false) && (drag_info.current_pointer_frame <= rv->region.first_frame()/speed)) {
3950 for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin(); i != selection->audio_regions.by_layer().end(); ++i) {
3951 single_start_trim (**i, frame_delta, left_direction, obey_snap);
3957 if ((left_direction == true) && (drag_info.current_pointer_frame > (jack_nframes_t) (rv->region.last_frame()/speed))) {
3960 for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin(); i != selection->audio_regions.by_layer().end(); ++i) {
3961 single_end_trim (**i, frame_delta, left_direction, obey_snap);
3968 bool swap_direction = false;
3970 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
3971 swap_direction = true;
3974 for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin();
3975 i != selection->audio_regions.by_layer().end(); ++i)
3977 single_contents_trim (**i, frame_delta, left_direction, swap_direction, obey_snap);
3985 show_verbose_time_cursor((jack_nframes_t) (rv->region.position()/speed), 10);
3988 show_verbose_time_cursor((jack_nframes_t) (rv->region.last_frame()/speed), 10);
3991 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
3995 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
3996 drag_info.first_move = false;
4000 Editor::single_contents_trim (AudioRegionView& rv, jack_nframes_t frame_delta, bool left_direction, bool swap_direction, bool obey_snap)
4002 Region& region (rv.region);
4004 if (region.locked()) {
4008 jack_nframes_t new_bound;
4011 TimeAxisView* tvp = clicked_trackview;
4012 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
4014 if (tv && tv->is_audio_track()) {
4015 speed = tv->get_diskstream()->speed();
4018 if (left_direction) {
4019 if (swap_direction) {
4020 new_bound = (jack_nframes_t) (region.position()/speed) + frame_delta;
4022 new_bound = (jack_nframes_t) (region.position()/speed) - frame_delta;
4025 if (swap_direction) {
4026 new_bound = (jack_nframes_t) (region.position()/speed) - frame_delta;
4028 new_bound = (jack_nframes_t) (region.position()/speed) + frame_delta;
4033 snap_to (new_bound);
4035 region.trim_start ((jack_nframes_t) (new_bound * speed), this);
4036 rv.region_changed (StartChanged);
4040 Editor::single_start_trim (AudioRegionView& rv, jack_nframes_t frame_delta, bool left_direction, bool obey_snap)
4042 Region& region (rv.region);
4044 if (region.locked()) {
4048 jack_nframes_t new_bound;
4051 TimeAxisView* tvp = clicked_trackview;
4052 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
4054 if (tv && tv->is_audio_track()) {
4055 speed = tv->get_diskstream()->speed();
4058 if (left_direction) {
4059 new_bound = (jack_nframes_t) (region.position()/speed) - frame_delta;
4061 new_bound = (jack_nframes_t) (region.position()/speed) + frame_delta;
4065 snap_to (new_bound, (left_direction ? 0 : 1));
4068 region.trim_front ((jack_nframes_t) (new_bound * speed), this);
4070 rv.region_changed (Change (LengthChanged|PositionChanged|StartChanged));
4074 Editor::single_end_trim (AudioRegionView& rv, jack_nframes_t frame_delta, bool left_direction, bool obey_snap)
4076 Region& region (rv.region);
4078 if (region.locked()) {
4082 jack_nframes_t new_bound;
4085 TimeAxisView* tvp = clicked_trackview;
4086 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
4088 if (tv && tv->is_audio_track()) {
4089 speed = tv->get_diskstream()->speed();
4092 if (left_direction) {
4093 new_bound = (jack_nframes_t) ((region.last_frame() + 1)/speed) - frame_delta;
4095 new_bound = (jack_nframes_t) ((region.last_frame() + 1)/speed) + frame_delta;
4099 snap_to (new_bound);
4101 region.trim_end ((jack_nframes_t) (new_bound * speed), this);
4102 rv.region_changed (LengthChanged);
4106 Editor::trim_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
4108 if (!drag_info.first_move) {
4109 trim_motion_callback (item, event);
4111 if (!clicked_regionview->get_selected()) {
4112 thaw_region_after_trim (*clicked_regionview);
4115 for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin();
4116 i != selection->audio_regions.by_layer().end(); ++i)
4118 thaw_region_after_trim (**i);
4122 for (set<Playlist*>::iterator p = motion_frozen_playlists.begin(); p != motion_frozen_playlists.end(); ++p) {
4124 session->add_redo_no_execute ((*p)->get_memento());
4127 motion_frozen_playlists.clear ();
4129 commit_reversible_command();
4131 /* no mouse movement */
4137 Editor::point_trim (GdkEvent* event)
4139 AudioRegionView* rv = clicked_regionview;
4140 jack_nframes_t new_bound = drag_info.current_pointer_frame;
4142 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4143 snap_to (new_bound);
4146 /* Choose action dependant on which button was pressed */
4147 switch (event->button.button) {
4149 trim_op = StartTrim;
4150 begin_reversible_command (_("Start point trim"));
4152 if (rv->get_selected()) {
4154 for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin();
4155 i != selection->audio_regions.by_layer().end(); ++i)
4157 if (!(*i)->region.locked()) {
4158 session->add_undo ((*i)->region.playlist()->get_memento());
4159 (*i)->region.trim_front (new_bound, this);
4160 session->add_redo_no_execute ((*i)->region.playlist()->get_memento());
4166 if (!rv->region.locked()) {
4167 session->add_undo (rv->region.playlist()->get_memento());
4168 rv->region.trim_front (new_bound, this);
4169 session->add_redo_no_execute (rv->region.playlist()->get_memento());
4173 commit_reversible_command();
4178 begin_reversible_command (_("End point trim"));
4180 if (rv->get_selected()) {
4182 for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin(); i != selection->audio_regions.by_layer().end(); ++i)
4184 if (!(*i)->region.locked()) {
4185 session->add_undo ((*i)->region.playlist()->get_memento());
4186 (*i)->region.trim_end (new_bound, this);
4187 session->add_redo_no_execute ((*i)->region.playlist()->get_memento());
4193 if (!rv->region.locked()) {
4194 session->add_undo (rv->region.playlist()->get_memento());
4195 rv->region.trim_end (new_bound, this);
4196 session->add_redo_no_execute (rv->region.playlist()->get_memento());
4200 commit_reversible_command();
4209 Editor::thaw_region_after_trim (AudioRegionView& rv)
4211 Region& region (rv.region);
4213 if (region.locked()) {
4217 region.thaw (_("trimmed region"));
4218 session->add_redo_no_execute (region.playlist()->get_memento());
4220 rv.unhide_envelope ();
4224 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* event)
4229 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
4230 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
4234 Location* location = find_location_from_marker (marker, is_start);
4235 location->set_hidden (true, this);
4240 Editor::start_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event, RangeMarkerOp op)
4247 drag_info.item = item;
4248 drag_info.motion_callback = &Editor::drag_range_markerbar_op;
4249 drag_info.finished_callback = &Editor::end_range_markerbar_op;
4251 range_marker_op = op;
4253 if (!temp_location) {
4254 temp_location = new Location;
4258 case CreateRangeMarker:
4259 case CreateTransportMarker:
4261 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
4262 drag_info.copy = true;
4264 drag_info.copy = false;
4266 start_grab (event, selector_cursor);
4270 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4275 Editor::drag_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4277 jack_nframes_t start = 0;
4278 jack_nframes_t end = 0;
4279 ArdourCanvas::SimpleRect *crect = (range_marker_op == CreateRangeMarker) ? range_bar_drag_rect: transport_bar_drag_rect;
4281 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4282 snap_to (drag_info.current_pointer_frame);
4285 /* only alter selection if the current frame is
4286 different from the last frame position.
4289 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4291 switch (range_marker_op) {
4292 case CreateRangeMarker:
4293 case CreateTransportMarker:
4294 if (drag_info.first_move) {
4295 snap_to (drag_info.grab_frame);
4298 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4299 start = drag_info.current_pointer_frame;
4300 end = drag_info.grab_frame;
4302 end = drag_info.current_pointer_frame;
4303 start = drag_info.grab_frame;
4306 /* first drag: Either add to the selection
4307 or create a new selection->
4310 if (drag_info.first_move) {
4312 temp_location->set (start, end);
4316 update_marker_drag_item (temp_location);
4317 range_marker_drag_rect->show();
4318 range_marker_drag_rect->raise_to_top();
4324 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
4325 start_canvas_autoscroll (1);
4329 temp_location->set (start, end);
4331 double x1 = frame_to_pixel (start);
4332 double x2 = frame_to_pixel (end);
4333 crect->property_x1() = x1;
4334 crect->property_x2() = x2;
4336 update_marker_drag_item (temp_location);
4339 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4340 drag_info.first_move = false;
4342 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4347 Editor::end_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4349 Location * newloc = 0;
4351 if (!drag_info.first_move) {
4352 drag_range_markerbar_op (item, event);
4354 switch (range_marker_op) {
4355 case CreateRangeMarker:
4356 begin_reversible_command (_("new range marker"));
4357 session->add_undo (session->locations()->get_memento());
4358 newloc = new Location(temp_location->start(), temp_location->end(), "unnamed", Location::IsRangeMarker);
4359 session->locations()->add (newloc, true);
4360 session->add_redo_no_execute (session->locations()->get_memento());
4361 commit_reversible_command ();
4363 range_bar_drag_rect->hide();
4364 range_marker_drag_rect->hide();
4367 case CreateTransportMarker:
4368 // popup menu to pick loop or punch
4369 new_transport_marker_context_menu (&event->button, item);
4374 /* just a click, no pointer movement.*/
4376 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
4383 stop_canvas_autoscroll ();
4389 Editor::start_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4391 drag_info.item = item;
4392 drag_info.motion_callback = &Editor::drag_mouse_zoom;
4393 drag_info.finished_callback = &Editor::end_mouse_zoom;
4395 start_grab (event, zoom_cursor);
4397 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4401 Editor::drag_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4403 jack_nframes_t start;
4406 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4407 snap_to (drag_info.current_pointer_frame);
4409 if (drag_info.first_move) {
4410 snap_to (drag_info.grab_frame);
4414 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4416 /* base start and end on initial click position */
4417 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4418 start = drag_info.current_pointer_frame;
4419 end = drag_info.grab_frame;
4421 end = drag_info.current_pointer_frame;
4422 start = drag_info.grab_frame;
4427 if (drag_info.first_move) {
4429 zoom_rect->raise_to_top();
4432 reposition_zoom_rect(start, end);
4434 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4435 drag_info.first_move = false;
4437 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4442 Editor::end_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4444 if (!drag_info.first_move) {
4445 drag_mouse_zoom (item, event);
4447 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
4448 temporal_zoom_by_frame (drag_info.grab_frame, drag_info.last_pointer_frame, "mouse zoom");
4450 temporal_zoom_by_frame (drag_info.last_pointer_frame, drag_info.grab_frame, "mouse zoom");
4453 temporal_zoom_to_frame (false, drag_info.grab_frame);
4455 temporal_zoom_step (false);
4456 center_screen (drag_info.grab_frame);
4464 Editor::reposition_zoom_rect (jack_nframes_t start, jack_nframes_t end)
4466 double x1 = frame_to_pixel (start);
4467 double x2 = frame_to_pixel (end);
4468 double y2 = canvas_height - 2;
4470 zoom_rect->property_x1() = x1;
4471 zoom_rect->property_y1() = 1.0;
4472 zoom_rect->property_x2() = x2;
4473 zoom_rect->property_y2() = y2;
4477 Editor::start_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4479 drag_info.item = item;
4480 drag_info.motion_callback = &Editor::drag_rubberband_select;
4481 drag_info.finished_callback = &Editor::end_rubberband_select;
4483 start_grab (event, cross_hair_cursor);
4485 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4489 Editor::drag_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4491 jack_nframes_t start;
4496 /* use a bigger drag threshold than the default */
4498 if (abs ((int) (drag_info.current_pointer_frame - drag_info.grab_frame)) < 8) {
4502 // if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4503 // snap_to (drag_info.current_pointer_frame);
4505 // if (drag_info.first_move) {
4506 // snap_to (drag_info.grab_frame);
4511 /* base start and end on initial click position */
4512 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4513 start = drag_info.current_pointer_frame;
4514 end = drag_info.grab_frame;
4516 end = drag_info.current_pointer_frame;
4517 start = drag_info.grab_frame;
4520 if (drag_info.current_pointer_y < drag_info.grab_y) {
4521 y1 = drag_info.current_pointer_y;
4522 y2 = drag_info.grab_y;
4525 y2 = drag_info.current_pointer_y;
4526 y1 = drag_info.grab_y;
4530 if (start != end || y1 != y2) {
4532 double x1 = frame_to_pixel (start);
4533 double x2 = frame_to_pixel (end);
4535 rubberband_rect->property_x1() = x1;
4536 rubberband_rect->property_y1() = y1;
4537 rubberband_rect->property_x2() = x2;
4538 rubberband_rect->property_y2() = y2;
4540 rubberband_rect->show();
4541 rubberband_rect->raise_to_top();
4543 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4544 drag_info.first_move = false;
4546 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4551 Editor::end_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4553 if (!drag_info.first_move) {
4555 drag_rubberband_select (item, event);
4558 if (drag_info.current_pointer_y < drag_info.grab_y) {
4559 y1 = drag_info.current_pointer_y;
4560 y2 = drag_info.grab_y;
4563 y2 = drag_info.current_pointer_y;
4564 y1 = drag_info.grab_y;
4568 Selection::Operation op = Keyboard::selection_type (event->button.state);
4571 begin_reversible_command (_("select regions"));
4573 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
4574 commit = select_all_within (drag_info.grab_frame, drag_info.last_pointer_frame, y1, y2, op);
4576 commit = select_all_within (drag_info.last_pointer_frame, drag_info.grab_frame, y1, y2, op);
4580 commit_reversible_command ();
4584 selection->clear_audio_regions();
4585 selection->clear_points ();
4586 selection->clear_lines ();
4589 rubberband_rect->hide();
4594 Editor::mouse_rename_region (ArdourCanvas::Item* item, GdkEvent* event)
4596 using namespace Gtkmm2ext;
4598 ArdourPrompter prompter (false);
4600 prompter.set_prompt (_("Name for region:"));
4601 prompter.set_initial_text (clicked_regionview->region.name());
4602 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
4603 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
4604 prompter.show_all ();
4605 switch (prompter.run ()) {
4606 case Gtk::RESPONSE_ACCEPT:
4608 prompter.get_result(str);
4610 clicked_regionview->region.set_name (str);
4618 Editor::start_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
4620 drag_info.item = item;
4621 drag_info.motion_callback = &Editor::time_fx_motion;
4622 drag_info.finished_callback = &Editor::end_time_fx;
4626 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4630 Editor::time_fx_motion (ArdourCanvas::Item *item, GdkEvent* event)
4632 AudioRegionView* rv = clicked_regionview;
4634 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4635 snap_to (drag_info.current_pointer_frame);
4638 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
4642 if (drag_info.current_pointer_frame > rv->region.position()) {
4643 rv->get_time_axis_view().show_timestretch (rv->region.position(), drag_info.current_pointer_frame);
4646 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4647 drag_info.first_move = false;
4649 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4653 Editor::end_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
4655 clicked_regionview->get_time_axis_view().hide_timestretch ();
4657 if (drag_info.first_move) {
4661 jack_nframes_t newlen = drag_info.last_pointer_frame - clicked_regionview->region.position();
4662 float percentage = (float) ((double) newlen - (double) clicked_regionview->region.length()) / ((double) newlen) * 100.0f;
4664 begin_reversible_command (_("timestretch"));
4666 if (run_timestretch (selection->audio_regions, percentage) == 0) {
4667 session->commit_reversible_command ();
4672 Editor::mouse_brush_insert_region (AudioRegionView* rv, jack_nframes_t pos)
4674 /* no brushing without a useful snap setting */
4676 switch (snap_mode) {
4678 return; /* can't work because it allows region to be placed anywhere */
4683 switch (snap_type) {
4686 case SnapToEditCursor:
4693 /* don't brush a copy over the original */
4695 if (pos == rv->region.position()) {
4699 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*>(&rv->get_time_axis_view());
4701 if (atv == 0 || !atv->is_audio_track()) {
4705 Playlist* playlist = atv->playlist();
4706 double speed = atv->get_diskstream()->speed();
4708 session->add_undo (playlist->get_memento());
4709 playlist->add_region (*(new AudioRegion (rv->region)), (jack_nframes_t) (pos * speed));
4710 session->add_redo_no_execute (playlist->get_memento());
4712 // playlist is frozen, so we have to update manually
4714 playlist->StateChanged (Change (~0)); /* EMIT SIGNAL */
4718 Editor::track_height_step_timeout ()
4721 struct timeval delta;
4723 gettimeofday (&now, 0);
4724 timersub (&now, &last_track_height_step_timestamp, &delta);
4726 if (delta.tv_sec * 1000000 + delta.tv_usec > 250000) { /* milliseconds */
4727 current_stepping_trackview = 0;