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 (m == mouse_mode && !force) {
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), true);
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);
946 remove_marker (*item, event);
951 if (mouse_mode == MouseObject) {
952 remove_clicked_region ();
956 case GainControlPointItem:
957 if (mouse_mode == MouseGain) {
958 remove_gain_control_point (item, event);
962 case GainAutomationControlPointItem:
963 case PanAutomationControlPointItem:
964 case RedirectAutomationControlPointItem:
965 remove_control_point (item, event);
974 switch (event->button.button) {
978 /* see comments in button_press_handler */
980 case PlayheadCursorItem:
983 case GainAutomationLineItem:
984 case PanAutomationLineItem:
985 case RedirectAutomationLineItem:
986 case StartSelectionTrimItem:
987 case EndSelectionTrimItem:
991 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
992 snap_to (where, 0, true);
994 mouse_add_new_marker (where);
998 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1001 mouse_add_new_tempo_event (where);
1005 mouse_add_new_meter_event (pixel_to_frame (event->button.x));
1013 switch (mouse_mode) {
1015 switch (item_type) {
1016 case AutomationTrackItem:
1017 dynamic_cast<AutomationTimeAxisView*>(clicked_trackview)->add_automation_event
1031 switch (item_type) {
1033 clicked_regionview->add_gain_point_event (item, event);
1037 case AutomationTrackItem:
1038 dynamic_cast<AutomationTimeAxisView*>(clicked_trackview)->
1039 add_automation_event (item, event, where, event->button.y);
1048 switch (item_type) {
1050 audition_selected_region ();
1067 switch (mouse_mode) {
1070 switch (item_type) {
1072 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
1074 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::Shift|Keyboard::Alt))) {
1077 // Button2 click is unused
1090 // x_style_paste (where, 1.0);
1110 Editor::maybe_autoscroll (GdkEvent* event)
1112 jack_nframes_t one_page = (jack_nframes_t) rint (canvas_width * frames_per_unit);
1113 jack_nframes_t rightmost_frame = leftmost_frame + one_page;
1115 jack_nframes_t frame = drag_info.current_pointer_frame;
1117 if (autoscroll_timeout_tag < 0) {
1118 if (frame > rightmost_frame) {
1119 if (rightmost_frame < max_frames) {
1120 start_canvas_autoscroll (1);
1122 } else if (frame < leftmost_frame) {
1123 if (leftmost_frame > 0) {
1124 start_canvas_autoscroll (-1);
1128 if (frame >= leftmost_frame && frame < rightmost_frame) {
1129 stop_canvas_autoscroll ();
1135 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1141 switch (item_type) {
1142 case GainControlPointItem:
1143 if (mouse_mode == MouseGain) {
1144 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1145 cp->set_visible (true);
1149 at_y = cp->get_y ();
1150 cp->item->i2w (at_x, at_y);
1154 fraction = 1.0 - (cp->get_y() / cp->line.height());
1156 set_verbose_canvas_cursor (cp->line.get_verbose_cursor_string (fraction), at_x, at_y);
1157 show_verbose_canvas_cursor ();
1159 if (is_drawable()) {
1160 track_canvas.get_window()->set_cursor (*fader_cursor);
1165 case GainAutomationControlPointItem:
1166 case PanAutomationControlPointItem:
1167 case RedirectAutomationControlPointItem:
1168 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1169 cp->set_visible (true);
1173 at_y = cp->get_y ();
1174 cp->item->i2w (at_x, at_y);
1178 fraction = 1.0 - (cp->get_y() / cp->line.height());
1180 set_verbose_canvas_cursor (cp->line.get_verbose_cursor_string (fraction), at_x, at_y);
1181 show_verbose_canvas_cursor ();
1183 if (is_drawable()) {
1184 track_canvas.get_window()->set_cursor (*fader_cursor);
1189 if (mouse_mode == MouseGain) {
1190 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1192 line->property_fill_color_rgba() = color_map[cEnteredGainLine];
1193 if (is_drawable()) {
1194 track_canvas.get_window()->set_cursor (*fader_cursor);
1199 case GainAutomationLineItem:
1200 case RedirectAutomationLineItem:
1201 case PanAutomationLineItem:
1203 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1205 line->property_fill_color_rgba() = color_map[cEnteredAutomationLine];
1207 if (is_drawable()) {
1208 track_canvas.get_window()->set_cursor (*fader_cursor);
1212 case AudioRegionViewNameHighlight:
1213 if (is_drawable() && mouse_mode == MouseObject) {
1214 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1218 case StartSelectionTrimItem:
1219 case EndSelectionTrimItem:
1220 /* <CMT Additions> */
1221 case ImageFrameHandleStartItem:
1222 case ImageFrameHandleEndItem:
1223 case MarkerViewHandleStartItem:
1224 case MarkerViewHandleEndItem:
1225 /* </CMT Additions> */
1227 if (is_drawable()) {
1228 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1232 case EditCursorItem:
1233 case PlayheadCursorItem:
1234 if (is_drawable()) {
1235 track_canvas.get_window()->set_cursor (*grabber_cursor);
1239 case AudioRegionViewName:
1241 /* when the name is not an active item, the entire name highlight is for trimming */
1243 if (!reinterpret_cast<AudioRegionView *> (item->get_data ("regionview"))->name_active()) {
1244 if (mouse_mode == MouseObject && is_drawable()) {
1245 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1251 case AutomationTrackItem:
1252 if (is_drawable()) {
1253 Gdk::Cursor *cursor;
1254 switch (mouse_mode) {
1256 cursor = selector_cursor;
1259 cursor = zoom_cursor;
1262 cursor = cross_hair_cursor;
1266 track_canvas.get_window()->set_cursor (*cursor);
1268 AutomationTimeAxisView* atv;
1269 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1270 clear_entered_track = false;
1271 set_entered_track (atv);
1277 case RangeMarkerBarItem:
1278 case TransportMarkerBarItem:
1281 if (is_drawable()) {
1282 time_canvas.get_window()->set_cursor (*timebar_cursor);
1287 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1290 marker->set_color_rgba (color_map[cEnteredMarker]);
1292 case MeterMarkerItem:
1293 case TempoMarkerItem:
1294 if (is_drawable()) {
1295 time_canvas.get_window()->set_cursor (*timebar_cursor);
1298 case FadeInHandleItem:
1299 case FadeOutHandleItem:
1300 if (mouse_mode == MouseObject) {
1301 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1303 rect->property_fill_color_rgba() = 0;
1304 rect->property_outline_pixels() = 1;
1313 /* second pass to handle entered track status in a comprehensible way.
1316 switch (item_type) {
1318 case GainAutomationLineItem:
1319 case RedirectAutomationLineItem:
1320 case PanAutomationLineItem:
1321 case GainControlPointItem:
1322 case GainAutomationControlPointItem:
1323 case PanAutomationControlPointItem:
1324 case RedirectAutomationControlPointItem:
1325 /* these do not affect the current entered track state */
1326 clear_entered_track = false;
1329 case AutomationTrackItem:
1330 /* handled above already */
1334 set_entered_track (0);
1342 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1348 AudioRegionView* rv;
1351 switch (item_type) {
1352 case GainControlPointItem:
1353 case GainAutomationControlPointItem:
1354 case PanAutomationControlPointItem:
1355 case RedirectAutomationControlPointItem:
1356 cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
1357 if (cp->line.npoints() > 1) {
1358 if (!cp->selected) {
1359 cp->set_visible (false);
1363 if (is_drawable()) {
1364 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1367 hide_verbose_canvas_cursor ();
1370 case AudioRegionViewNameHighlight:
1371 case StartSelectionTrimItem:
1372 case EndSelectionTrimItem:
1373 case EditCursorItem:
1374 case PlayheadCursorItem:
1375 /* <CMT Additions> */
1376 case ImageFrameHandleStartItem:
1377 case ImageFrameHandleEndItem:
1378 case MarkerViewHandleStartItem:
1379 case MarkerViewHandleEndItem:
1380 /* </CMT Additions> */
1381 if (is_drawable()) {
1382 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1387 case GainAutomationLineItem:
1388 case RedirectAutomationLineItem:
1389 case PanAutomationLineItem:
1390 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1392 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1394 line->property_fill_color_rgba() = al->get_line_color();
1396 if (is_drawable()) {
1397 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1401 case AudioRegionViewName:
1402 /* see enter_handler() for notes */
1403 if (!reinterpret_cast<AudioRegionView *> (item->get_data ("regionview"))->name_active()) {
1404 if (is_drawable() && mouse_mode == MouseObject) {
1405 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1410 case RangeMarkerBarItem:
1411 case TransportMarkerBarItem:
1415 if (is_drawable()) {
1416 time_canvas.get_window()->set_cursor (*timebar_cursor);
1421 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1424 loc = find_location_from_marker (marker, is_start);
1425 if (loc) location_flags_changed (loc, this);
1427 case MeterMarkerItem:
1428 case TempoMarkerItem:
1430 if (is_drawable()) {
1431 time_canvas.get_window()->set_cursor (*timebar_cursor);
1436 case FadeInHandleItem:
1437 case FadeOutHandleItem:
1438 rv = static_cast<AudioRegionView*>(item->get_data ("regionview"));
1440 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1442 rect->property_fill_color_rgba() = rv->get_fill_color();
1443 rect->property_outline_pixels() = 0;
1448 case AutomationTrackItem:
1449 if (is_drawable()) {
1450 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1451 clear_entered_track = true;
1452 Glib::signal_idle().connect (mem_fun(*this, &Editor::left_automation_track));
1464 Editor::left_automation_track ()
1466 if (clear_entered_track) {
1467 set_entered_track (0);
1468 clear_entered_track = false;
1474 Editor::motion_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1478 /* We call this so that MOTION_NOTIFY events continue to be
1479 delivered to the canvas. We need to do this because we set
1480 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
1481 the density of the events, at the expense of a round-trip
1482 to the server. Given that this will mostly occur on cases
1483 where DISPLAY = :0.0, and given the cost of what the motion
1484 event might do, its a good tradeoff.
1487 track_canvas.get_pointer (x, y);
1489 if (current_stepping_trackview) {
1490 /* don't keep the persistent stepped trackview if the mouse moves */
1491 current_stepping_trackview = 0;
1492 step_timeout.disconnect ();
1495 if (session && session->actively_recording()) {
1496 /* Sorry. no dragging stuff around while we record */
1500 drag_info.current_pointer_frame = event_frame (event, &drag_info.current_pointer_x,
1501 &drag_info.current_pointer_y);
1503 if (drag_info.item) {
1504 /* item != 0 is the best test i can think of for
1507 if (!drag_info.move_threshold_passsed) {
1508 drag_info.move_threshold_passsed = (abs ((int) (drag_info.current_pointer_x - drag_info.grab_x)) > 4);
1510 // and change the initial grab loc/frame if this drag info wants us to
1511 if (drag_info.want_move_threshold && drag_info.move_threshold_passsed) {
1512 drag_info.grab_frame = drag_info.current_pointer_frame;
1513 drag_info.grab_x = drag_info.current_pointer_x;
1514 drag_info.grab_y = drag_info.current_pointer_y;
1515 drag_info.last_pointer_frame = drag_info.grab_frame;
1516 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
1521 switch (item_type) {
1522 case PlayheadCursorItem:
1523 case EditCursorItem:
1525 case GainControlPointItem:
1526 case RedirectAutomationControlPointItem:
1527 case GainAutomationControlPointItem:
1528 case PanAutomationControlPointItem:
1529 case TempoMarkerItem:
1530 case MeterMarkerItem:
1531 case AudioRegionViewNameHighlight:
1532 case StartSelectionTrimItem:
1533 case EndSelectionTrimItem:
1536 case RedirectAutomationLineItem:
1537 case GainAutomationLineItem:
1538 case PanAutomationLineItem:
1539 case FadeInHandleItem:
1540 case FadeOutHandleItem:
1541 /* <CMT Additions> */
1542 case ImageFrameHandleStartItem:
1543 case ImageFrameHandleEndItem:
1544 case MarkerViewHandleStartItem:
1545 case MarkerViewHandleEndItem:
1546 /* </CMT Additions> */
1547 if (drag_info.item && (event->motion.state & Gdk::BUTTON1_MASK ||
1548 (event->motion.state & Gdk::BUTTON2_MASK))) {
1549 maybe_autoscroll (event);
1550 (this->*(drag_info.motion_callback)) (item, event);
1559 switch (mouse_mode) {
1564 if (drag_info.item && (event->motion.state & GDK_BUTTON1_MASK ||
1565 (event->motion.state & GDK_BUTTON2_MASK))) {
1566 maybe_autoscroll (event);
1567 (this->*(drag_info.motion_callback)) (item, event);
1578 track_canvas_motion (event);
1586 Editor::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
1588 if (drag_info.item == 0) {
1589 fatal << _("programming error: start_grab called without drag item") << endmsg;
1595 cursor = grabber_cursor;
1598 // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
1600 if (event->button.button == 2) {
1601 drag_info.x_constrained = true;
1603 drag_info.x_constrained = false;
1606 drag_info.grab_frame = event_frame(event, &drag_info.grab_x, &drag_info.grab_y);
1607 drag_info.last_pointer_frame = drag_info.grab_frame;
1608 drag_info.current_pointer_frame = drag_info.grab_frame;
1609 drag_info.current_pointer_x = drag_info.grab_x;
1610 drag_info.current_pointer_y = drag_info.grab_y;
1611 drag_info.cumulative_x_drag = 0;
1612 drag_info.cumulative_y_drag = 0;
1613 drag_info.first_move = true;
1614 drag_info.move_threshold_passsed = false;
1615 drag_info.want_move_threshold = false;
1616 drag_info.pointer_frame_offset = 0;
1617 drag_info.brushing = false;
1618 drag_info.copied_location = 0;
1620 drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK,
1622 event->button.time);
1624 if (session && session->transport_rolling()) {
1625 drag_info.was_rolling = true;
1627 drag_info.was_rolling = false;
1630 switch (snap_type) {
1631 case SnapToRegionStart:
1632 case SnapToRegionEnd:
1633 case SnapToRegionSync:
1634 case SnapToRegionBoundary:
1635 build_region_boundary_cache ();
1643 Editor::end_grab (ArdourCanvas::Item* item, GdkEvent* event)
1645 bool did_drag = false;
1647 stop_canvas_autoscroll ();
1649 if (drag_info.item == 0) {
1653 drag_info.item->ungrab (event->button.time);
1655 if (drag_info.finished_callback) {
1656 (this->*(drag_info.finished_callback)) (item, event);
1659 did_drag = !drag_info.first_move;
1661 hide_verbose_canvas_cursor();
1664 drag_info.copy = false;
1665 drag_info.motion_callback = 0;
1666 drag_info.finished_callback = 0;
1667 drag_info.last_trackview = 0;
1668 drag_info.last_frame_position = 0;
1669 drag_info.grab_frame = 0;
1670 drag_info.last_pointer_frame = 0;
1671 drag_info.current_pointer_frame = 0;
1672 drag_info.brushing = false;
1674 if (drag_info.copied_location) {
1675 delete drag_info.copied_location;
1676 drag_info.copied_location = 0;
1683 Editor::set_edit_cursor (GdkEvent* event)
1685 jack_nframes_t pointer_frame = event_frame (event);
1687 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1688 if (snap_type != SnapToEditCursor) {
1689 snap_to (pointer_frame);
1693 edit_cursor->set_position (pointer_frame);
1694 edit_cursor_clock.set (pointer_frame);
1698 Editor::set_playhead_cursor (GdkEvent* event)
1700 jack_nframes_t pointer_frame = event_frame (event);
1702 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1703 snap_to (pointer_frame);
1707 session->request_locate (pointer_frame, session->transport_rolling());
1712 Editor::start_fade_in_grab (ArdourCanvas::Item* item, GdkEvent* event)
1714 drag_info.item = item;
1715 drag_info.motion_callback = &Editor::fade_in_drag_motion_callback;
1716 drag_info.finished_callback = &Editor::fade_in_drag_finished_callback;
1720 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
1721 fatal << _("programming error: fade in canvas item has no regionview data pointer!") << endmsg;
1725 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1727 drag_info.pointer_frame_offset = drag_info.grab_frame - ((jack_nframes_t) arv->region.fade_in().back()->when + arv->region.position());
1731 Editor::fade_in_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1733 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1735 jack_nframes_t fade_length;
1737 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1738 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1744 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1748 if (pos < (arv->region.position() + 64)) {
1749 fade_length = 64; // this should be a minimum defined somewhere
1750 } else if (pos > arv->region.last_frame()) {
1751 fade_length = arv->region.length();
1753 fade_length = pos - arv->region.position();
1756 arv->reset_fade_in_shape_width (fade_length);
1758 show_verbose_duration_cursor (arv->region.position(), arv->region.position() + fade_length, 10);
1760 drag_info.first_move = false;
1764 Editor::fade_in_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1766 if (drag_info.first_move) return;
1768 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1770 jack_nframes_t fade_length;
1772 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1773 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1779 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1783 if (pos < (arv->region.position() + 64)) {
1784 fade_length = 64; // this should be a minimum defined somewhere
1786 else if (pos > arv->region.last_frame()) {
1787 fade_length = arv->region.length();
1790 fade_length = pos - arv->region.position();
1793 begin_reversible_command (_("change fade in length"));
1794 session->add_undo (arv->region.get_memento());
1795 arv->region.set_fade_in_length (fade_length);
1796 session->add_redo_no_execute (arv->region.get_memento());
1797 commit_reversible_command ();
1798 fade_in_drag_motion_callback (item, event);
1802 Editor::start_fade_out_grab (ArdourCanvas::Item* item, GdkEvent* event)
1804 drag_info.item = item;
1805 drag_info.motion_callback = &Editor::fade_out_drag_motion_callback;
1806 drag_info.finished_callback = &Editor::fade_out_drag_finished_callback;
1810 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
1811 fatal << _("programming error: fade out canvas item has no regionview data pointer!") << endmsg;
1815 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1817 drag_info.pointer_frame_offset = drag_info.grab_frame - (arv->region.length() - (jack_nframes_t) arv->region.fade_out().back()->when + arv->region.position());
1821 Editor::fade_out_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1823 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1825 jack_nframes_t fade_length;
1827 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1828 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1834 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1838 if (pos > (arv->region.last_frame() - 64)) {
1839 fade_length = 64; // this should really be a minimum fade defined somewhere
1841 else if (pos < arv->region.position()) {
1842 fade_length = arv->region.length();
1845 fade_length = arv->region.last_frame() - pos;
1848 arv->reset_fade_out_shape_width (fade_length);
1850 show_verbose_duration_cursor (arv->region.last_frame() - fade_length, arv->region.last_frame(), 10);
1852 drag_info.first_move = false;
1856 Editor::fade_out_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1858 if (drag_info.first_move) return;
1860 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1862 jack_nframes_t fade_length;
1864 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1865 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1871 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1875 if (pos > (arv->region.last_frame() - 64)) {
1876 fade_length = 64; // this should really be a minimum fade defined somewhere
1878 else if (pos < arv->region.position()) {
1879 fade_length = arv->region.length();
1882 fade_length = arv->region.last_frame() - pos;
1885 begin_reversible_command (_("change fade out length"));
1886 session->add_undo (arv->region.get_memento());
1887 arv->region.set_fade_out_length (fade_length);
1888 session->add_redo_no_execute (arv->region.get_memento());
1889 commit_reversible_command ();
1891 fade_out_drag_motion_callback (item, event);
1895 Editor::start_cursor_grab (ArdourCanvas::Item* item, GdkEvent* event)
1897 drag_info.item = item;
1898 drag_info.motion_callback = &Editor::cursor_drag_motion_callback;
1899 drag_info.finished_callback = &Editor::cursor_drag_finished_callback;
1903 if ((drag_info.data = (item->get_data ("cursor"))) == 0) {
1904 fatal << _("programming error: cursor canvas item has no cursor data pointer!") << endmsg;
1908 Cursor* cursor = (Cursor *) drag_info.data;
1910 if (session && cursor == playhead_cursor) {
1911 if (drag_info.was_rolling) {
1912 session->request_stop ();
1916 drag_info.pointer_frame_offset = drag_info.grab_frame - cursor->current_frame;
1918 show_verbose_time_cursor (cursor->current_frame, 10);
1922 Editor::cursor_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1924 Cursor* cursor = (Cursor *) drag_info.data;
1925 jack_nframes_t adjusted_frame;
1927 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1928 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1934 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1935 if (cursor != edit_cursor || snap_type != SnapToEditCursor) {
1936 snap_to (adjusted_frame);
1940 if (adjusted_frame == drag_info.last_pointer_frame) return;
1942 cursor->set_position (adjusted_frame);
1944 if (cursor == edit_cursor) {
1945 edit_cursor_clock.set (cursor->current_frame);
1948 show_verbose_time_cursor (cursor->current_frame, 10);
1950 drag_info.last_pointer_frame = adjusted_frame;
1951 drag_info.first_move = false;
1955 Editor::cursor_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1957 if (drag_info.first_move) return;
1959 cursor_drag_motion_callback (item, event);
1961 if (item == &playhead_cursor->canvas_item) {
1963 session->request_locate (playhead_cursor->current_frame, drag_info.was_rolling);
1965 } else if (item == &edit_cursor->canvas_item) {
1966 edit_cursor->set_position (edit_cursor->current_frame);
1967 edit_cursor_clock.set (edit_cursor->current_frame);
1972 Editor::update_marker_drag_item (Location *location)
1974 double x1 = frame_to_pixel (location->start());
1975 double x2 = frame_to_pixel (location->end());
1977 if (location->is_mark()) {
1978 marker_drag_line_points.front().set_x(x1);
1979 marker_drag_line_points.back().set_x(x1);
1980 marker_drag_line->property_points() = marker_drag_line_points;
1983 range_marker_drag_rect->property_x1() = x1;
1984 range_marker_drag_rect->property_x2() = x2;
1989 Editor::start_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
1993 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1994 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2000 Location *location = find_location_from_marker (marker, is_start);
2002 drag_info.item = item;
2003 drag_info.data = marker;
2004 drag_info.motion_callback = &Editor::marker_drag_motion_callback;
2005 drag_info.finished_callback = &Editor::marker_drag_finished_callback;
2009 drag_info.copied_location = new Location (*location);
2010 drag_info.pointer_frame_offset = drag_info.grab_frame - (is_start ? location->start() : location->end());
2012 update_marker_drag_item (location);
2014 if (location->is_mark()) {
2015 marker_drag_line->show();
2016 marker_drag_line->raise_to_top();
2019 range_marker_drag_rect->show();
2020 range_marker_drag_rect->raise_to_top();
2023 if (is_start) show_verbose_time_cursor (location->start(), 10);
2024 else show_verbose_time_cursor (location->end(), 10);
2028 Editor::marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2030 jack_nframes_t f_delta;
2031 Marker* marker = (Marker *) drag_info.data;
2032 Location *real_location;
2033 Location *copy_location;
2035 bool move_both = false;
2037 jack_nframes_t newframe;
2038 if (drag_info.pointer_frame_offset <= (long) drag_info.current_pointer_frame) {
2039 newframe = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2045 jack_nframes_t next = newframe;
2047 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2048 snap_to (newframe, 0, true);
2051 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
2053 /* call this to find out if its the start or end */
2055 real_location = find_location_from_marker (marker, is_start);
2057 /* use the copy that we're "dragging" around */
2059 copy_location = drag_info.copied_location;
2061 f_delta = copy_location->end() - copy_location->start();
2063 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
2067 if (is_start) { // start marker
2070 copy_location->set_start (newframe);
2071 copy_location->set_end (newframe + f_delta);
2072 } else if (newframe < copy_location->end()) {
2073 copy_location->set_start (newframe);
2075 snap_to (next, 1, true);
2076 copy_location->set_end (next);
2077 copy_location->set_start (newframe);
2080 } else { // end marker
2083 copy_location->set_end (newframe);
2084 copy_location->set_start (newframe - f_delta);
2085 } else if (newframe > copy_location->start()) {
2086 copy_location->set_end (newframe);
2088 } else if (newframe > 0) {
2089 snap_to (next, -1, true);
2090 copy_location->set_start (next);
2091 copy_location->set_end (newframe);
2095 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
2096 drag_info.first_move = false;
2098 update_marker_drag_item (copy_location);
2100 LocationMarkers* lm = find_location_markers (real_location);
2101 lm->set_position (copy_location->start(), copy_location->end());
2103 show_verbose_time_cursor (newframe, 10);
2107 Editor::marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2109 if (drag_info.first_move) {
2110 marker_drag_motion_callback (item, event);
2114 Marker* marker = (Marker *) drag_info.data;
2119 begin_reversible_command ( _("move marker") );
2120 session->add_undo( session->locations()->get_memento() );
2122 Location * location = find_location_from_marker (marker, is_start);
2125 location->set (drag_info.copied_location->start(), drag_info.copied_location->end());
2128 session->add_redo_no_execute( session->locations()->get_memento() );
2129 commit_reversible_command ();
2131 marker_drag_line->hide();
2132 range_marker_drag_rect->hide();
2136 Editor::start_meter_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2139 MeterMarker* meter_marker;
2141 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2142 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2146 meter_marker = dynamic_cast<MeterMarker*> (marker);
2148 MetricSection& section (meter_marker->meter());
2150 if (!section.movable()) {
2154 drag_info.item = item;
2155 drag_info.data = marker;
2156 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2157 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2161 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2163 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2167 Editor::start_meter_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2170 MeterMarker* meter_marker;
2172 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2173 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2177 meter_marker = dynamic_cast<MeterMarker*> (marker);
2179 // create a dummy marker for visual representation of moving the copy.
2180 // The actual copying is not done before we reach the finish callback.
2182 snprintf (name, sizeof(name), "%g/%g", meter_marker->meter().beats_per_bar(), meter_marker->meter().note_divisor ());
2183 MeterMarker* new_marker = new MeterMarker(*this, *meter_group, color_map[cMeterMarker], name,
2184 *new MeterSection(meter_marker->meter()));
2186 drag_info.item = &new_marker->the_item();
2187 drag_info.copy = true;
2188 drag_info.data = new_marker;
2189 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2190 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2194 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2196 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2200 Editor::meter_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2202 MeterMarker* marker = (MeterMarker *) drag_info.data;
2203 jack_nframes_t adjusted_frame;
2205 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2206 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2212 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2213 snap_to (adjusted_frame);
2216 if (adjusted_frame == drag_info.last_pointer_frame) return;
2218 marker->set_position (adjusted_frame);
2221 drag_info.last_pointer_frame = adjusted_frame;
2222 drag_info.first_move = false;
2224 show_verbose_time_cursor (adjusted_frame, 10);
2228 Editor::meter_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2230 if (drag_info.first_move) return;
2232 meter_marker_drag_motion_callback (drag_info.item, event);
2234 MeterMarker* marker = (MeterMarker *) drag_info.data;
2237 TempoMap& map (session->tempo_map());
2238 map.bbt_time (drag_info.last_pointer_frame, when);
2240 if (drag_info.copy == true) {
2241 begin_reversible_command (_("copy meter mark"));
2242 session->add_undo (map.get_memento());
2243 map.add_meter (marker->meter(), when);
2244 session->add_redo_no_execute (map.get_memento());
2245 commit_reversible_command ();
2247 // delete the dummy marker we used for visual representation of copying.
2248 // a new visual marker will show up automatically.
2251 begin_reversible_command (_("move meter mark"));
2252 session->add_undo (map.get_memento());
2253 map.move_meter (marker->meter(), when);
2254 session->add_redo_no_execute (map.get_memento());
2255 commit_reversible_command ();
2260 Editor::start_tempo_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2263 TempoMarker* tempo_marker;
2265 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2266 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2270 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2271 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2275 MetricSection& section (tempo_marker->tempo());
2277 if (!section.movable()) {
2281 drag_info.item = item;
2282 drag_info.data = marker;
2283 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2284 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2288 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2289 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2293 Editor::start_tempo_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2296 TempoMarker* tempo_marker;
2298 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2299 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2303 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2304 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2308 // create a dummy marker for visual representation of moving the copy.
2309 // The actual copying is not done before we reach the finish callback.
2311 snprintf (name, sizeof (name), "%.2f", tempo_marker->tempo().beats_per_minute());
2312 TempoMarker* new_marker = new TempoMarker(*this, *tempo_group, color_map[cTempoMarker], name,
2313 *new TempoSection(tempo_marker->tempo()));
2315 drag_info.item = &new_marker->the_item();
2316 drag_info.copy = true;
2317 drag_info.data = new_marker;
2318 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2319 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2323 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2325 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2329 Editor::tempo_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2331 TempoMarker* marker = (TempoMarker *) drag_info.data;
2332 jack_nframes_t adjusted_frame;
2334 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2335 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2341 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2342 snap_to (adjusted_frame);
2345 if (adjusted_frame == drag_info.last_pointer_frame) return;
2347 /* OK, we've moved far enough to make it worth actually move the thing. */
2349 marker->set_position (adjusted_frame);
2351 show_verbose_time_cursor (adjusted_frame, 10);
2353 drag_info.last_pointer_frame = adjusted_frame;
2354 drag_info.first_move = false;
2358 Editor::tempo_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2360 if (drag_info.first_move) return;
2362 tempo_marker_drag_motion_callback (drag_info.item, event);
2364 TempoMarker* marker = (TempoMarker *) drag_info.data;
2367 TempoMap& map (session->tempo_map());
2368 map.bbt_time (drag_info.last_pointer_frame, when);
2370 if (drag_info.copy == true) {
2371 begin_reversible_command (_("copy tempo mark"));
2372 session->add_undo (map.get_memento());
2373 map.add_tempo (marker->tempo(), when);
2374 session->add_redo_no_execute (map.get_memento());
2375 commit_reversible_command ();
2377 // delete the dummy marker we used for visual representation of copying.
2378 // a new visual marker will show up automatically.
2381 begin_reversible_command (_("move tempo mark"));
2382 session->add_undo (map.get_memento());
2383 map.move_tempo (marker->tempo(), when);
2384 session->add_redo_no_execute (map.get_memento());
2385 commit_reversible_command ();
2390 Editor::remove_gain_control_point (ArdourCanvas::Item*item, GdkEvent* event)
2392 ControlPoint* control_point;
2394 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2395 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2399 // We shouldn't remove the first or last gain point
2400 if (control_point->line.is_last_point(*control_point) ||
2401 control_point->line.is_first_point(*control_point)) {
2405 control_point->line.remove_point (*control_point);
2409 Editor::remove_control_point (ArdourCanvas::Item*item, GdkEvent* event)
2411 ControlPoint* control_point;
2413 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2414 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2418 control_point->line.remove_point (*control_point);
2422 Editor::start_control_point_grab (ArdourCanvas::Item* item, GdkEvent* event)
2424 ControlPoint* control_point;
2426 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2427 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2431 drag_info.item = item;
2432 drag_info.data = control_point;
2433 drag_info.motion_callback = &Editor::control_point_drag_motion_callback;
2434 drag_info.finished_callback = &Editor::control_point_drag_finished_callback;
2436 start_grab (event, fader_cursor);
2438 control_point->line.start_drag (control_point, 0);
2440 float fraction = 1.0 - (control_point->get_y() / control_point->line.height());
2441 set_verbose_canvas_cursor (control_point->line.get_verbose_cursor_string (fraction),
2442 drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
2444 show_verbose_canvas_cursor ();
2448 Editor::control_point_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2450 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2452 double cx = drag_info.current_pointer_x;
2453 double cy = drag_info.current_pointer_y;
2455 drag_info.cumulative_x_drag = cx - drag_info.grab_x ;
2456 drag_info.cumulative_y_drag = cy - drag_info.grab_y ;
2458 bool x_constrained = false;
2460 if (drag_info.x_constrained) {
2461 if (fabs(drag_info.cumulative_x_drag) < fabs(drag_info.cumulative_y_drag)) {
2462 cx = drag_info.grab_x;
2463 x_constrained = true;
2466 cy = drag_info.grab_y;
2471 cp->line.parent_group().w2i (cx, cy);
2475 cy = min ((double) cp->line.height(), cy);
2477 //translate cx to frames
2478 jack_nframes_t cx_frames = (jack_nframes_t) floor (cx * frames_per_unit);
2480 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier()) && !x_constrained) {
2481 snap_to (cx_frames);
2484 float fraction = 1.0 - (cy / cp->line.height());
2488 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
2494 cp->line.point_drag (*cp, cx_frames , fraction, push);
2496 set_verbose_canvas_cursor_text (cp->line.get_verbose_cursor_string (fraction));
2500 Editor::control_point_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2502 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2503 control_point_drag_motion_callback (item, event);
2504 cp->line.end_drag (cp);
2508 Editor::start_line_grab_from_regionview (ArdourCanvas::Item* item, GdkEvent* event)
2510 switch (mouse_mode) {
2512 start_line_grab (clicked_regionview->get_gain_line(), event);
2520 Editor::start_line_grab_from_line (ArdourCanvas::Item* item, GdkEvent* event)
2524 if ((al = reinterpret_cast<AutomationLine*> (item->get_data ("line"))) == 0) {
2525 fatal << _("programming error: line canvas item has no line pointer!") << endmsg;
2529 start_line_grab (al, event);
2533 Editor::start_line_grab (AutomationLine* line, GdkEvent* event)
2537 jack_nframes_t frame_within_region;
2539 /* need to get x coordinate in terms of parent (TimeAxisItemView)
2543 cx = event->button.x;
2544 cy = event->button.y;
2545 line->parent_group().w2i (cx, cy);
2546 frame_within_region = (jack_nframes_t) floor (cx * frames_per_unit);
2548 if (!line->control_points_adjacent (frame_within_region, current_line_drag_info.before,
2549 current_line_drag_info.after)) {
2550 /* no adjacent points */
2554 drag_info.item = &line->grab_item();
2555 drag_info.data = line;
2556 drag_info.motion_callback = &Editor::line_drag_motion_callback;
2557 drag_info.finished_callback = &Editor::line_drag_finished_callback;
2559 start_grab (event, fader_cursor);
2561 double fraction = 1.0 - (cy / line->height());
2563 line->start_drag (0, fraction);
2565 set_verbose_canvas_cursor (line->get_verbose_cursor_string (fraction),
2566 drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
2567 show_verbose_canvas_cursor ();
2571 Editor::line_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2573 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
2574 double cx = drag_info.current_pointer_x;
2575 double cy = drag_info.current_pointer_y;
2577 line->parent_group().w2i (cx, cy);
2580 fraction = 1.0 - (cy / line->height());
2584 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
2590 line->line_drag (current_line_drag_info.before, current_line_drag_info.after, fraction, push);
2592 set_verbose_canvas_cursor_text (line->get_verbose_cursor_string (fraction));
2596 Editor::line_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2598 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
2599 line_drag_motion_callback (item, event);
2604 Editor::start_region_grab (ArdourCanvas::Item* item, GdkEvent* event)
2606 if (selection->audio_regions.empty() || clicked_regionview == 0) {
2610 drag_info.copy = false;
2611 drag_info.item = item;
2612 drag_info.data = clicked_regionview;
2613 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2614 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2619 TimeAxisView* tvp = clicked_trackview;
2620 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
2622 if (tv && tv->is_audio_track()) {
2623 speed = tv->get_diskstream()->speed();
2626 drag_info.last_frame_position = (jack_nframes_t) (clicked_regionview->region.position() / speed);
2627 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2628 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2629 // we want a move threshold
2630 drag_info.want_move_threshold = true;
2632 show_verbose_time_cursor (drag_info.last_frame_position, 10);
2634 begin_reversible_command (_("move region(s)"));
2638 Editor::start_region_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2640 if (selection->audio_regions.empty() || clicked_regionview == 0) {
2644 /* this is committed in the grab finished callback. */
2646 begin_reversible_command (_("Drag region copy"));
2648 /* duplicate the region(s) */
2650 vector<AudioRegionView*> new_regionviews;
2652 set<Playlist*> affected_playlists;
2653 pair<set<Playlist*>::iterator,bool> insert_result;
2655 for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin(); i != selection->audio_regions.by_layer().end(); ++i) {
2656 AudioRegionView* rv;
2660 Playlist* to_playlist = rv->region.playlist();
2661 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*>(&rv->get_time_axis_view());
2663 insert_result = affected_playlists.insert (to_playlist);
2664 if (insert_result.second) {
2665 session->add_undo (to_playlist->get_memento ());
2668 latest_regionview = 0;
2670 sigc::connection c = atv->view->AudioRegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
2672 /* create a new region with the same name.
2675 AudioRegion* newregion = new AudioRegion (rv->region);
2677 /* if the original region was locked, we don't care */
2679 newregion->set_locked (false);
2681 to_playlist->add_region (*newregion, (jack_nframes_t) (rv->region.position() * atv->get_diskstream()->speed()));
2685 if (latest_regionview) {
2686 new_regionviews.push_back (latest_regionview);
2690 if (new_regionviews.empty()) {
2694 /* reset selection to new regionviews */
2696 selection->set (new_regionviews);
2698 /* reset drag_info data to reflect the fact that we are dragging the copies */
2700 drag_info.data = new_regionviews.front();
2701 drag_info.item = new_regionviews.front()->get_canvas_group ();
2703 drag_info.copy = true;
2704 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2705 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2709 TimeAxisView* tv = &clicked_regionview->get_time_axis_view();
2710 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*>(tv);
2713 if (atv && atv->is_audio_track()) {
2714 speed = atv->get_diskstream()->speed();
2717 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2718 drag_info.last_frame_position = (jack_nframes_t) (clicked_regionview->region.position() / speed);
2719 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2720 // we want a move threshold
2721 drag_info.want_move_threshold = true;
2723 show_verbose_time_cursor (drag_info.last_frame_position, 10);
2725 //begin_reversible_command (_("copy region(s)"));
2729 Editor::start_region_brush_grab (ArdourCanvas::Item* item, GdkEvent* event)
2731 if (selection->audio_regions.empty() || clicked_regionview == 0) {
2735 drag_info.copy = false;
2736 drag_info.item = item;
2737 drag_info.data = clicked_regionview;
2738 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2739 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2744 TimeAxisView* tvp = clicked_trackview;
2745 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
2747 if (tv && tv->is_audio_track()) {
2748 speed = tv->get_diskstream()->speed();
2751 drag_info.last_frame_position = (jack_nframes_t) (clicked_regionview->region.position() / speed);
2752 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2753 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2754 // we want a move threshold
2755 drag_info.want_move_threshold = true;
2756 drag_info.brushing = true;
2758 begin_reversible_command (_("Drag region brush"));
2762 Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2766 AudioRegionView *rv = reinterpret_cast<AudioRegionView*> (drag_info.data);
2767 jack_nframes_t pending_region_position = 0;
2768 int32_t pointer_y_span = 0, canvas_pointer_y_span = 0, original_pointer_order;
2769 int32_t visible_y_high = 0, visible_y_low = 512; //high meaning higher numbered.. not the height on the screen
2770 bool clamp_y_axis = false;
2771 vector<int32_t> height_list(512) ;
2772 vector<int32_t>::iterator j;
2774 /* Which trackview is this ? */
2776 TimeAxisView* tvp = trackview_by_y_position (drag_info.current_pointer_y);
2777 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
2779 /* The region motion is only processed if the pointer is over
2783 if (!tv || !tv->is_audio_track()) {
2784 /* To make sure we hide the verbose canvas cursor when the mouse is
2785 not held over and audiotrack.
2787 hide_verbose_canvas_cursor ();
2791 original_pointer_order = drag_info.last_trackview->order;
2793 /************************************************************
2795 ************************************************************/
2797 if (drag_info.brushing) {
2798 clamp_y_axis = true;
2803 if ((pointer_y_span = (drag_info.last_trackview->order - tv->order)) != 0) {
2805 int32_t children = 0, numtracks = 0;
2806 // XXX hard coding track limit, oh my, so very very bad
2807 bitset <1024> tracks (0x00);
2808 /* get a bitmask representing the visible tracks */
2810 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2811 TimeAxisView *tracklist_timeview;
2812 tracklist_timeview = (*i);
2813 AudioTimeAxisView* atv2 = dynamic_cast<AudioTimeAxisView*>(tracklist_timeview);
2814 list<TimeAxisView*> children_list;
2816 /* zeroes are audio tracks. ones are other types. */
2818 if (!atv2->hidden()) {
2820 if (visible_y_high < atv2->order) {
2821 visible_y_high = atv2->order;
2823 if (visible_y_low > atv2->order) {
2824 visible_y_low = atv2->order;
2827 if (!atv2->is_audio_track()) {
2828 tracks = tracks |= (0x01 << atv2->order);
2831 height_list[atv2->order] = (*i)->height;
2833 if ((children_list = atv2->get_child_list()).size() > 0) {
2834 for (list<TimeAxisView*>::iterator j = children_list.begin(); j != children_list.end(); ++j) {
2835 tracks = tracks |= (0x01 << (atv2->order + children));
2836 height_list[atv2->order + children] = (*j)->height;
2844 /* find the actual span according to the canvas */
2846 canvas_pointer_y_span = pointer_y_span;
2847 if (drag_info.last_trackview->order >= tv->order) {
2849 for (y = tv->order; y < drag_info.last_trackview->order; y++) {
2850 if (height_list[y] == 0 ) {
2851 canvas_pointer_y_span--;
2856 for (y = drag_info.last_trackview->order;y <= tv->order; y++) {
2857 if ( height_list[y] == 0 ) {
2858 canvas_pointer_y_span++;
2863 for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin(); i != selection->audio_regions.by_layer().end(); ++i) {
2864 AudioRegionView* rv2;
2866 double ix1, ix2, iy1, iy2;
2869 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
2870 rv2->get_canvas_group()->i2w (ix1, iy1);
2871 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
2872 AudioTimeAxisView* atv2 = dynamic_cast<AudioTimeAxisView*>(tvp2);
2874 if (atv2->order != original_pointer_order) {
2875 /* this isn't the pointer track */
2877 if (canvas_pointer_y_span > 0) {
2879 /* moving up the canvas */
2880 if ((atv2->order - canvas_pointer_y_span) >= visible_y_low) {
2882 int32_t visible_tracks = 0;
2883 while (visible_tracks < canvas_pointer_y_span ) {
2886 while (height_list[atv2->order - (visible_tracks - n)] == 0) {
2887 /* we're passing through a hidden track */
2892 if (tracks[atv2->order - (canvas_pointer_y_span - n)] != 0x00) {
2893 clamp_y_axis = true;
2897 clamp_y_axis = true;
2900 } else if (canvas_pointer_y_span < 0) {
2902 /*moving down the canvas*/
2904 if ((atv2->order - (canvas_pointer_y_span - n)) <= visible_y_high) { // we will overflow
2907 int32_t visible_tracks = 0;
2909 while (visible_tracks > canvas_pointer_y_span ) {
2912 while (height_list[atv2->order - (visible_tracks - n)] == 0) {
2916 if ( tracks[atv2->order - ( canvas_pointer_y_span - n)] != 0x00) {
2917 clamp_y_axis = true;
2922 clamp_y_axis = true;
2928 /* this is the pointer's track */
2929 if ((atv2->order - pointer_y_span) > visible_y_high) { // we will overflow
2930 clamp_y_axis = true;
2931 } else if ((atv2->order - pointer_y_span) < visible_y_low) { // we will underflow
2932 clamp_y_axis = true;
2940 } else if (drag_info.last_trackview == tv) {
2941 clamp_y_axis = true;
2945 if (!clamp_y_axis) {
2946 drag_info.last_trackview = tv;
2949 /************************************************************
2951 ************************************************************/
2953 /* compute the amount of pointer motion in frames, and where
2954 the region would be if we moved it by that much.
2957 if (drag_info.move_threshold_passsed) {
2959 if ((int32_t)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2961 jack_nframes_t sync_frame;
2962 jack_nframes_t sync_offset;
2965 pending_region_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2967 sync_offset = rv->region.sync_offset (sync_dir);
2968 sync_frame = rv->region.adjust_to_sync (pending_region_position);
2970 /* we snap if the snap modifier is not enabled.
2973 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2974 snap_to (sync_frame);
2977 if (sync_frame - sync_offset <= sync_frame) {
2978 pending_region_position = sync_frame - (sync_dir*sync_offset);
2980 pending_region_position = 0;
2984 pending_region_position = 0;
2987 if (pending_region_position > max_frames - rv->region.length()) {
2988 pending_region_position = drag_info.last_frame_position;
2991 // printf ("3: pending_region_position= %lu %lu\n", pending_region_position, drag_info.last_frame_position );
2993 if (pending_region_position != drag_info.last_frame_position && !drag_info.x_constrained) {
2995 /* now compute the canvas unit distance we need to move the regiondrag_info.last_trackview->order
2996 to make it appear at the new location.
2999 if (pending_region_position > drag_info.last_frame_position) {
3000 x_delta = ((double) (pending_region_position - drag_info.last_frame_position) / frames_per_unit);
3002 x_delta = -((double) (drag_info.last_frame_position - pending_region_position) / frames_per_unit);
3005 drag_info.last_frame_position = pending_region_position;
3012 /* threshold not passed */
3017 /*************************************************************
3019 ************************************************************/
3021 if (x_delta == 0 && (pointer_y_span == 0)) {
3022 /* haven't reached next snap point, and we're not switching
3023 trackviews. nothing to do.
3029 for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin(); i != selection->audio_regions.by_layer().end(); ++i) {
3031 AudioRegionView* rv2;
3034 /* if any regionview is at zero, we need to know so we can
3035 stop further leftward motion.
3038 double ix1, ix2, iy1, iy2;
3039 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3040 rv2->get_canvas_group()->i2w (ix1, iy1);
3049 /*************************************************************
3051 ************************************************************/
3053 pair<set<Playlist*>::iterator,bool> insert_result;
3054 const list<AudioRegionView*>& layered_regions = selection->audio_regions.by_layer();
3056 for (list<AudioRegionView*>::const_iterator i = layered_regions.begin(); i != layered_regions.end(); ++i) {
3058 AudioRegionView* rv;
3060 double ix1, ix2, iy1, iy2;
3061 int32_t temp_pointer_y_span = pointer_y_span;
3063 /* get item BBox, which will be relative to parent. so we have
3064 to query on a child, then convert to world coordinates using
3068 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3069 rv->get_canvas_group()->i2w (ix1, iy1);
3070 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3071 AudioTimeAxisView* canvas_atv = dynamic_cast<AudioTimeAxisView*>(tvp2);
3072 AudioTimeAxisView* temp_atv;
3074 if ((pointer_y_span != 0) && !clamp_y_axis) {
3077 for (j = height_list.begin(); j!= height_list.end(); j++) {
3078 if (x == canvas_atv->order) {
3079 /* we found the track the region is on */
3080 if (x != original_pointer_order) {
3081 /*this isn't from the same track we're dragging from */
3082 temp_pointer_y_span = canvas_pointer_y_span;
3084 while (temp_pointer_y_span > 0) {
3085 /* we're moving up canvas-wise,
3086 so we need to find the next track height
3088 if (j != height_list.begin()) {
3091 if (x != original_pointer_order) {
3092 /* we're not from the dragged track, so ignore hidden tracks. */
3094 temp_pointer_y_span++;
3098 temp_pointer_y_span--;
3100 while (temp_pointer_y_span < 0) {
3102 if (x != original_pointer_order) {
3104 temp_pointer_y_span--;
3108 if (j != height_list.end()) {
3111 temp_pointer_y_span++;
3113 /* find out where we'll be when we move and set height accordingly */
3115 tvp2 = trackview_by_y_position (iy1 + y_delta);
3116 temp_atv = dynamic_cast<AudioTimeAxisView*>(tvp2);
3117 rv->set_height (temp_atv->height);
3119 /* if you un-comment the following, the region colours will follow the track colours whilst dragging,
3120 personally, i think this can confuse things, but never mind.
3123 //const GdkColor& col (temp_atv->view->get_region_color());
3124 //rv->set_color (const_cast<GdkColor&>(col));
3131 /* prevent the regionview from being moved to before
3132 the zero position on the canvas.
3137 if (-x_delta > ix1) {
3140 } else if ((x_delta > 0) &&(rv->region.last_frame() > max_frames - x_delta)) {
3141 x_delta = max_frames - rv->region.last_frame();
3144 if (drag_info.first_move) {
3146 /* hide any dependent views */
3148 // rv->get_time_axis_view().hide_dependent_views (*rv);
3150 /* this is subtle. raising the regionview itself won't help,
3151 because raise_to_top() just puts the item on the top of
3152 its parent's stack. so, we need to put the trackview canvas_display group
3153 on the top, since its parent is the whole canvas.
3156 rv->get_canvas_group()->raise_to_top();
3157 rv->get_time_axis_view().canvas_display->raise_to_top();
3158 cursor_group->raise_to_top();
3160 /* freeze the playlists from notifying till
3164 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*> (&rv->get_time_axis_view());
3165 if (atv && atv->is_audio_track()) {
3166 AudioPlaylist* pl = atv->get_diskstream()->playlist();
3168 /* only freeze and capture state once */
3170 insert_result = motion_frozen_playlists.insert (pl);
3171 if (insert_result.second) {
3173 session->add_undo(pl->get_memento());
3179 if (drag_info.brushing) {
3180 mouse_brush_insert_region (rv, pending_region_position);
3182 rv->move (x_delta, y_delta);
3186 if (drag_info.first_move) {
3187 cursor_group->raise_to_top();
3190 drag_info.first_move = false;
3192 if (x_delta != 0 && !drag_info.brushing) {
3193 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3199 Editor::region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3201 jack_nframes_t where;
3202 AudioRegionView* rv = reinterpret_cast<AudioRegionView *> (drag_info.data);
3203 pair<set<Playlist*>::iterator,bool> insert_result;
3204 bool nocommit = true;
3206 AudioTimeAxisView* atv;
3207 bool regionview_y_movement;
3208 bool regionview_x_movement;
3210 /* first_move is set to false if the regionview has been moved in the
3214 if (drag_info.first_move) {
3221 /* The regionview has been moved at some stage during the grab so we need
3222 to account for any mouse movement between this event and the last one.
3225 region_drag_motion_callback (item, event);
3227 if (drag_info.brushing) {
3228 /* all changes were made during motion event handlers */
3232 /* adjust for track speed */
3235 atv = dynamic_cast<AudioTimeAxisView*> (drag_info.last_trackview);
3236 if (atv && atv->get_diskstream()) {
3237 speed = atv->get_diskstream()->speed();
3240 regionview_x_movement = (drag_info.last_frame_position != (jack_nframes_t) (rv->region.position()/speed));
3241 regionview_y_movement = (drag_info.last_trackview != &rv->get_time_axis_view());
3243 //printf ("last_frame: %s position is %lu %g\n", rv->get_time_axis_view().name().c_str(), drag_info.last_frame_position, speed);
3244 //printf ("last_rackview: %s \n", drag_info.last_trackview->name().c_str());
3246 if (regionview_y_movement) {
3248 /* motion between tracks */
3250 list<AudioRegionView*> new_selection;
3252 /* moved to a different audio track. */
3254 for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin(); i != selection->audio_regions.by_layer().end(); ) {
3256 AudioRegionView* rv2 = (*i);
3258 /* the region that used to be in the old playlist is not
3259 moved to the new one - we make a copy of it. as a result,
3260 any existing editor for the region should no longer be
3264 if (!drag_info.copy) {
3265 rv2->hide_region_editor();
3267 new_selection.push_back (rv2);
3271 /* first, freeze the target tracks */
3273 for (list<AudioRegionView*>::const_iterator i = new_selection.begin(); i != new_selection.end();i++ ) {
3275 Playlist* from_playlist;
3276 Playlist* to_playlist;
3278 double ix1, ix2, iy1, iy2;
3280 (*i)->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3281 (*i)->get_canvas_group()->i2w (ix1, iy1);
3282 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3283 AudioTimeAxisView* atv2 = dynamic_cast<AudioTimeAxisView*>(tvp2);
3285 from_playlist = (*i)->region.playlist();
3286 to_playlist = atv2->playlist();
3288 /* the from_playlist was frozen in the "first_move" case
3289 of the motion handler. the insert can fail,
3290 but that doesn't matter. it just means
3291 we already have the playlist in the list.
3294 motion_frozen_playlists.insert (from_playlist);
3296 /* only freeze the to_playlist once */
3298 insert_result = motion_frozen_playlists.insert(to_playlist);
3299 if (insert_result.second) {
3300 to_playlist->freeze();
3301 session->add_undo(to_playlist->get_memento());
3306 /* now do it again with the actual operations */
3308 for (list<AudioRegionView*>::const_iterator i = new_selection.begin(); i != new_selection.end();i++ ) {
3310 Playlist* from_playlist;
3311 Playlist* to_playlist;
3313 double ix1, ix2, iy1, iy2;
3315 (*i)->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3316 (*i)->get_canvas_group()->i2w (ix1, iy1);
3317 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3318 AudioTimeAxisView* atv2 = dynamic_cast<AudioTimeAxisView*>(tvp2);
3320 from_playlist = (*i)->region.playlist();
3321 to_playlist = atv2->playlist();
3323 latest_regionview = 0;
3325 where = (jack_nframes_t) (unit_to_frame (ix1) * speed);
3326 Region* new_region = createRegion ((*i)->region);
3328 from_playlist->remove_region (&((*i)->region));
3330 sigc::connection c = atv2->view->AudioRegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3331 to_playlist->add_region (*new_region, where);
3334 if (latest_regionview) {
3335 selection->add (latest_regionview);
3341 /* motion within a single track */
3343 for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin(); i != selection->audio_regions.by_layer().end(); ++i) {
3347 if (rv->region.locked()) {
3351 if (regionview_x_movement) {
3352 double ownspeed = 1.0;
3353 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*> (&(rv->get_time_axis_view()));
3355 if (atv && atv->get_diskstream()) {
3356 ownspeed = atv->get_diskstream()->speed();
3359 /* base the new region position on the current position of the regionview.*/
3361 double ix1, ix2, iy1, iy2;
3363 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3364 rv->get_canvas_group()->i2w (ix1, iy1);
3365 where = (jack_nframes_t) (unit_to_frame (ix1) * ownspeed);
3369 where = rv->region.position();
3372 rv->get_time_axis_view().reveal_dependent_views (*rv);
3374 /* no need to add an undo here, we did that when we added this playlist to motion_frozen playlists */
3376 rv->region.set_position (where, (void *) this);
3381 for (set<Playlist*>::iterator p = motion_frozen_playlists.begin(); p != motion_frozen_playlists.end(); ++p) {
3383 session->add_redo_no_execute ((*p)->get_memento());
3386 motion_frozen_playlists.clear ();
3389 commit_reversible_command ();
3394 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
3396 /* Either add to or set the set the region selection, unless
3397 this is an alignment click (control used)
3400 if (Keyboard::modifier_state_contains (event->state, Keyboard::Control)) {
3401 TimeAxisView* tv = &rv.get_time_axis_view();
3402 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*>(tv);
3404 if (atv && atv->is_audio_track()) {
3405 speed = atv->get_diskstream()->speed();
3408 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Alt))) {
3410 align_region (rv.region, SyncPoint, (jack_nframes_t) (edit_cursor->current_frame * speed));
3412 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Shift))) {
3414 align_region (rv.region, End, (jack_nframes_t) (edit_cursor->current_frame * speed));
3418 align_region (rv.region, Start, (jack_nframes_t) (edit_cursor->current_frame * speed));
3424 Editor::show_verbose_time_cursor (jack_nframes_t frame, double offset, double xpos, double ypos)
3435 switch (ARDOUR_UI::instance()->secondary_clock.mode ()) {
3436 case AudioClock::BBT:
3437 session->bbt_time (frame, bbt);
3438 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, bbt.bars, bbt.beats, bbt.ticks);
3441 case AudioClock::SMPTE:
3442 session->smpte_time (frame, smpte);
3443 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
3446 case AudioClock::MinSec:
3447 /* XXX fix this to compute min/sec properly */
3448 session->smpte_time (frame, smpte);
3449 secs = smpte.seconds + ((float) smpte.frames / session->smpte_frames_per_second);
3450 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", smpte.hours, smpte.minutes, secs);
3454 snprintf (buf, sizeof(buf), "%u", frame);
3458 if (xpos >= 0 && ypos >=0) {
3459 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
3462 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
3464 show_verbose_canvas_cursor ();
3468 Editor::show_verbose_duration_cursor (jack_nframes_t start, jack_nframes_t end, double offset, double xpos, double ypos)
3475 Meter meter_at_start(session->tempo_map().meter_at(start));
3481 switch (ARDOUR_UI::instance()->secondary_clock.mode ()) {
3482 case AudioClock::BBT:
3483 session->bbt_time (start, sbbt);
3484 session->bbt_time (end, ebbt);
3487 /* XXX this computation won't work well if the
3488 user makes a selection that spans any meter changes.
3491 ebbt.bars -= sbbt.bars;
3492 if (ebbt.beats >= sbbt.beats) {
3493 ebbt.beats -= sbbt.beats;
3496 ebbt.beats = int(meter_at_start.beats_per_bar()) + ebbt.beats - sbbt.beats;
3498 if (ebbt.ticks >= sbbt.ticks) {
3499 ebbt.ticks -= sbbt.ticks;
3502 ebbt.ticks = int(Meter::ticks_per_beat) + ebbt.ticks - sbbt.ticks;
3505 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, ebbt.bars, ebbt.beats, ebbt.ticks);
3508 case AudioClock::SMPTE:
3509 session->smpte_duration (end - start, smpte);
3510 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
3513 case AudioClock::MinSec:
3514 /* XXX fix this to compute min/sec properly */
3515 session->smpte_duration (end - start, smpte);
3516 secs = smpte.seconds + ((float) smpte.frames / session->smpte_frames_per_second);
3517 snprintf (buf, sizeof (buf), "%02ld:%02ld:%.4f", smpte.hours, smpte.minutes, secs);
3521 snprintf (buf, sizeof(buf), "%u", end - start);
3525 if (xpos >= 0 && ypos >=0) {
3526 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
3529 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
3531 show_verbose_canvas_cursor ();
3535 Editor::collect_new_region_view (AudioRegionView* rv)
3537 latest_regionview = rv;
3541 Editor::start_selection_grab (ArdourCanvas::Item* item, GdkEvent* event)
3543 if (clicked_regionview == 0) {
3547 /* lets try to create new Region for the selection */
3549 vector<AudioRegion*> new_regions;
3550 create_region_from_selection (new_regions);
3552 if (new_regions.empty()) {
3556 /* XXX fix me one day to use all new regions */
3558 Region* region = new_regions.front();
3560 /* add it to the current stream/playlist.
3562 tricky: the streamview for the track will add a new regionview. we will
3563 catch the signal it sends when it creates the regionview to
3564 set the regionview we want to then drag.
3567 latest_regionview = 0;
3568 sigc::connection c = clicked_audio_trackview->view->AudioRegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3570 /* A selection grab currently creates two undo/redo operations, one for
3571 creating the new region and another for moving it.
3574 begin_reversible_command (_("selection grab"));
3576 Playlist* playlist = clicked_trackview->playlist();
3578 session->add_undo (playlist->get_memento ());
3579 clicked_trackview->playlist()->add_region (*region, selection->time[clicked_selection].start);
3580 session->add_redo_no_execute (playlist->get_memento ());
3582 commit_reversible_command ();
3586 if (latest_regionview == 0) {
3587 /* something went wrong */
3591 /* we need to deselect all other regionviews, and select this one
3592 i'm ignoring undo stuff, because the region creation will take care of it */
3593 selection->set (latest_regionview);
3595 drag_info.item = latest_regionview->get_canvas_group();
3596 drag_info.data = latest_regionview;
3597 drag_info.motion_callback = &Editor::region_drag_motion_callback;
3598 drag_info.finished_callback = &Editor::region_drag_finished_callback;
3602 drag_info.last_trackview = clicked_trackview;
3603 drag_info.last_frame_position = latest_regionview->region.position();
3604 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
3606 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3610 Editor::cancel_selection ()
3612 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
3613 (*i)->hide_selection ();
3615 selection->clear ();
3616 clicked_selection = 0;
3620 Editor::start_selection_op (ArdourCanvas::Item* item, GdkEvent* event, SelectionOp op)
3622 jack_nframes_t start = 0;
3623 jack_nframes_t end = 0;
3629 drag_info.item = item;
3630 drag_info.motion_callback = &Editor::drag_selection;
3631 drag_info.finished_callback = &Editor::end_selection_op;
3636 case CreateSelection:
3638 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
3639 drag_info.copy = true;
3641 drag_info.copy = false;
3643 start_grab (event, selector_cursor);
3646 case SelectionStartTrim:
3647 clicked_trackview->order_selection_trims (item, true);
3648 start_grab (event, trimmer_cursor);
3649 start = selection->time[clicked_selection].start;
3650 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
3653 case SelectionEndTrim:
3654 clicked_trackview->order_selection_trims (item, false);
3655 start_grab (event, trimmer_cursor);
3656 end = selection->time[clicked_selection].end;
3657 drag_info.pointer_frame_offset = drag_info.grab_frame - end;
3661 start = selection->time[clicked_selection].start;
3663 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
3667 if (selection_op == SelectionMove) {
3668 show_verbose_time_cursor(start, 10);
3670 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
3675 Editor::drag_selection (ArdourCanvas::Item* item, GdkEvent* event)
3677 jack_nframes_t start = 0;
3678 jack_nframes_t end = 0;
3679 jack_nframes_t length;
3680 jack_nframes_t pending_position;
3682 if ((int32_t) drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
3683 pending_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
3686 pending_position = 0;
3689 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3690 snap_to (pending_position);
3693 /* only alter selection if the current frame is
3694 different from the last frame position (adjusted)
3697 if (pending_position == drag_info.last_pointer_frame) return;
3699 switch (selection_op) {
3700 case CreateSelection:
3702 if (drag_info.first_move) {
3703 snap_to (drag_info.grab_frame);
3706 if (pending_position < drag_info.grab_frame) {
3707 start = pending_position;
3708 end = drag_info.grab_frame;
3710 end = pending_position;
3711 start = drag_info.grab_frame;
3714 /* first drag: Either add to the selection
3715 or create a new selection->
3718 if (drag_info.first_move) {
3720 begin_reversible_command (_("range selection"));
3722 if (drag_info.copy) {
3723 /* adding to the selection */
3724 clicked_selection = selection->add (start, end);
3725 drag_info.copy = false;
3727 /* new selection-> */
3728 clicked_selection = selection->set (clicked_trackview, start, end);
3733 case SelectionStartTrim:
3735 if (drag_info.first_move) {
3736 begin_reversible_command (_("trim selection start"));
3739 start = selection->time[clicked_selection].start;
3740 end = selection->time[clicked_selection].end;
3742 if (pending_position > end) {
3745 start = pending_position;
3749 case SelectionEndTrim:
3751 if (drag_info.first_move) {
3752 begin_reversible_command (_("trim selection end"));
3755 start = selection->time[clicked_selection].start;
3756 end = selection->time[clicked_selection].end;
3758 if (pending_position < start) {
3761 end = pending_position;
3768 if (drag_info.first_move) {
3769 begin_reversible_command (_("move selection"));
3772 start = selection->time[clicked_selection].start;
3773 end = selection->time[clicked_selection].end;
3775 length = end - start;
3777 start = pending_position;
3780 end = start + length;
3785 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
3786 start_canvas_autoscroll (1);
3790 selection->replace (clicked_selection, start, end);
3793 drag_info.last_pointer_frame = pending_position;
3794 drag_info.first_move = false;
3796 if (selection_op == SelectionMove) {
3797 show_verbose_time_cursor(start, 10);
3799 show_verbose_time_cursor(pending_position, 10);
3804 Editor::end_selection_op (ArdourCanvas::Item* item, GdkEvent* event)
3806 if (!drag_info.first_move) {
3807 drag_selection (item, event);
3808 /* XXX this is not object-oriented programming at all. ick */
3809 if (selection->time.consolidate()) {
3810 selection->TimeChanged ();
3812 commit_reversible_command ();
3814 /* just a click, no pointer movement.*/
3816 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
3818 selection->clear_time();
3823 /* XXX what happens if its a music selection? */
3824 session->set_audio_range (selection->time);
3825 stop_canvas_autoscroll ();
3829 Editor::start_trim (ArdourCanvas::Item* item, GdkEvent* event)
3832 TimeAxisView* tvp = clicked_trackview;
3833 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
3835 if (tv && tv->is_audio_track()) {
3836 speed = tv->get_diskstream()->speed();
3839 jack_nframes_t region_start = (jack_nframes_t) (clicked_regionview->region.position() / speed);
3840 jack_nframes_t region_end = (jack_nframes_t) (clicked_regionview->region.last_frame() / speed);
3841 jack_nframes_t region_length = (jack_nframes_t) (clicked_regionview->region.length() / speed);
3843 motion_frozen_playlists.clear();
3845 //drag_info.item = clicked_regionview->get_name_highlight();
3846 drag_info.item = item;
3847 drag_info.motion_callback = &Editor::trim_motion_callback;
3848 drag_info.finished_callback = &Editor::trim_finished_callback;
3850 start_grab (event, trimmer_cursor);
3852 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
3853 trim_op = ContentsTrim;
3855 /* These will get overridden for a point trim.*/
3856 if (drag_info.current_pointer_frame < (region_start + region_length/2)) {
3857 /* closer to start */
3858 trim_op = StartTrim;
3859 } else if (drag_info.current_pointer_frame > (region_end - region_length/2)) {
3867 show_verbose_time_cursor(region_start, 10);
3870 show_verbose_time_cursor(region_end, 10);
3873 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
3879 Editor::trim_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
3881 AudioRegionView* rv = clicked_regionview;
3882 jack_nframes_t frame_delta = 0;
3883 bool left_direction;
3884 bool obey_snap = !Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier());
3886 /* snap modifier works differently here..
3887 its' current state has to be passed to the
3888 various trim functions in order to work properly
3892 TimeAxisView* tvp = clicked_trackview;
3893 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
3894 pair<set<Playlist*>::iterator,bool> insert_result;
3896 if (tv && tv->is_audio_track()) {
3897 speed = tv->get_diskstream()->speed();
3900 if (drag_info.last_pointer_frame > drag_info.current_pointer_frame) {
3901 left_direction = true;
3903 left_direction = false;
3907 snap_to (drag_info.current_pointer_frame);
3910 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
3914 if (drag_info.first_move) {
3920 trim_type = "Region start trim";
3923 trim_type = "Region end trim";
3926 trim_type = "Region content trim";
3930 begin_reversible_command (trim_type);
3932 for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin(); i != selection->audio_regions.by_layer().end(); ++i) {
3933 (*i)->region.freeze ();
3934 (*i)->temporarily_hide_envelope ();
3936 Playlist * pl = (*i)->region.playlist();
3937 insert_result = motion_frozen_playlists.insert (pl);
3938 if (insert_result.second) {
3939 session->add_undo (pl->get_memento());
3944 if (left_direction) {
3945 frame_delta = (drag_info.last_pointer_frame - drag_info.current_pointer_frame);
3947 frame_delta = (drag_info.current_pointer_frame - drag_info.last_pointer_frame);
3952 if ((left_direction == false) && (drag_info.current_pointer_frame <= rv->region.first_frame()/speed)) {
3955 for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin(); i != selection->audio_regions.by_layer().end(); ++i) {
3956 single_start_trim (**i, frame_delta, left_direction, obey_snap);
3962 if ((left_direction == true) && (drag_info.current_pointer_frame > (jack_nframes_t) (rv->region.last_frame()/speed))) {
3965 for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin(); i != selection->audio_regions.by_layer().end(); ++i) {
3966 single_end_trim (**i, frame_delta, left_direction, obey_snap);
3973 bool swap_direction = false;
3975 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
3976 swap_direction = true;
3979 for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin();
3980 i != selection->audio_regions.by_layer().end(); ++i)
3982 single_contents_trim (**i, frame_delta, left_direction, swap_direction, obey_snap);
3990 show_verbose_time_cursor((jack_nframes_t) (rv->region.position()/speed), 10);
3993 show_verbose_time_cursor((jack_nframes_t) (rv->region.last_frame()/speed), 10);
3996 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4000 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4001 drag_info.first_move = false;
4005 Editor::single_contents_trim (AudioRegionView& rv, jack_nframes_t frame_delta, bool left_direction, bool swap_direction, bool obey_snap)
4007 Region& region (rv.region);
4009 if (region.locked()) {
4013 jack_nframes_t new_bound;
4016 TimeAxisView* tvp = clicked_trackview;
4017 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
4019 if (tv && tv->is_audio_track()) {
4020 speed = tv->get_diskstream()->speed();
4023 if (left_direction) {
4024 if (swap_direction) {
4025 new_bound = (jack_nframes_t) (region.position()/speed) + frame_delta;
4027 new_bound = (jack_nframes_t) (region.position()/speed) - frame_delta;
4030 if (swap_direction) {
4031 new_bound = (jack_nframes_t) (region.position()/speed) - frame_delta;
4033 new_bound = (jack_nframes_t) (region.position()/speed) + frame_delta;
4038 snap_to (new_bound);
4040 region.trim_start ((jack_nframes_t) (new_bound * speed), this);
4041 rv.region_changed (StartChanged);
4045 Editor::single_start_trim (AudioRegionView& rv, jack_nframes_t frame_delta, bool left_direction, bool obey_snap)
4047 Region& region (rv.region);
4049 if (region.locked()) {
4053 jack_nframes_t new_bound;
4056 TimeAxisView* tvp = clicked_trackview;
4057 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
4059 if (tv && tv->is_audio_track()) {
4060 speed = tv->get_diskstream()->speed();
4063 if (left_direction) {
4064 new_bound = (jack_nframes_t) (region.position()/speed) - frame_delta;
4066 new_bound = (jack_nframes_t) (region.position()/speed) + frame_delta;
4070 snap_to (new_bound, (left_direction ? 0 : 1));
4073 region.trim_front ((jack_nframes_t) (new_bound * speed), this);
4075 rv.region_changed (Change (LengthChanged|PositionChanged|StartChanged));
4079 Editor::single_end_trim (AudioRegionView& rv, jack_nframes_t frame_delta, bool left_direction, bool obey_snap)
4081 Region& region (rv.region);
4083 if (region.locked()) {
4087 jack_nframes_t new_bound;
4090 TimeAxisView* tvp = clicked_trackview;
4091 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
4093 if (tv && tv->is_audio_track()) {
4094 speed = tv->get_diskstream()->speed();
4097 if (left_direction) {
4098 new_bound = (jack_nframes_t) ((region.last_frame() + 1)/speed) - frame_delta;
4100 new_bound = (jack_nframes_t) ((region.last_frame() + 1)/speed) + frame_delta;
4104 snap_to (new_bound);
4106 region.trim_end ((jack_nframes_t) (new_bound * speed), this);
4107 rv.region_changed (LengthChanged);
4111 Editor::trim_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
4113 if (!drag_info.first_move) {
4114 trim_motion_callback (item, event);
4116 if (!clicked_regionview->get_selected()) {
4117 thaw_region_after_trim (*clicked_regionview);
4120 for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin();
4121 i != selection->audio_regions.by_layer().end(); ++i)
4123 thaw_region_after_trim (**i);
4127 for (set<Playlist*>::iterator p = motion_frozen_playlists.begin(); p != motion_frozen_playlists.end(); ++p) {
4129 session->add_redo_no_execute ((*p)->get_memento());
4132 motion_frozen_playlists.clear ();
4134 commit_reversible_command();
4136 /* no mouse movement */
4142 Editor::point_trim (GdkEvent* event)
4144 AudioRegionView* rv = clicked_regionview;
4145 jack_nframes_t new_bound = drag_info.current_pointer_frame;
4147 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4148 snap_to (new_bound);
4151 /* Choose action dependant on which button was pressed */
4152 switch (event->button.button) {
4154 trim_op = StartTrim;
4155 begin_reversible_command (_("Start point trim"));
4157 if (rv->get_selected()) {
4159 for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin();
4160 i != selection->audio_regions.by_layer().end(); ++i)
4162 if (!(*i)->region.locked()) {
4163 session->add_undo ((*i)->region.playlist()->get_memento());
4164 (*i)->region.trim_front (new_bound, this);
4165 session->add_redo_no_execute ((*i)->region.playlist()->get_memento());
4171 if (!rv->region.locked()) {
4172 session->add_undo (rv->region.playlist()->get_memento());
4173 rv->region.trim_front (new_bound, this);
4174 session->add_redo_no_execute (rv->region.playlist()->get_memento());
4178 commit_reversible_command();
4183 begin_reversible_command (_("End point trim"));
4185 if (rv->get_selected()) {
4187 for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin(); i != selection->audio_regions.by_layer().end(); ++i)
4189 if (!(*i)->region.locked()) {
4190 session->add_undo ((*i)->region.playlist()->get_memento());
4191 (*i)->region.trim_end (new_bound, this);
4192 session->add_redo_no_execute ((*i)->region.playlist()->get_memento());
4198 if (!rv->region.locked()) {
4199 session->add_undo (rv->region.playlist()->get_memento());
4200 rv->region.trim_end (new_bound, this);
4201 session->add_redo_no_execute (rv->region.playlist()->get_memento());
4205 commit_reversible_command();
4214 Editor::thaw_region_after_trim (AudioRegionView& rv)
4216 Region& region (rv.region);
4218 if (region.locked()) {
4222 region.thaw (_("trimmed region"));
4223 session->add_redo_no_execute (region.playlist()->get_memento());
4225 rv.unhide_envelope ();
4229 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* event)
4234 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
4235 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
4239 Location* location = find_location_from_marker (marker, is_start);
4240 location->set_hidden (true, this);
4245 Editor::start_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event, RangeMarkerOp op)
4252 drag_info.item = item;
4253 drag_info.motion_callback = &Editor::drag_range_markerbar_op;
4254 drag_info.finished_callback = &Editor::end_range_markerbar_op;
4256 range_marker_op = op;
4258 if (!temp_location) {
4259 temp_location = new Location;
4263 case CreateRangeMarker:
4264 case CreateTransportMarker:
4266 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
4267 drag_info.copy = true;
4269 drag_info.copy = false;
4271 start_grab (event, selector_cursor);
4275 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4280 Editor::drag_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4282 jack_nframes_t start = 0;
4283 jack_nframes_t end = 0;
4284 ArdourCanvas::SimpleRect *crect = (range_marker_op == CreateRangeMarker) ? range_bar_drag_rect: transport_bar_drag_rect;
4286 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4287 snap_to (drag_info.current_pointer_frame);
4290 /* only alter selection if the current frame is
4291 different from the last frame position.
4294 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4296 switch (range_marker_op) {
4297 case CreateRangeMarker:
4298 case CreateTransportMarker:
4299 if (drag_info.first_move) {
4300 snap_to (drag_info.grab_frame);
4303 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4304 start = drag_info.current_pointer_frame;
4305 end = drag_info.grab_frame;
4307 end = drag_info.current_pointer_frame;
4308 start = drag_info.grab_frame;
4311 /* first drag: Either add to the selection
4312 or create a new selection->
4315 if (drag_info.first_move) {
4317 temp_location->set (start, end);
4321 update_marker_drag_item (temp_location);
4322 range_marker_drag_rect->show();
4323 range_marker_drag_rect->raise_to_top();
4329 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
4330 start_canvas_autoscroll (1);
4334 temp_location->set (start, end);
4336 double x1 = frame_to_pixel (start);
4337 double x2 = frame_to_pixel (end);
4338 crect->property_x1() = x1;
4339 crect->property_x2() = x2;
4341 update_marker_drag_item (temp_location);
4344 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4345 drag_info.first_move = false;
4347 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4352 Editor::end_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4354 Location * newloc = 0;
4356 if (!drag_info.first_move) {
4357 drag_range_markerbar_op (item, event);
4359 switch (range_marker_op) {
4360 case CreateRangeMarker:
4361 begin_reversible_command (_("new range marker"));
4362 session->add_undo (session->locations()->get_memento());
4363 newloc = new Location(temp_location->start(), temp_location->end(), "unnamed", Location::IsRangeMarker);
4364 session->locations()->add (newloc, true);
4365 session->add_redo_no_execute (session->locations()->get_memento());
4366 commit_reversible_command ();
4368 range_bar_drag_rect->hide();
4369 range_marker_drag_rect->hide();
4372 case CreateTransportMarker:
4373 // popup menu to pick loop or punch
4374 new_transport_marker_context_menu (&event->button, item);
4379 /* just a click, no pointer movement.*/
4381 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
4388 stop_canvas_autoscroll ();
4394 Editor::start_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4396 drag_info.item = item;
4397 drag_info.motion_callback = &Editor::drag_mouse_zoom;
4398 drag_info.finished_callback = &Editor::end_mouse_zoom;
4400 start_grab (event, zoom_cursor);
4402 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4406 Editor::drag_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4408 jack_nframes_t start;
4411 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4412 snap_to (drag_info.current_pointer_frame);
4414 if (drag_info.first_move) {
4415 snap_to (drag_info.grab_frame);
4419 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4421 /* base start and end on initial click position */
4422 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4423 start = drag_info.current_pointer_frame;
4424 end = drag_info.grab_frame;
4426 end = drag_info.current_pointer_frame;
4427 start = drag_info.grab_frame;
4432 if (drag_info.first_move) {
4434 zoom_rect->raise_to_top();
4437 reposition_zoom_rect(start, end);
4439 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4440 drag_info.first_move = false;
4442 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4447 Editor::end_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4449 if (!drag_info.first_move) {
4450 drag_mouse_zoom (item, event);
4452 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
4453 temporal_zoom_by_frame (drag_info.grab_frame, drag_info.last_pointer_frame, "mouse zoom");
4455 temporal_zoom_by_frame (drag_info.last_pointer_frame, drag_info.grab_frame, "mouse zoom");
4458 temporal_zoom_to_frame (false, drag_info.grab_frame);
4460 temporal_zoom_step (false);
4461 center_screen (drag_info.grab_frame);
4469 Editor::reposition_zoom_rect (jack_nframes_t start, jack_nframes_t end)
4471 double x1 = frame_to_pixel (start);
4472 double x2 = frame_to_pixel (end);
4473 double y2 = canvas_height - 2;
4475 zoom_rect->property_x1() = x1;
4476 zoom_rect->property_y1() = 1.0;
4477 zoom_rect->property_x2() = x2;
4478 zoom_rect->property_y2() = y2;
4482 Editor::start_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4484 drag_info.item = item;
4485 drag_info.motion_callback = &Editor::drag_rubberband_select;
4486 drag_info.finished_callback = &Editor::end_rubberband_select;
4488 start_grab (event, cross_hair_cursor);
4490 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4494 Editor::drag_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4496 jack_nframes_t start;
4501 /* use a bigger drag threshold than the default */
4503 if (abs ((int) (drag_info.current_pointer_frame - drag_info.grab_frame)) < 8) {
4507 // if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4508 // snap_to (drag_info.current_pointer_frame);
4510 // if (drag_info.first_move) {
4511 // snap_to (drag_info.grab_frame);
4516 /* base start and end on initial click position */
4517 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4518 start = drag_info.current_pointer_frame;
4519 end = drag_info.grab_frame;
4521 end = drag_info.current_pointer_frame;
4522 start = drag_info.grab_frame;
4525 if (drag_info.current_pointer_y < drag_info.grab_y) {
4526 y1 = drag_info.current_pointer_y;
4527 y2 = drag_info.grab_y;
4530 y2 = drag_info.current_pointer_y;
4531 y1 = drag_info.grab_y;
4535 if (start != end || y1 != y2) {
4537 double x1 = frame_to_pixel (start);
4538 double x2 = frame_to_pixel (end);
4540 rubberband_rect->property_x1() = x1;
4541 rubberband_rect->property_y1() = y1;
4542 rubberband_rect->property_x2() = x2;
4543 rubberband_rect->property_y2() = y2;
4545 rubberband_rect->show();
4546 rubberband_rect->raise_to_top();
4548 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4549 drag_info.first_move = false;
4551 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4556 Editor::end_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4558 if (!drag_info.first_move) {
4560 drag_rubberband_select (item, event);
4563 if (drag_info.current_pointer_y < drag_info.grab_y) {
4564 y1 = drag_info.current_pointer_y;
4565 y2 = drag_info.grab_y;
4568 y2 = drag_info.current_pointer_y;
4569 y1 = drag_info.grab_y;
4573 Selection::Operation op = Keyboard::selection_type (event->button.state);
4576 begin_reversible_command (_("select regions"));
4578 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
4579 commit = select_all_within (drag_info.grab_frame, drag_info.last_pointer_frame, y1, y2, op);
4581 commit = select_all_within (drag_info.last_pointer_frame, drag_info.grab_frame, y1, y2, op);
4585 commit_reversible_command ();
4589 selection->clear_audio_regions();
4590 selection->clear_points ();
4591 selection->clear_lines ();
4594 rubberband_rect->hide();
4599 Editor::mouse_rename_region (ArdourCanvas::Item* item, GdkEvent* event)
4601 using namespace Gtkmm2ext;
4603 ArdourPrompter prompter (false);
4605 prompter.set_prompt (_("Name for region:"));
4606 prompter.set_initial_text (clicked_regionview->region.name());
4607 prompter.show_all ();
4608 switch (prompter.run ()) {
4609 case Gtk::RESPONSE_ACCEPT:
4611 prompter.get_result(str);
4613 clicked_regionview->region.set_name (str);
4621 Editor::start_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
4623 drag_info.item = item;
4624 drag_info.motion_callback = &Editor::time_fx_motion;
4625 drag_info.finished_callback = &Editor::end_time_fx;
4629 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4633 Editor::time_fx_motion (ArdourCanvas::Item *item, GdkEvent* event)
4635 AudioRegionView* rv = clicked_regionview;
4637 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4638 snap_to (drag_info.current_pointer_frame);
4641 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
4645 if (drag_info.current_pointer_frame > rv->region.position()) {
4646 rv->get_time_axis_view().show_timestretch (rv->region.position(), drag_info.current_pointer_frame);
4649 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4650 drag_info.first_move = false;
4652 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4656 Editor::end_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
4658 clicked_regionview->get_time_axis_view().hide_timestretch ();
4660 if (drag_info.first_move) {
4664 jack_nframes_t newlen = drag_info.last_pointer_frame - clicked_regionview->region.position();
4665 float percentage = (float) ((double) newlen - (double) clicked_regionview->region.length()) / ((double) newlen) * 100.0f;
4667 begin_reversible_command (_("timestretch"));
4669 if (run_timestretch (selection->audio_regions, percentage) == 0) {
4670 session->commit_reversible_command ();
4675 Editor::mouse_brush_insert_region (AudioRegionView* rv, jack_nframes_t pos)
4677 /* no brushing without a useful snap setting */
4679 switch (snap_mode) {
4681 return; /* can't work because it allows region to be placed anywhere */
4686 switch (snap_type) {
4689 case SnapToEditCursor:
4696 /* don't brush a copy over the original */
4698 if (pos == rv->region.position()) {
4702 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*>(&rv->get_time_axis_view());
4704 if (atv == 0 || !atv->is_audio_track()) {
4708 Playlist* playlist = atv->playlist();
4709 double speed = atv->get_diskstream()->speed();
4711 session->add_undo (playlist->get_memento());
4712 playlist->add_region (*(new AudioRegion (rv->region)), (jack_nframes_t) (pos * speed));
4713 session->add_redo_no_execute (playlist->get_memento());
4715 // playlist is frozen, so we have to update manually
4717 playlist->StateChanged (Change (~0)); /* EMIT SIGNAL */
4721 Editor::track_height_step_timeout ()
4724 struct timeval delta;
4726 gettimeofday (&now, 0);
4727 timersub (&now, &last_track_height_step_timestamp, &delta);
4729 if (delta.tv_sec * 1000000 + delta.tv_usec > 250000) { /* milliseconds */
4730 current_stepping_trackview = 0;