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);
338 case AutomationTrackItem:
346 #define SELECT_TRACK_FROM_CANVAS_IN_RANGE_MODE
347 #ifdef SELECT_TRACK_FROM_CANVAS_IN_RANGE_MODE
348 /* in range mode, button 1/2/3 press potentially selects a track */
350 if (mouse_mode == MouseRange &&
351 event->type == GDK_BUTTON_PRESS &&
352 event->button.button <= 3) {
359 case AutomationTrackItem:
360 set_selected_track_from_click (Keyboard::selection_type (event->button.state), true, true);
363 case AudioRegionViewNameHighlight:
364 case AudioRegionViewName:
365 rv = reinterpret_cast<AudioRegionView *> (item->get_data ("regionview"));
372 if (drag_info.item == 0 &&
373 (Keyboard::is_delete_event (&event->button) ||
374 Keyboard::is_context_menu_event (&event->button) ||
375 Keyboard::is_edit_event (&event->button))) {
377 /* handled by button release */
381 switch (event->button.button) {
384 if (event->type == GDK_BUTTON_PRESS) {
386 if (drag_info.item) {
387 drag_info.item->ungrab (event->button.time);
390 /* single mouse clicks on any of these item types operate
391 independent of mouse mode, mostly because they are
392 not on the main track canvas or because we want
398 case PlayheadCursorItem:
399 start_cursor_grab (item, event);
403 if (Keyboard::modifier_state_equals (event->button.state,
404 Keyboard::ModifierMask(Keyboard::Control|Keyboard::Shift))) {
405 hide_marker (item, event);
407 start_marker_grab (item, event);
411 case TempoMarkerItem:
412 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
413 start_tempo_marker_copy_grab (item, event);
415 start_tempo_marker_grab (item, event);
419 case MeterMarkerItem:
420 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
421 start_meter_marker_copy_grab (item, event);
423 start_meter_marker_grab (item, event);
433 case RangeMarkerBarItem:
434 start_range_markerbar_op (item, event, CreateRangeMarker);
437 case TransportMarkerBarItem:
438 start_range_markerbar_op (item, event, CreateTransportMarker);
447 switch (mouse_mode) {
450 case StartSelectionTrimItem:
451 start_selection_op (item, event, SelectionStartTrim);
454 case EndSelectionTrimItem:
455 start_selection_op (item, event, SelectionEndTrim);
459 if (Keyboard::modifier_state_contains
460 (event->button.state, Keyboard::ModifierMask(Keyboard::Alt))) {
461 // contains and not equals because I can't use alt as a modifier alone.
462 start_selection_grab (item, event);
463 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
464 /* grab selection for moving */
465 start_selection_op (item, event, SelectionMove);
468 /* this was debated, but decided the more common action was to
469 make a new selection */
470 start_selection_op (item, event, CreateSelection);
475 start_selection_op (item, event, CreateSelection);
481 if (Keyboard::modifier_state_contains (event->button.state,
482 Keyboard::ModifierMask(Keyboard::Control|Keyboard::Alt))
483 && event->type == GDK_BUTTON_PRESS) {
485 start_rubberband_select (item, event);
487 } else if (event->type == GDK_BUTTON_PRESS) {
490 case FadeInHandleItem:
491 start_fade_in_grab (item, event);
494 case FadeOutHandleItem:
495 start_fade_out_grab (item, event);
499 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
500 start_region_copy_grab (item, event);
501 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
502 start_region_brush_grab (item, event);
504 start_region_grab (item, event);
508 case AudioRegionViewNameHighlight:
509 start_trim (item, event);
513 case AudioRegionViewName:
514 /* rename happens on edit clicks */
515 start_trim (clicked_regionview->get_name_highlight(), event);
519 case GainAutomationControlPointItem:
520 case PanAutomationControlPointItem:
521 case RedirectAutomationControlPointItem:
522 start_control_point_grab (item, event);
526 case GainAutomationLineItem:
527 case PanAutomationLineItem:
528 case RedirectAutomationLineItem:
529 start_line_grab_from_line (item, event);
534 case AutomationTrackItem:
535 start_rubberband_select (item, event);
538 /* <CMT Additions> */
539 case ImageFrameHandleStartItem:
540 imageframe_start_handle_op(item, event) ;
543 case ImageFrameHandleEndItem:
544 imageframe_end_handle_op(item, event) ;
547 case MarkerViewHandleStartItem:
548 markerview_item_start_handle_op(item, event) ;
551 case MarkerViewHandleEndItem:
552 markerview_item_end_handle_op(item, event) ;
555 /* </CMT Additions> */
557 /* <CMT Additions> */
559 start_markerview_grab(item, event) ;
562 start_imageframe_grab(item, event) ;
564 /* </CMT Additions> */
576 // start_line_grab_from_regionview (item, event);
579 case GainControlPointItem:
580 start_control_point_grab (item, event);
584 start_line_grab_from_line (item, event);
587 case GainAutomationControlPointItem:
588 case PanAutomationControlPointItem:
589 case RedirectAutomationControlPointItem:
590 start_control_point_grab (item, event);
601 case GainAutomationControlPointItem:
602 case PanAutomationControlPointItem:
603 case RedirectAutomationControlPointItem:
604 start_control_point_grab (item, event);
607 case GainAutomationLineItem:
608 case PanAutomationLineItem:
609 case RedirectAutomationLineItem:
610 start_line_grab_from_line (item, event);
614 // XXX need automation mode to identify which
616 // start_line_grab_from_regionview (item, event);
626 if (event->type == GDK_BUTTON_PRESS) {
627 start_mouse_zoom (item, event);
634 if (item_type == RegionItem) {
635 start_time_fx (item, event);
640 /* handled in release */
649 switch (mouse_mode) {
651 if (event->type == GDK_BUTTON_PRESS) {
654 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
655 start_region_copy_grab (item, event);
657 start_region_grab (item, event);
661 case GainAutomationControlPointItem:
662 case PanAutomationControlPointItem:
663 case RedirectAutomationControlPointItem:
664 start_control_point_grab (item, event);
675 case AudioRegionViewNameHighlight:
676 start_trim (item, event);
680 case AudioRegionViewName:
681 start_trim (clicked_regionview->get_name_highlight(), event);
692 if (event->type == GDK_BUTTON_PRESS) {
693 /* relax till release */
700 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
701 temporal_zoom_session();
703 temporal_zoom_to_frame (true, event_frame(event));
718 switch (mouse_mode) {
720 //temporal_zoom_to_frame (true, where);
721 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
722 temporal_zoom_to_frame (true, where);
725 temporal_zoom_step (true);
730 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask(Keyboard::Shift))) {
731 scroll_backward (0.6f);
734 else if (Keyboard::no_modifier_keys_pressed (&event->button)) {
735 scroll_tracks_up_line ();
737 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
738 if (clicked_trackview) {
739 if (!current_stepping_trackview) {
740 step_timeout = Glib::signal_timeout().connect (mem_fun(*this, &Editor::track_height_step_timeout), 500);
741 current_stepping_trackview = clicked_trackview;
743 gettimeofday (&last_track_height_step_timestamp, 0);
744 current_stepping_trackview->step_height (true);
747 else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
748 temporal_zoom_to_frame (true, where);
755 switch (mouse_mode) {
757 // temporal_zoom_to_frame (false, where);
758 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
759 temporal_zoom_to_frame (false, where);
762 temporal_zoom_step (false);
767 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask(Keyboard::Shift))) {
768 scroll_forward (0.6f);
771 else if (Keyboard::no_modifier_keys_pressed (&event->button)) {
772 scroll_tracks_down_line ();
774 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
775 if (clicked_trackview) {
776 if (!current_stepping_trackview) {
777 step_timeout = Glib::signal_timeout().connect (mem_fun(*this, &Editor::track_height_step_timeout), 500);
778 current_stepping_trackview = clicked_trackview;
780 gettimeofday (&last_track_height_step_timestamp, 0);
781 current_stepping_trackview->step_height (false);
783 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
784 temporal_zoom_to_frame (false, where);
799 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
801 jack_nframes_t where = event_frame (event, 0, 0);
803 /* no action if we're recording */
805 if (session && session->actively_recording()) {
809 /* first, see if we're finishing a drag ... */
811 if (drag_info.item) {
812 if (end_grab (item, event)) {
813 /* grab dragged, so do nothing else */
818 /* edit events get handled here */
820 if (drag_info.item == 0 && Keyboard::is_edit_event (&event->button)) {
826 case TempoMarkerItem:
827 edit_tempo_marker (item);
830 case MeterMarkerItem:
831 edit_meter_marker (item);
834 case AudioRegionViewName:
835 if (clicked_regionview->name_active()) {
836 return mouse_rename_region (item, event);
846 /* context menu events get handled here */
848 if (Keyboard::is_context_menu_event (&event->button)) {
850 if (drag_info.item == 0) {
852 /* no matter which button pops up the context menu, tell the menu
853 widget to use button 1 to drive menu selection.
858 case FadeInHandleItem:
860 case FadeOutHandleItem:
861 popup_fade_context_menu (1, event->button.time, item, item_type);
865 popup_track_context_menu (1, event->button.time, item_type, false, where);
869 case AudioRegionViewNameHighlight:
870 case AudioRegionViewName:
871 popup_track_context_menu (1, event->button.time, item_type, false, where);
875 popup_track_context_menu (1, event->button.time, item_type, true, where);
878 case AutomationTrackItem:
879 popup_track_context_menu (1, event->button.time, item_type, false, where);
883 case RangeMarkerBarItem:
884 case TransportMarkerBarItem:
887 popup_ruler_menu (pixel_to_frame(event->button.x), item_type);
891 marker_context_menu (&event->button, item);
894 case TempoMarkerItem:
895 tm_marker_context_menu (&event->button, item);
898 case MeterMarkerItem:
899 tm_marker_context_menu (&event->button, item);
902 case CrossfadeViewItem:
903 popup_track_context_menu (1, event->button.time, item_type, false, where);
906 /* <CMT Additions> */
908 popup_imageframe_edit_menu(1, event->button.time, item, true) ;
910 case ImageFrameTimeAxisItem:
911 popup_imageframe_edit_menu(1, event->button.time, item, false) ;
914 popup_marker_time_axis_edit_menu(1, event->button.time, item, true) ;
916 case MarkerTimeAxisItem:
917 popup_marker_time_axis_edit_menu(1, event->button.time, item, false) ;
919 /* <CMT Additions> */
930 /* delete events get handled here */
932 if (drag_info.item == 0 && Keyboard::is_delete_event (&event->button)) {
935 case TempoMarkerItem:
936 remove_tempo_marker (item);
939 case MeterMarkerItem:
940 remove_meter_marker (item);
945 remove_marker (*item, event);
950 if (mouse_mode == MouseObject) {
951 remove_clicked_region ();
955 case GainControlPointItem:
956 if (mouse_mode == MouseGain) {
957 remove_gain_control_point (item, event);
961 case GainAutomationControlPointItem:
962 case PanAutomationControlPointItem:
963 case RedirectAutomationControlPointItem:
964 remove_control_point (item, event);
973 switch (event->button.button) {
977 /* see comments in button_press_handler */
979 case PlayheadCursorItem:
982 case GainAutomationLineItem:
983 case PanAutomationLineItem:
984 case RedirectAutomationLineItem:
985 case StartSelectionTrimItem:
986 case EndSelectionTrimItem:
990 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
991 snap_to (where, 0, true);
993 mouse_add_new_marker (where);
997 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1000 mouse_add_new_tempo_event (where);
1004 mouse_add_new_meter_event (pixel_to_frame (event->button.x));
1012 switch (mouse_mode) {
1014 switch (item_type) {
1015 case AutomationTrackItem:
1016 dynamic_cast<AutomationTimeAxisView*>(clicked_trackview)->add_automation_event
1030 switch (item_type) {
1032 clicked_regionview->add_gain_point_event (item, event);
1036 case AutomationTrackItem:
1037 dynamic_cast<AutomationTimeAxisView*>(clicked_trackview)->
1038 add_automation_event (item, event, where, event->button.y);
1047 switch (item_type) {
1049 audition_selected_region ();
1066 switch (mouse_mode) {
1069 switch (item_type) {
1071 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
1073 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::Shift|Keyboard::Alt))) {
1076 // Button2 click is unused
1089 // x_style_paste (where, 1.0);
1109 Editor::maybe_autoscroll (GdkEvent* event)
1111 jack_nframes_t one_page = (jack_nframes_t) rint (canvas_width * frames_per_unit);
1112 jack_nframes_t rightmost_frame = leftmost_frame + one_page;
1114 jack_nframes_t frame = drag_info.current_pointer_frame;
1116 if (autoscroll_timeout_tag < 0) {
1117 if (frame > rightmost_frame) {
1118 if (rightmost_frame < max_frames) {
1119 start_canvas_autoscroll (1);
1121 } else if (frame < leftmost_frame) {
1122 if (leftmost_frame > 0) {
1123 start_canvas_autoscroll (-1);
1127 if (frame >= leftmost_frame && frame < rightmost_frame) {
1128 stop_canvas_autoscroll ();
1134 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1140 switch (item_type) {
1141 case GainControlPointItem:
1142 if (mouse_mode == MouseGain) {
1143 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1144 cp->set_visible (true);
1148 at_y = cp->get_y ();
1149 cp->item->i2w (at_x, at_y);
1153 fraction = 1.0 - (cp->get_y() / cp->line.height());
1155 set_verbose_canvas_cursor (cp->line.get_verbose_cursor_string (fraction), at_x, at_y);
1156 show_verbose_canvas_cursor ();
1158 if (is_drawable()) {
1159 track_canvas.get_window()->set_cursor (*fader_cursor);
1164 case GainAutomationControlPointItem:
1165 case PanAutomationControlPointItem:
1166 case RedirectAutomationControlPointItem:
1167 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1168 cp->set_visible (true);
1172 at_y = cp->get_y ();
1173 cp->item->i2w (at_x, at_y);
1177 fraction = 1.0 - (cp->get_y() / cp->line.height());
1179 set_verbose_canvas_cursor (cp->line.get_verbose_cursor_string (fraction), at_x, at_y);
1180 show_verbose_canvas_cursor ();
1182 if (is_drawable()) {
1183 track_canvas.get_window()->set_cursor (*fader_cursor);
1188 if (mouse_mode == MouseGain) {
1189 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1191 line->property_fill_color_rgba() = color_map[cEnteredGainLine];
1192 if (is_drawable()) {
1193 track_canvas.get_window()->set_cursor (*fader_cursor);
1198 case GainAutomationLineItem:
1199 case RedirectAutomationLineItem:
1200 case PanAutomationLineItem:
1202 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1204 line->property_fill_color_rgba() = color_map[cEnteredAutomationLine];
1206 if (is_drawable()) {
1207 track_canvas.get_window()->set_cursor (*fader_cursor);
1211 case AudioRegionViewNameHighlight:
1212 if (is_drawable() && mouse_mode == MouseObject) {
1213 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1217 case StartSelectionTrimItem:
1218 case EndSelectionTrimItem:
1219 /* <CMT Additions> */
1220 case ImageFrameHandleStartItem:
1221 case ImageFrameHandleEndItem:
1222 case MarkerViewHandleStartItem:
1223 case MarkerViewHandleEndItem:
1224 /* </CMT Additions> */
1226 if (is_drawable()) {
1227 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1231 case EditCursorItem:
1232 case PlayheadCursorItem:
1233 if (is_drawable()) {
1234 track_canvas.get_window()->set_cursor (*grabber_cursor);
1238 case AudioRegionViewName:
1240 /* when the name is not an active item, the entire name highlight is for trimming */
1242 if (!reinterpret_cast<AudioRegionView *> (item->get_data ("regionview"))->name_active()) {
1243 if (mouse_mode == MouseObject && is_drawable()) {
1244 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1250 case AutomationTrackItem:
1251 if (is_drawable()) {
1252 Gdk::Cursor *cursor;
1253 switch (mouse_mode) {
1255 cursor = selector_cursor;
1258 cursor = zoom_cursor;
1261 cursor = cross_hair_cursor;
1265 track_canvas.get_window()->set_cursor (*cursor);
1267 AutomationTimeAxisView* atv;
1268 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1269 clear_entered_track = false;
1270 set_entered_track (atv);
1276 case RangeMarkerBarItem:
1277 case TransportMarkerBarItem:
1280 if (is_drawable()) {
1281 time_canvas.get_window()->set_cursor (*timebar_cursor);
1286 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1289 marker->set_color_rgba (color_map[cEnteredMarker]);
1291 case MeterMarkerItem:
1292 case TempoMarkerItem:
1293 if (is_drawable()) {
1294 time_canvas.get_window()->set_cursor (*timebar_cursor);
1297 case FadeInHandleItem:
1298 case FadeOutHandleItem:
1299 if (mouse_mode == MouseObject) {
1300 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1302 rect->property_fill_color_rgba() = 0;
1303 rect->property_outline_pixels() = 1;
1312 /* second pass to handle entered track status in a comprehensible way.
1315 switch (item_type) {
1317 case GainAutomationLineItem:
1318 case RedirectAutomationLineItem:
1319 case PanAutomationLineItem:
1320 case GainControlPointItem:
1321 case GainAutomationControlPointItem:
1322 case PanAutomationControlPointItem:
1323 case RedirectAutomationControlPointItem:
1324 /* these do not affect the current entered track state */
1325 clear_entered_track = false;
1328 case AutomationTrackItem:
1329 /* handled above already */
1333 set_entered_track (0);
1341 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1347 AudioRegionView* rv;
1350 switch (item_type) {
1351 case GainControlPointItem:
1352 case GainAutomationControlPointItem:
1353 case PanAutomationControlPointItem:
1354 case RedirectAutomationControlPointItem:
1355 cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
1356 if (cp->line.npoints() > 1) {
1357 if (!cp->selected) {
1358 cp->set_visible (false);
1362 if (is_drawable()) {
1363 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1366 hide_verbose_canvas_cursor ();
1369 case AudioRegionViewNameHighlight:
1370 case StartSelectionTrimItem:
1371 case EndSelectionTrimItem:
1372 case EditCursorItem:
1373 case PlayheadCursorItem:
1374 /* <CMT Additions> */
1375 case ImageFrameHandleStartItem:
1376 case ImageFrameHandleEndItem:
1377 case MarkerViewHandleStartItem:
1378 case MarkerViewHandleEndItem:
1379 /* </CMT Additions> */
1380 if (is_drawable()) {
1381 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1386 case GainAutomationLineItem:
1387 case RedirectAutomationLineItem:
1388 case PanAutomationLineItem:
1389 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1391 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1393 line->property_fill_color_rgba() = al->get_line_color();
1395 if (is_drawable()) {
1396 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1400 case AudioRegionViewName:
1401 /* see enter_handler() for notes */
1402 if (!reinterpret_cast<AudioRegionView *> (item->get_data ("regionview"))->name_active()) {
1403 if (is_drawable() && mouse_mode == MouseObject) {
1404 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1409 case RangeMarkerBarItem:
1410 case TransportMarkerBarItem:
1414 if (is_drawable()) {
1415 time_canvas.get_window()->set_cursor (*timebar_cursor);
1420 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1423 loc = find_location_from_marker (marker, is_start);
1424 if (loc) location_flags_changed (loc, this);
1426 case MeterMarkerItem:
1427 case TempoMarkerItem:
1429 if (is_drawable()) {
1430 time_canvas.get_window()->set_cursor (*timebar_cursor);
1435 case FadeInHandleItem:
1436 case FadeOutHandleItem:
1437 rv = static_cast<AudioRegionView*>(item->get_data ("regionview"));
1439 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1441 rect->property_fill_color_rgba() = rv->get_fill_color();
1442 rect->property_outline_pixels() = 0;
1447 case AutomationTrackItem:
1448 if (is_drawable()) {
1449 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1450 clear_entered_track = true;
1451 Glib::signal_idle().connect (mem_fun(*this, &Editor::left_automation_track));
1463 Editor::left_automation_track ()
1465 if (clear_entered_track) {
1466 set_entered_track (0);
1467 clear_entered_track = false;
1473 Editor::motion_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1477 /* We call this so that MOTION_NOTIFY events continue to be
1478 delivered to the canvas. We need to do this because we set
1479 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
1480 the density of the events, at the expense of a round-trip
1481 to the server. Given that this will mostly occur on cases
1482 where DISPLAY = :0.0, and given the cost of what the motion
1483 event might do, its a good tradeoff.
1486 track_canvas.get_pointer (x, y);
1488 if (current_stepping_trackview) {
1489 /* don't keep the persistent stepped trackview if the mouse moves */
1490 current_stepping_trackview = 0;
1491 step_timeout.disconnect ();
1494 if (session && session->actively_recording()) {
1495 /* Sorry. no dragging stuff around while we record */
1499 drag_info.current_pointer_frame = event_frame (event, &drag_info.current_pointer_x,
1500 &drag_info.current_pointer_y);
1502 if (drag_info.item) {
1503 /* item != 0 is the best test i can think of for
1506 if (!drag_info.move_threshold_passsed) {
1507 drag_info.move_threshold_passsed = (abs ((int) (drag_info.current_pointer_x - drag_info.grab_x)) > 4);
1509 // and change the initial grab loc/frame if this drag info wants us to
1510 if (drag_info.want_move_threshold && drag_info.move_threshold_passsed) {
1511 drag_info.grab_frame = drag_info.current_pointer_frame;
1512 drag_info.grab_x = drag_info.current_pointer_x;
1513 drag_info.grab_y = drag_info.current_pointer_y;
1514 drag_info.last_pointer_frame = drag_info.grab_frame;
1515 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
1520 switch (item_type) {
1521 case PlayheadCursorItem:
1522 case EditCursorItem:
1524 case GainControlPointItem:
1525 case RedirectAutomationControlPointItem:
1526 case GainAutomationControlPointItem:
1527 case PanAutomationControlPointItem:
1528 case TempoMarkerItem:
1529 case MeterMarkerItem:
1530 case AudioRegionViewNameHighlight:
1531 case StartSelectionTrimItem:
1532 case EndSelectionTrimItem:
1535 case RedirectAutomationLineItem:
1536 case GainAutomationLineItem:
1537 case PanAutomationLineItem:
1538 case FadeInHandleItem:
1539 case FadeOutHandleItem:
1540 /* <CMT Additions> */
1541 case ImageFrameHandleStartItem:
1542 case ImageFrameHandleEndItem:
1543 case MarkerViewHandleStartItem:
1544 case MarkerViewHandleEndItem:
1545 /* </CMT Additions> */
1546 if (drag_info.item && (event->motion.state & Gdk::BUTTON1_MASK ||
1547 (event->motion.state & Gdk::BUTTON2_MASK))) {
1548 maybe_autoscroll (event);
1549 (this->*(drag_info.motion_callback)) (item, event);
1558 switch (mouse_mode) {
1563 if (drag_info.item && (event->motion.state & GDK_BUTTON1_MASK ||
1564 (event->motion.state & GDK_BUTTON2_MASK))) {
1565 maybe_autoscroll (event);
1566 (this->*(drag_info.motion_callback)) (item, event);
1577 track_canvas_motion (event);
1585 Editor::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
1587 if (drag_info.item == 0) {
1588 fatal << _("programming error: start_grab called without drag item") << endmsg;
1594 cursor = grabber_cursor;
1597 // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
1599 if (event->button.button == 2) {
1600 drag_info.x_constrained = true;
1602 drag_info.x_constrained = false;
1605 drag_info.grab_frame = event_frame(event, &drag_info.grab_x, &drag_info.grab_y);
1606 drag_info.last_pointer_frame = drag_info.grab_frame;
1607 drag_info.current_pointer_frame = drag_info.grab_frame;
1608 drag_info.current_pointer_x = drag_info.grab_x;
1609 drag_info.current_pointer_y = drag_info.grab_y;
1610 drag_info.cumulative_x_drag = 0;
1611 drag_info.cumulative_y_drag = 0;
1612 drag_info.first_move = true;
1613 drag_info.move_threshold_passsed = false;
1614 drag_info.want_move_threshold = false;
1615 drag_info.pointer_frame_offset = 0;
1616 drag_info.brushing = false;
1617 drag_info.copied_location = 0;
1619 drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK,
1621 event->button.time);
1623 if (session && session->transport_rolling()) {
1624 drag_info.was_rolling = true;
1626 drag_info.was_rolling = false;
1629 switch (snap_type) {
1630 case SnapToRegionStart:
1631 case SnapToRegionEnd:
1632 case SnapToRegionSync:
1633 case SnapToRegionBoundary:
1634 build_region_boundary_cache ();
1642 Editor::end_grab (ArdourCanvas::Item* item, GdkEvent* event)
1644 bool did_drag = false;
1646 stop_canvas_autoscroll ();
1648 if (drag_info.item == 0) {
1652 drag_info.item->ungrab (event->button.time);
1654 if (drag_info.finished_callback) {
1655 (this->*(drag_info.finished_callback)) (item, event);
1658 did_drag = !drag_info.first_move;
1660 hide_verbose_canvas_cursor();
1663 drag_info.copy = false;
1664 drag_info.motion_callback = 0;
1665 drag_info.finished_callback = 0;
1666 drag_info.last_trackview = 0;
1667 drag_info.last_frame_position = 0;
1668 drag_info.grab_frame = 0;
1669 drag_info.last_pointer_frame = 0;
1670 drag_info.current_pointer_frame = 0;
1671 drag_info.brushing = false;
1673 if (drag_info.copied_location) {
1674 delete drag_info.copied_location;
1675 drag_info.copied_location = 0;
1682 Editor::set_edit_cursor (GdkEvent* event)
1684 jack_nframes_t pointer_frame = event_frame (event);
1686 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1687 if (snap_type != SnapToEditCursor) {
1688 snap_to (pointer_frame);
1692 edit_cursor->set_position (pointer_frame);
1693 edit_cursor_clock.set (pointer_frame);
1697 Editor::set_playhead_cursor (GdkEvent* event)
1699 jack_nframes_t pointer_frame = event_frame (event);
1701 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1702 snap_to (pointer_frame);
1706 session->request_locate (pointer_frame, session->transport_rolling());
1711 Editor::start_fade_in_grab (ArdourCanvas::Item* item, GdkEvent* event)
1713 drag_info.item = item;
1714 drag_info.motion_callback = &Editor::fade_in_drag_motion_callback;
1715 drag_info.finished_callback = &Editor::fade_in_drag_finished_callback;
1719 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
1720 fatal << _("programming error: fade in canvas item has no regionview data pointer!") << endmsg;
1724 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1726 drag_info.pointer_frame_offset = drag_info.grab_frame - ((jack_nframes_t) arv->region.fade_in().back()->when + arv->region.position());
1730 Editor::fade_in_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1732 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1734 jack_nframes_t fade_length;
1736 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1737 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1743 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1747 if (pos < (arv->region.position() + 64)) {
1748 fade_length = 64; // this should be a minimum defined somewhere
1749 } else if (pos > arv->region.last_frame()) {
1750 fade_length = arv->region.length();
1752 fade_length = pos - arv->region.position();
1755 arv->reset_fade_in_shape_width (fade_length);
1757 show_verbose_duration_cursor (arv->region.position(), arv->region.position() + fade_length, 10);
1759 drag_info.first_move = false;
1763 Editor::fade_in_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1765 if (drag_info.first_move) return;
1767 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1769 jack_nframes_t fade_length;
1771 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1772 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1778 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1782 if (pos < (arv->region.position() + 64)) {
1783 fade_length = 64; // this should be a minimum defined somewhere
1785 else if (pos > arv->region.last_frame()) {
1786 fade_length = arv->region.length();
1789 fade_length = pos - arv->region.position();
1792 begin_reversible_command (_("change fade in length"));
1793 session->add_undo (arv->region.get_memento());
1794 arv->region.set_fade_in_length (fade_length);
1795 session->add_redo_no_execute (arv->region.get_memento());
1796 commit_reversible_command ();
1797 fade_in_drag_motion_callback (item, event);
1801 Editor::start_fade_out_grab (ArdourCanvas::Item* item, GdkEvent* event)
1803 drag_info.item = item;
1804 drag_info.motion_callback = &Editor::fade_out_drag_motion_callback;
1805 drag_info.finished_callback = &Editor::fade_out_drag_finished_callback;
1809 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
1810 fatal << _("programming error: fade out canvas item has no regionview data pointer!") << endmsg;
1814 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1816 drag_info.pointer_frame_offset = drag_info.grab_frame - (arv->region.length() - (jack_nframes_t) arv->region.fade_out().back()->when + arv->region.position());
1820 Editor::fade_out_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1822 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1824 jack_nframes_t fade_length;
1826 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1827 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1833 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1837 if (pos > (arv->region.last_frame() - 64)) {
1838 fade_length = 64; // this should really be a minimum fade defined somewhere
1840 else if (pos < arv->region.position()) {
1841 fade_length = arv->region.length();
1844 fade_length = arv->region.last_frame() - pos;
1847 arv->reset_fade_out_shape_width (fade_length);
1849 show_verbose_duration_cursor (arv->region.last_frame() - fade_length, arv->region.last_frame(), 10);
1851 drag_info.first_move = false;
1855 Editor::fade_out_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1857 if (drag_info.first_move) return;
1859 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1861 jack_nframes_t fade_length;
1863 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1864 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1870 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1874 if (pos > (arv->region.last_frame() - 64)) {
1875 fade_length = 64; // this should really be a minimum fade defined somewhere
1877 else if (pos < arv->region.position()) {
1878 fade_length = arv->region.length();
1881 fade_length = arv->region.last_frame() - pos;
1884 begin_reversible_command (_("change fade out length"));
1885 session->add_undo (arv->region.get_memento());
1886 arv->region.set_fade_out_length (fade_length);
1887 session->add_redo_no_execute (arv->region.get_memento());
1888 commit_reversible_command ();
1890 fade_out_drag_motion_callback (item, event);
1894 Editor::start_cursor_grab (ArdourCanvas::Item* item, GdkEvent* event)
1896 drag_info.item = item;
1897 drag_info.motion_callback = &Editor::cursor_drag_motion_callback;
1898 drag_info.finished_callback = &Editor::cursor_drag_finished_callback;
1902 if ((drag_info.data = (item->get_data ("cursor"))) == 0) {
1903 fatal << _("programming error: cursor canvas item has no cursor data pointer!") << endmsg;
1907 Cursor* cursor = (Cursor *) drag_info.data;
1909 if (session && cursor == playhead_cursor) {
1910 if (drag_info.was_rolling) {
1911 session->request_stop ();
1915 drag_info.pointer_frame_offset = drag_info.grab_frame - cursor->current_frame;
1917 show_verbose_time_cursor (cursor->current_frame, 10);
1921 Editor::cursor_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1923 Cursor* cursor = (Cursor *) drag_info.data;
1924 jack_nframes_t adjusted_frame;
1926 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1927 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1933 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1934 if (cursor != edit_cursor || snap_type != SnapToEditCursor) {
1935 snap_to (adjusted_frame);
1939 if (adjusted_frame == drag_info.last_pointer_frame) return;
1941 cursor->set_position (adjusted_frame);
1943 if (cursor == edit_cursor) {
1944 edit_cursor_clock.set (cursor->current_frame);
1947 show_verbose_time_cursor (cursor->current_frame, 10);
1949 drag_info.last_pointer_frame = adjusted_frame;
1950 drag_info.first_move = false;
1954 Editor::cursor_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1956 if (drag_info.first_move) return;
1958 cursor_drag_motion_callback (item, event);
1960 if (item == &playhead_cursor->canvas_item) {
1962 session->request_locate (playhead_cursor->current_frame, drag_info.was_rolling);
1964 } else if (item == &edit_cursor->canvas_item) {
1965 edit_cursor->set_position (edit_cursor->current_frame);
1966 edit_cursor_clock.set (edit_cursor->current_frame);
1971 Editor::update_marker_drag_item (Location *location)
1973 double x1 = frame_to_pixel (location->start());
1974 double x2 = frame_to_pixel (location->end());
1976 if (location->is_mark()) {
1977 marker_drag_line_points.front().set_x(x1);
1978 marker_drag_line_points.back().set_x(x1);
1979 marker_drag_line->property_points() = marker_drag_line_points;
1982 range_marker_drag_rect->property_x1() = x1;
1983 range_marker_drag_rect->property_x2() = x2;
1988 Editor::start_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
1992 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1993 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
1999 Location *location = find_location_from_marker (marker, is_start);
2001 drag_info.item = item;
2002 drag_info.data = marker;
2003 drag_info.motion_callback = &Editor::marker_drag_motion_callback;
2004 drag_info.finished_callback = &Editor::marker_drag_finished_callback;
2008 drag_info.copied_location = new Location (*location);
2009 drag_info.pointer_frame_offset = drag_info.grab_frame - (is_start ? location->start() : location->end());
2011 update_marker_drag_item (location);
2013 if (location->is_mark()) {
2014 marker_drag_line->show();
2015 marker_drag_line->raise_to_top();
2018 range_marker_drag_rect->show();
2019 range_marker_drag_rect->raise_to_top();
2022 if (is_start) show_verbose_time_cursor (location->start(), 10);
2023 else show_verbose_time_cursor (location->end(), 10);
2027 Editor::marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2029 jack_nframes_t f_delta;
2030 Marker* marker = (Marker *) drag_info.data;
2031 Location *real_location;
2032 Location *copy_location;
2034 bool move_both = false;
2036 jack_nframes_t newframe;
2037 if (drag_info.pointer_frame_offset <= (long) drag_info.current_pointer_frame) {
2038 newframe = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2044 jack_nframes_t next = newframe;
2046 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2047 snap_to (newframe, 0, true);
2050 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
2052 /* call this to find out if its the start or end */
2054 real_location = find_location_from_marker (marker, is_start);
2056 /* use the copy that we're "dragging" around */
2058 copy_location = drag_info.copied_location;
2060 f_delta = copy_location->end() - copy_location->start();
2062 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
2066 if (is_start) { // start marker
2069 copy_location->set_start (newframe);
2070 copy_location->set_end (newframe + f_delta);
2071 } else if (newframe < copy_location->end()) {
2072 copy_location->set_start (newframe);
2074 snap_to (next, 1, true);
2075 copy_location->set_end (next);
2076 copy_location->set_start (newframe);
2079 } else { // end marker
2082 copy_location->set_end (newframe);
2083 copy_location->set_start (newframe - f_delta);
2084 } else if (newframe > copy_location->start()) {
2085 copy_location->set_end (newframe);
2087 } else if (newframe > 0) {
2088 snap_to (next, -1, true);
2089 copy_location->set_start (next);
2090 copy_location->set_end (newframe);
2094 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
2095 drag_info.first_move = false;
2097 update_marker_drag_item (copy_location);
2099 LocationMarkers* lm = find_location_markers (real_location);
2100 lm->set_position (copy_location->start(), copy_location->end());
2102 show_verbose_time_cursor (newframe, 10);
2106 Editor::marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2108 if (drag_info.first_move) {
2109 marker_drag_motion_callback (item, event);
2113 Marker* marker = (Marker *) drag_info.data;
2115 Location * location = find_location_from_marker (marker, is_start);
2117 location->set (drag_info.copied_location->start(), drag_info.copied_location->end());
2120 marker_drag_line->hide();
2121 range_marker_drag_rect->hide();
2125 Editor::start_meter_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2128 MeterMarker* meter_marker;
2130 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2131 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2135 meter_marker = dynamic_cast<MeterMarker*> (marker);
2137 MetricSection& section (meter_marker->meter());
2139 if (!section.movable()) {
2143 drag_info.item = item;
2144 drag_info.data = marker;
2145 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2146 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2150 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2152 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2156 Editor::start_meter_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2159 MeterMarker* meter_marker;
2161 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2162 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2166 meter_marker = dynamic_cast<MeterMarker*> (marker);
2168 // create a dummy marker for visual representation of moving the copy.
2169 // The actual copying is not done before we reach the finish callback.
2171 snprintf (name, sizeof(name), "%g/%g", meter_marker->meter().beats_per_bar(), meter_marker->meter().note_divisor ());
2172 MeterMarker* new_marker = new MeterMarker(*this, *meter_group, color_map[cMeterMarker], name,
2173 *new MeterSection(meter_marker->meter()));
2175 drag_info.item = &new_marker->the_item();
2176 drag_info.copy = true;
2177 drag_info.data = new_marker;
2178 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2179 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2183 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2185 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2189 Editor::meter_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2191 MeterMarker* marker = (MeterMarker *) drag_info.data;
2192 jack_nframes_t adjusted_frame;
2194 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2195 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2201 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2202 snap_to (adjusted_frame);
2205 if (adjusted_frame == drag_info.last_pointer_frame) return;
2207 marker->set_position (adjusted_frame);
2210 drag_info.last_pointer_frame = adjusted_frame;
2211 drag_info.first_move = false;
2213 show_verbose_time_cursor (adjusted_frame, 10);
2217 Editor::meter_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2219 if (drag_info.first_move) return;
2221 meter_marker_drag_motion_callback (drag_info.item, event);
2223 MeterMarker* marker = (MeterMarker *) drag_info.data;
2226 TempoMap& map (session->tempo_map());
2227 map.bbt_time (drag_info.last_pointer_frame, when);
2229 if (drag_info.copy == true) {
2230 begin_reversible_command (_("copy meter mark"));
2231 session->add_undo (map.get_memento());
2232 map.add_meter (marker->meter(), when);
2233 session->add_redo_no_execute (map.get_memento());
2234 commit_reversible_command ();
2236 // delete the dummy marker we used for visual representation of copying.
2237 // a new visual marker will show up automatically.
2240 begin_reversible_command (_("move meter mark"));
2241 session->add_undo (map.get_memento());
2242 map.move_meter (marker->meter(), when);
2243 session->add_redo_no_execute (map.get_memento());
2244 commit_reversible_command ();
2249 Editor::start_tempo_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2252 TempoMarker* tempo_marker;
2254 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2255 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2259 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2260 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2264 MetricSection& section (tempo_marker->tempo());
2266 if (!section.movable()) {
2270 drag_info.item = item;
2271 drag_info.data = marker;
2272 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2273 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2277 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2278 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2282 Editor::start_tempo_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2285 TempoMarker* tempo_marker;
2287 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2288 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2292 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2293 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2297 // create a dummy marker for visual representation of moving the copy.
2298 // The actual copying is not done before we reach the finish callback.
2300 snprintf (name, sizeof (name), "%.2f", tempo_marker->tempo().beats_per_minute());
2301 TempoMarker* new_marker = new TempoMarker(*this, *tempo_group, color_map[cTempoMarker], name,
2302 *new TempoSection(tempo_marker->tempo()));
2304 drag_info.item = &new_marker->the_item();
2305 drag_info.copy = true;
2306 drag_info.data = new_marker;
2307 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2308 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2312 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2314 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2318 Editor::tempo_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2320 TempoMarker* marker = (TempoMarker *) drag_info.data;
2321 jack_nframes_t adjusted_frame;
2323 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2324 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2330 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2331 snap_to (adjusted_frame);
2334 if (adjusted_frame == drag_info.last_pointer_frame) return;
2336 /* OK, we've moved far enough to make it worth actually move the thing. */
2338 marker->set_position (adjusted_frame);
2340 show_verbose_time_cursor (adjusted_frame, 10);
2342 drag_info.last_pointer_frame = adjusted_frame;
2343 drag_info.first_move = false;
2347 Editor::tempo_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2349 if (drag_info.first_move) return;
2351 tempo_marker_drag_motion_callback (drag_info.item, event);
2353 TempoMarker* marker = (TempoMarker *) drag_info.data;
2356 TempoMap& map (session->tempo_map());
2357 map.bbt_time (drag_info.last_pointer_frame, when);
2359 if (drag_info.copy == true) {
2360 begin_reversible_command (_("copy tempo mark"));
2361 session->add_undo (map.get_memento());
2362 map.add_tempo (marker->tempo(), when);
2363 session->add_redo_no_execute (map.get_memento());
2364 commit_reversible_command ();
2366 // delete the dummy marker we used for visual representation of copying.
2367 // a new visual marker will show up automatically.
2370 begin_reversible_command (_("move tempo mark"));
2371 session->add_undo (map.get_memento());
2372 map.move_tempo (marker->tempo(), when);
2373 session->add_redo_no_execute (map.get_memento());
2374 commit_reversible_command ();
2379 Editor::remove_gain_control_point (ArdourCanvas::Item*item, GdkEvent* event)
2381 ControlPoint* control_point;
2383 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2384 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2388 // We shouldn't remove the first or last gain point
2389 if (control_point->line.is_last_point(*control_point) ||
2390 control_point->line.is_first_point(*control_point)) {
2394 control_point->line.remove_point (*control_point);
2398 Editor::remove_control_point (ArdourCanvas::Item*item, GdkEvent* event)
2400 ControlPoint* control_point;
2402 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2403 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2407 control_point->line.remove_point (*control_point);
2411 Editor::start_control_point_grab (ArdourCanvas::Item* item, GdkEvent* event)
2413 ControlPoint* control_point;
2415 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2416 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2420 drag_info.item = item;
2421 drag_info.data = control_point;
2422 drag_info.motion_callback = &Editor::control_point_drag_motion_callback;
2423 drag_info.finished_callback = &Editor::control_point_drag_finished_callback;
2425 start_grab (event, fader_cursor);
2427 control_point->line.start_drag (control_point, 0);
2429 float fraction = 1.0 - (control_point->get_y() / control_point->line.height());
2430 set_verbose_canvas_cursor (control_point->line.get_verbose_cursor_string (fraction),
2431 drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
2433 show_verbose_canvas_cursor ();
2437 Editor::control_point_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2439 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2441 double cx = drag_info.current_pointer_x;
2442 double cy = drag_info.current_pointer_y;
2444 drag_info.cumulative_x_drag = cx - drag_info.grab_x ;
2445 drag_info.cumulative_y_drag = cy - drag_info.grab_y ;
2447 bool x_constrained = false;
2449 if (drag_info.x_constrained) {
2450 if (fabs(drag_info.cumulative_x_drag) < fabs(drag_info.cumulative_y_drag)) {
2451 cx = drag_info.grab_x;
2452 x_constrained = true;
2455 cy = drag_info.grab_y;
2460 cp->line.parent_group().w2i (cx, cy);
2464 cy = min ((double) cp->line.height(), cy);
2466 //translate cx to frames
2467 jack_nframes_t cx_frames = (jack_nframes_t) floor (cx * frames_per_unit);
2469 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier()) && !x_constrained) {
2470 snap_to (cx_frames);
2473 float fraction = 1.0 - (cy / cp->line.height());
2477 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
2483 cp->line.point_drag (*cp, cx_frames , fraction, push);
2485 set_verbose_canvas_cursor_text (cp->line.get_verbose_cursor_string (fraction));
2489 Editor::control_point_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2491 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2492 control_point_drag_motion_callback (item, event);
2493 cp->line.end_drag (cp);
2497 Editor::start_line_grab_from_regionview (ArdourCanvas::Item* item, GdkEvent* event)
2499 switch (mouse_mode) {
2501 start_line_grab (clicked_regionview->get_gain_line(), event);
2509 Editor::start_line_grab_from_line (ArdourCanvas::Item* item, GdkEvent* event)
2513 if ((al = reinterpret_cast<AutomationLine*> (item->get_data ("line"))) == 0) {
2514 fatal << _("programming error: line canvas item has no line pointer!") << endmsg;
2518 start_line_grab (al, event);
2522 Editor::start_line_grab (AutomationLine* line, GdkEvent* event)
2526 jack_nframes_t frame_within_region;
2528 /* need to get x coordinate in terms of parent (TimeAxisItemView)
2532 cx = event->button.x;
2533 cy = event->button.y;
2534 line->parent_group().w2i (cx, cy);
2535 frame_within_region = (jack_nframes_t) floor (cx * frames_per_unit);
2537 if (!line->control_points_adjacent (frame_within_region, current_line_drag_info.before,
2538 current_line_drag_info.after)) {
2539 /* no adjacent points */
2543 drag_info.item = &line->grab_item();
2544 drag_info.data = line;
2545 drag_info.motion_callback = &Editor::line_drag_motion_callback;
2546 drag_info.finished_callback = &Editor::line_drag_finished_callback;
2548 start_grab (event, fader_cursor);
2550 double fraction = 1.0 - (cy / line->height());
2552 line->start_drag (0, fraction);
2554 set_verbose_canvas_cursor (line->get_verbose_cursor_string (fraction),
2555 drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
2556 show_verbose_canvas_cursor ();
2560 Editor::line_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2562 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
2563 double cx = drag_info.current_pointer_x;
2564 double cy = drag_info.current_pointer_y;
2566 line->parent_group().w2i (cx, cy);
2569 fraction = 1.0 - (cy / line->height());
2573 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
2579 line->line_drag (current_line_drag_info.before, current_line_drag_info.after, fraction, push);
2581 set_verbose_canvas_cursor_text (line->get_verbose_cursor_string (fraction));
2585 Editor::line_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2587 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
2588 line_drag_motion_callback (item, event);
2593 Editor::start_region_grab (ArdourCanvas::Item* item, GdkEvent* event)
2595 if (selection->audio_regions.empty() || clicked_regionview == 0) {
2599 drag_info.copy = false;
2600 drag_info.item = item;
2601 drag_info.data = clicked_regionview;
2602 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2603 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2608 TimeAxisView* tvp = clicked_trackview;
2609 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
2611 if (tv && tv->is_audio_track()) {
2612 speed = tv->get_diskstream()->speed();
2615 drag_info.last_frame_position = (jack_nframes_t) (clicked_regionview->region.position() / speed);
2616 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2617 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2618 // we want a move threshold
2619 drag_info.want_move_threshold = true;
2621 show_verbose_time_cursor (drag_info.last_frame_position, 10);
2623 begin_reversible_command (_("move region(s)"));
2627 Editor::start_region_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2629 if (selection->audio_regions.empty() || clicked_regionview == 0) {
2633 /* this is committed in the grab finished callback. */
2635 begin_reversible_command (_("Drag region copy"));
2637 /* duplicate the region(s) */
2639 vector<AudioRegionView*> new_regionviews;
2641 set<Playlist*> affected_playlists;
2642 pair<set<Playlist*>::iterator,bool> insert_result;
2644 for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin(); i != selection->audio_regions.by_layer().end(); ++i) {
2645 AudioRegionView* rv;
2649 Playlist* to_playlist = rv->region.playlist();
2650 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*>(&rv->get_time_axis_view());
2652 insert_result = affected_playlists.insert (to_playlist);
2653 if (insert_result.second) {
2654 session->add_undo (to_playlist->get_memento ());
2657 latest_regionview = 0;
2659 sigc::connection c = atv->view->AudioRegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
2661 /* create a new region with the same name.
2664 AudioRegion* newregion = new AudioRegion (rv->region);
2666 /* if the original region was locked, we don't care */
2668 newregion->set_locked (false);
2670 to_playlist->add_region (*newregion, (jack_nframes_t) (rv->region.position() * atv->get_diskstream()->speed()));
2674 if (latest_regionview) {
2675 new_regionviews.push_back (latest_regionview);
2679 if (new_regionviews.empty()) {
2683 /* reset selection to new regionviews */
2685 selection->set (new_regionviews);
2687 /* reset drag_info data to reflect the fact that we are dragging the copies */
2689 drag_info.data = new_regionviews.front();
2690 drag_info.item = new_regionviews.front()->get_canvas_group ();
2692 drag_info.copy = true;
2693 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2694 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2698 TimeAxisView* tv = &clicked_regionview->get_time_axis_view();
2699 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*>(tv);
2702 if (atv && atv->is_audio_track()) {
2703 speed = atv->get_diskstream()->speed();
2706 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2707 drag_info.last_frame_position = (jack_nframes_t) (clicked_regionview->region.position() / speed);
2708 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2709 // we want a move threshold
2710 drag_info.want_move_threshold = true;
2712 show_verbose_time_cursor (drag_info.last_frame_position, 10);
2714 //begin_reversible_command (_("copy region(s)"));
2718 Editor::start_region_brush_grab (ArdourCanvas::Item* item, GdkEvent* event)
2720 if (selection->audio_regions.empty() || clicked_regionview == 0) {
2724 drag_info.copy = false;
2725 drag_info.item = item;
2726 drag_info.data = clicked_regionview;
2727 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2728 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2733 TimeAxisView* tvp = clicked_trackview;
2734 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
2736 if (tv && tv->is_audio_track()) {
2737 speed = tv->get_diskstream()->speed();
2740 drag_info.last_frame_position = (jack_nframes_t) (clicked_regionview->region.position() / speed);
2741 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2742 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2743 // we want a move threshold
2744 drag_info.want_move_threshold = true;
2745 drag_info.brushing = true;
2747 begin_reversible_command (_("Drag region brush"));
2751 Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2755 AudioRegionView *rv = reinterpret_cast<AudioRegionView*> (drag_info.data);
2756 jack_nframes_t pending_region_position = 0;
2757 int32_t pointer_y_span = 0, canvas_pointer_y_span = 0, original_pointer_order;
2758 int32_t visible_y_high = 0, visible_y_low = 512; //high meaning higher numbered.. not the height on the screen
2759 bool clamp_y_axis = false;
2760 vector<int32_t> height_list(512) ;
2761 vector<int32_t>::iterator j;
2763 /* Which trackview is this ? */
2765 TimeAxisView* tvp = trackview_by_y_position (drag_info.current_pointer_y);
2766 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
2768 /* The region motion is only processed if the pointer is over
2772 if (!tv || !tv->is_audio_track()) {
2773 /* To make sure we hide the verbose canvas cursor when the mouse is
2774 not held over and audiotrack.
2776 hide_verbose_canvas_cursor ();
2780 original_pointer_order = drag_info.last_trackview->order;
2782 /************************************************************
2784 ************************************************************/
2786 if (drag_info.brushing) {
2787 clamp_y_axis = true;
2792 if ((pointer_y_span = (drag_info.last_trackview->order - tv->order)) != 0) {
2794 int32_t children = 0, numtracks = 0;
2795 // XXX hard coding track limit, oh my, so very very bad
2796 bitset <1024> tracks (0x00);
2797 /* get a bitmask representing the visible tracks */
2799 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2800 TimeAxisView *tracklist_timeview;
2801 tracklist_timeview = (*i);
2802 AudioTimeAxisView* atv2 = dynamic_cast<AudioTimeAxisView*>(tracklist_timeview);
2803 list<TimeAxisView*> children_list;
2805 /* zeroes are audio tracks. ones are other types. */
2807 if (!atv2->hidden()) {
2809 if (visible_y_high < atv2->order) {
2810 visible_y_high = atv2->order;
2812 if (visible_y_low > atv2->order) {
2813 visible_y_low = atv2->order;
2816 if (!atv2->is_audio_track()) {
2817 tracks = tracks |= (0x01 << atv2->order);
2820 height_list[atv2->order] = (*i)->height;
2822 if ((children_list = atv2->get_child_list()).size() > 0) {
2823 for (list<TimeAxisView*>::iterator j = children_list.begin(); j != children_list.end(); ++j) {
2824 tracks = tracks |= (0x01 << (atv2->order + children));
2825 height_list[atv2->order + children] = (*j)->height;
2833 /* find the actual span according to the canvas */
2835 canvas_pointer_y_span = pointer_y_span;
2836 if (drag_info.last_trackview->order >= tv->order) {
2838 for (y = tv->order; y < drag_info.last_trackview->order; y++) {
2839 if (height_list[y] == 0 ) {
2840 canvas_pointer_y_span--;
2845 for (y = drag_info.last_trackview->order;y <= tv->order; y++) {
2846 if ( height_list[y] == 0 ) {
2847 canvas_pointer_y_span++;
2852 for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin(); i != selection->audio_regions.by_layer().end(); ++i) {
2853 AudioRegionView* rv2;
2855 double ix1, ix2, iy1, iy2;
2858 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
2859 rv2->get_canvas_group()->i2w (ix1, iy1);
2860 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
2861 AudioTimeAxisView* atv2 = dynamic_cast<AudioTimeAxisView*>(tvp2);
2863 if (atv2->order != original_pointer_order) {
2864 /* this isn't the pointer track */
2866 if (canvas_pointer_y_span > 0) {
2868 /* moving up the canvas */
2869 if ((atv2->order - canvas_pointer_y_span) >= visible_y_low) {
2871 int32_t visible_tracks = 0;
2872 while (visible_tracks < canvas_pointer_y_span ) {
2875 while (height_list[atv2->order - (visible_tracks - n)] == 0) {
2876 /* we're passing through a hidden track */
2881 if (tracks[atv2->order - (canvas_pointer_y_span - n)] != 0x00) {
2882 clamp_y_axis = true;
2886 clamp_y_axis = true;
2889 } else if (canvas_pointer_y_span < 0) {
2891 /*moving down the canvas*/
2893 if ((atv2->order - (canvas_pointer_y_span - n)) <= visible_y_high) { // we will overflow
2896 int32_t visible_tracks = 0;
2898 while (visible_tracks > canvas_pointer_y_span ) {
2901 while (height_list[atv2->order - (visible_tracks - n)] == 0) {
2905 if ( tracks[atv2->order - ( canvas_pointer_y_span - n)] != 0x00) {
2906 clamp_y_axis = true;
2911 clamp_y_axis = true;
2917 /* this is the pointer's track */
2918 if ((atv2->order - pointer_y_span) > visible_y_high) { // we will overflow
2919 clamp_y_axis = true;
2920 } else if ((atv2->order - pointer_y_span) < visible_y_low) { // we will underflow
2921 clamp_y_axis = true;
2929 } else if (drag_info.last_trackview == tv) {
2930 clamp_y_axis = true;
2934 if (!clamp_y_axis) {
2935 drag_info.last_trackview = tv;
2938 /************************************************************
2940 ************************************************************/
2942 /* compute the amount of pointer motion in frames, and where
2943 the region would be if we moved it by that much.
2946 if (drag_info.move_threshold_passsed) {
2948 if ((int32_t)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2950 jack_nframes_t sync_frame;
2951 jack_nframes_t sync_offset;
2954 pending_region_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2956 sync_offset = rv->region.sync_offset (sync_dir);
2957 sync_frame = rv->region.adjust_to_sync (pending_region_position);
2959 /* we snap if the snap modifier is not enabled.
2962 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2963 snap_to (sync_frame);
2966 if (sync_frame - sync_offset <= sync_frame) {
2967 pending_region_position = sync_frame - (sync_dir*sync_offset);
2969 pending_region_position = 0;
2973 pending_region_position = 0;
2976 if (pending_region_position > max_frames - rv->region.length()) {
2977 pending_region_position = drag_info.last_frame_position;
2980 // printf ("3: pending_region_position= %lu %lu\n", pending_region_position, drag_info.last_frame_position );
2982 if (pending_region_position != drag_info.last_frame_position && !drag_info.x_constrained) {
2984 /* now compute the canvas unit distance we need to move the regiondrag_info.last_trackview->order
2985 to make it appear at the new location.
2988 if (pending_region_position > drag_info.last_frame_position) {
2989 x_delta = ((double) (pending_region_position - drag_info.last_frame_position) / frames_per_unit);
2991 x_delta = -((double) (drag_info.last_frame_position - pending_region_position) / frames_per_unit);
2994 drag_info.last_frame_position = pending_region_position;
3001 /* threshold not passed */
3006 /*************************************************************
3008 ************************************************************/
3010 if (x_delta == 0 && (pointer_y_span == 0)) {
3011 /* haven't reached next snap point, and we're not switching
3012 trackviews. nothing to do.
3018 for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin(); i != selection->audio_regions.by_layer().end(); ++i) {
3020 AudioRegionView* rv2;
3023 /* if any regionview is at zero, we need to know so we can
3024 stop further leftward motion.
3027 double ix1, ix2, iy1, iy2;
3028 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3029 rv2->get_canvas_group()->i2w (ix1, iy1);
3038 /*************************************************************
3040 ************************************************************/
3042 pair<set<Playlist*>::iterator,bool> insert_result;
3043 const list<AudioRegionView*>& layered_regions = selection->audio_regions.by_layer();
3045 for (list<AudioRegionView*>::const_iterator i = layered_regions.begin(); i != layered_regions.end(); ++i) {
3047 AudioRegionView* rv;
3049 double ix1, ix2, iy1, iy2;
3050 int32_t temp_pointer_y_span = pointer_y_span;
3052 /* get item BBox, which will be relative to parent. so we have
3053 to query on a child, then convert to world coordinates using
3057 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3058 rv->get_canvas_group()->i2w (ix1, iy1);
3059 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3060 AudioTimeAxisView* canvas_atv = dynamic_cast<AudioTimeAxisView*>(tvp2);
3061 AudioTimeAxisView* temp_atv;
3063 if ((pointer_y_span != 0) && !clamp_y_axis) {
3066 for (j = height_list.begin(); j!= height_list.end(); j++) {
3067 if (x == canvas_atv->order) {
3068 /* we found the track the region is on */
3069 if (x != original_pointer_order) {
3070 /*this isn't from the same track we're dragging from */
3071 temp_pointer_y_span = canvas_pointer_y_span;
3073 while (temp_pointer_y_span > 0) {
3074 /* we're moving up canvas-wise,
3075 so we need to find the next track height
3077 if (j != height_list.begin()) {
3080 if (x != original_pointer_order) {
3081 /* we're not from the dragged track, so ignore hidden tracks. */
3083 temp_pointer_y_span++;
3087 temp_pointer_y_span--;
3089 while (temp_pointer_y_span < 0) {
3091 if (x != original_pointer_order) {
3093 temp_pointer_y_span--;
3097 if (j != height_list.end()) {
3100 temp_pointer_y_span++;
3102 /* find out where we'll be when we move and set height accordingly */
3104 tvp2 = trackview_by_y_position (iy1 + y_delta);
3105 temp_atv = dynamic_cast<AudioTimeAxisView*>(tvp2);
3106 rv->set_height (temp_atv->height);
3108 /* if you un-comment the following, the region colours will follow the track colours whilst dragging,
3109 personally, i think this can confuse things, but never mind.
3112 //const GdkColor& col (temp_atv->view->get_region_color());
3113 //rv->set_color (const_cast<GdkColor&>(col));
3120 /* prevent the regionview from being moved to before
3121 the zero position on the canvas.
3126 if (-x_delta > ix1) {
3129 } else if ((x_delta > 0) &&(rv->region.last_frame() > max_frames - x_delta)) {
3130 x_delta = max_frames - rv->region.last_frame();
3133 if (drag_info.first_move) {
3135 /* hide any dependent views */
3137 // rv->get_time_axis_view().hide_dependent_views (*rv);
3139 /* this is subtle. raising the regionview itself won't help,
3140 because raise_to_top() just puts the item on the top of
3141 its parent's stack. so, we need to put the trackview canvas_display group
3142 on the top, since its parent is the whole canvas.
3145 rv->get_canvas_group()->raise_to_top();
3146 rv->get_time_axis_view().canvas_display->raise_to_top();
3147 cursor_group->raise_to_top();
3149 /* freeze the playlists from notifying till
3153 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*> (&rv->get_time_axis_view());
3154 if (atv && atv->is_audio_track()) {
3155 AudioPlaylist* pl = atv->get_diskstream()->playlist();
3157 /* only freeze and capture state once */
3159 insert_result = motion_frozen_playlists.insert (pl);
3160 if (insert_result.second) {
3162 session->add_undo(pl->get_memento());
3168 if (drag_info.brushing) {
3169 mouse_brush_insert_region (rv, pending_region_position);
3171 rv->move (x_delta, y_delta);
3175 if (drag_info.first_move) {
3176 cursor_group->raise_to_top();
3179 drag_info.first_move = false;
3181 if (x_delta != 0 && !drag_info.brushing) {
3182 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3188 Editor::region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3190 jack_nframes_t where;
3191 AudioRegionView* rv = reinterpret_cast<AudioRegionView *> (drag_info.data);
3192 pair<set<Playlist*>::iterator,bool> insert_result;
3193 bool nocommit = true;
3195 AudioTimeAxisView* atv;
3196 bool regionview_y_movement;
3197 bool regionview_x_movement;
3199 /* first_move is set to false if the regionview has been moved in the
3203 if (drag_info.first_move) {
3210 /* The regionview has been moved at some stage during the grab so we need
3211 to account for any mouse movement between this event and the last one.
3214 region_drag_motion_callback (item, event);
3216 if (drag_info.brushing) {
3217 /* all changes were made during motion event handlers */
3221 /* adjust for track speed */
3224 atv = dynamic_cast<AudioTimeAxisView*> (drag_info.last_trackview);
3225 if (atv && atv->get_diskstream()) {
3226 speed = atv->get_diskstream()->speed();
3229 regionview_x_movement = (drag_info.last_frame_position != (jack_nframes_t) (rv->region.position()/speed));
3230 regionview_y_movement = (drag_info.last_trackview != &rv->get_time_axis_view());
3232 //printf ("last_frame: %s position is %lu %g\n", rv->get_time_axis_view().name().c_str(), drag_info.last_frame_position, speed);
3233 //printf ("last_rackview: %s \n", drag_info.last_trackview->name().c_str());
3235 if (regionview_y_movement) {
3237 /* motion between tracks */
3239 list<AudioRegionView*> new_selection;
3241 /* moved to a different audio track. */
3243 for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin(); i != selection->audio_regions.by_layer().end(); ) {
3245 AudioRegionView* rv2 = (*i);
3247 /* the region that used to be in the old playlist is not
3248 moved to the new one - we make a copy of it. as a result,
3249 any existing editor for the region should no longer be
3253 if (!drag_info.copy) {
3254 rv2->hide_region_editor();
3256 new_selection.push_back (rv2);
3260 /* first, freeze the target tracks */
3262 for (list<AudioRegionView*>::const_iterator i = new_selection.begin(); i != new_selection.end();i++ ) {
3264 Playlist* from_playlist;
3265 Playlist* to_playlist;
3267 double ix1, ix2, iy1, iy2;
3269 (*i)->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3270 (*i)->get_canvas_group()->i2w (ix1, iy1);
3271 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3272 AudioTimeAxisView* atv2 = dynamic_cast<AudioTimeAxisView*>(tvp2);
3274 from_playlist = (*i)->region.playlist();
3275 to_playlist = atv2->playlist();
3277 /* the from_playlist was frozen in the "first_move" case
3278 of the motion handler. the insert can fail,
3279 but that doesn't matter. it just means
3280 we already have the playlist in the list.
3283 motion_frozen_playlists.insert (from_playlist);
3285 /* only freeze the to_playlist once */
3287 insert_result = motion_frozen_playlists.insert(to_playlist);
3288 if (insert_result.second) {
3289 to_playlist->freeze();
3290 session->add_undo(to_playlist->get_memento());
3295 /* now do it again with the actual operations */
3297 for (list<AudioRegionView*>::const_iterator i = new_selection.begin(); i != new_selection.end();i++ ) {
3299 Playlist* from_playlist;
3300 Playlist* to_playlist;
3302 double ix1, ix2, iy1, iy2;
3304 (*i)->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3305 (*i)->get_canvas_group()->i2w (ix1, iy1);
3306 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3307 AudioTimeAxisView* atv2 = dynamic_cast<AudioTimeAxisView*>(tvp2);
3309 from_playlist = (*i)->region.playlist();
3310 to_playlist = atv2->playlist();
3312 latest_regionview = 0;
3314 where = (jack_nframes_t) (unit_to_frame (ix1) * speed);
3315 Region* new_region = createRegion ((*i)->region);
3317 from_playlist->remove_region (&((*i)->region));
3319 sigc::connection c = atv2->view->AudioRegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3320 to_playlist->add_region (*new_region, where);
3323 if (latest_regionview) {
3324 selection->add (latest_regionview);
3330 /* motion within a single track */
3332 for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin(); i != selection->audio_regions.by_layer().end(); ++i) {
3336 if (rv->region.locked()) {
3340 if (regionview_x_movement) {
3341 double ownspeed = 1.0;
3342 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*> (&(rv->get_time_axis_view()));
3344 if (atv && atv->get_diskstream()) {
3345 ownspeed = atv->get_diskstream()->speed();
3348 /* base the new region position on the current position of the regionview.*/
3350 double ix1, ix2, iy1, iy2;
3352 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3353 rv->get_canvas_group()->i2w (ix1, iy1);
3354 where = (jack_nframes_t) (unit_to_frame (ix1) * ownspeed);
3358 where = rv->region.position();
3361 rv->get_time_axis_view().reveal_dependent_views (*rv);
3363 /* no need to add an undo here, we did that when we added this playlist to motion_frozen playlists */
3365 rv->region.set_position (where, (void *) this);
3370 for (set<Playlist*>::iterator p = motion_frozen_playlists.begin(); p != motion_frozen_playlists.end(); ++p) {
3372 session->add_redo_no_execute ((*p)->get_memento());
3375 motion_frozen_playlists.clear ();
3378 commit_reversible_command ();
3383 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
3385 /* Either add to or set the set the region selection, unless
3386 this is an alignment click (control used)
3389 if (Keyboard::modifier_state_contains (event->state, Keyboard::Control)) {
3390 TimeAxisView* tv = &rv.get_time_axis_view();
3391 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*>(tv);
3393 if (atv && atv->is_audio_track()) {
3394 speed = atv->get_diskstream()->speed();
3397 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Alt))) {
3399 align_region (rv.region, SyncPoint, (jack_nframes_t) (edit_cursor->current_frame * speed));
3401 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Shift))) {
3403 align_region (rv.region, End, (jack_nframes_t) (edit_cursor->current_frame * speed));
3407 align_region (rv.region, Start, (jack_nframes_t) (edit_cursor->current_frame * speed));
3413 Editor::show_verbose_time_cursor (jack_nframes_t frame, double offset, double xpos, double ypos)
3424 switch (ARDOUR_UI::instance()->secondary_clock.mode ()) {
3425 case AudioClock::BBT:
3426 session->bbt_time (frame, bbt);
3427 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, bbt.bars, bbt.beats, bbt.ticks);
3430 case AudioClock::SMPTE:
3431 session->smpte_time (frame, smpte);
3432 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
3435 case AudioClock::MinSec:
3436 /* XXX fix this to compute min/sec properly */
3437 session->smpte_time (frame, smpte);
3438 secs = smpte.seconds + ((float) smpte.frames / session->smpte_frames_per_second);
3439 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", smpte.hours, smpte.minutes, secs);
3443 snprintf (buf, sizeof(buf), "%u", frame);
3447 if (xpos >= 0 && ypos >=0) {
3448 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
3451 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
3453 show_verbose_canvas_cursor ();
3457 Editor::show_verbose_duration_cursor (jack_nframes_t start, jack_nframes_t end, double offset, double xpos, double ypos)
3464 Meter meter_at_start(session->tempo_map().meter_at(start));
3470 switch (ARDOUR_UI::instance()->secondary_clock.mode ()) {
3471 case AudioClock::BBT:
3472 session->bbt_time (start, sbbt);
3473 session->bbt_time (end, ebbt);
3476 /* XXX this computation won't work well if the
3477 user makes a selection that spans any meter changes.
3480 ebbt.bars -= sbbt.bars;
3481 if (ebbt.beats >= sbbt.beats) {
3482 ebbt.beats -= sbbt.beats;
3485 ebbt.beats = int(meter_at_start.beats_per_bar()) + ebbt.beats - sbbt.beats;
3487 if (ebbt.ticks >= sbbt.ticks) {
3488 ebbt.ticks -= sbbt.ticks;
3491 ebbt.ticks = int(Meter::ticks_per_beat) + ebbt.ticks - sbbt.ticks;
3494 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, ebbt.bars, ebbt.beats, ebbt.ticks);
3497 case AudioClock::SMPTE:
3498 session->smpte_duration (end - start, smpte);
3499 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
3502 case AudioClock::MinSec:
3503 /* XXX fix this to compute min/sec properly */
3504 session->smpte_duration (end - start, smpte);
3505 secs = smpte.seconds + ((float) smpte.frames / session->smpte_frames_per_second);
3506 snprintf (buf, sizeof (buf), "%02ld:%02ld:%.4f", smpte.hours, smpte.minutes, secs);
3510 snprintf (buf, sizeof(buf), "%u", end - start);
3514 if (xpos >= 0 && ypos >=0) {
3515 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
3518 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
3520 show_verbose_canvas_cursor ();
3524 Editor::collect_new_region_view (AudioRegionView* rv)
3526 latest_regionview = rv;
3530 Editor::start_selection_grab (ArdourCanvas::Item* item, GdkEvent* event)
3532 if (clicked_regionview == 0) {
3536 /* lets try to create new Region for the selection */
3538 vector<AudioRegion*> new_regions;
3539 create_region_from_selection (new_regions);
3541 if (new_regions.empty()) {
3545 /* XXX fix me one day to use all new regions */
3547 Region* region = new_regions.front();
3549 /* add it to the current stream/playlist.
3551 tricky: the streamview for the track will add a new regionview. we will
3552 catch the signal it sends when it creates the regionview to
3553 set the regionview we want to then drag.
3556 latest_regionview = 0;
3557 sigc::connection c = clicked_audio_trackview->view->AudioRegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3559 /* A selection grab currently creates two undo/redo operations, one for
3560 creating the new region and another for moving it.
3563 begin_reversible_command (_("selection grab"));
3565 Playlist* playlist = clicked_trackview->playlist();
3567 session->add_undo (playlist->get_memento ());
3568 clicked_trackview->playlist()->add_region (*region, selection->time[clicked_selection].start);
3569 session->add_redo_no_execute (playlist->get_memento ());
3571 commit_reversible_command ();
3575 if (latest_regionview == 0) {
3576 /* something went wrong */
3580 /* we need to deselect all other regionviews, and select this one
3581 i'm ignoring undo stuff, because the region creation will take care of it */
3582 selection->set (latest_regionview);
3584 drag_info.item = latest_regionview->get_canvas_group();
3585 drag_info.data = latest_regionview;
3586 drag_info.motion_callback = &Editor::region_drag_motion_callback;
3587 drag_info.finished_callback = &Editor::region_drag_finished_callback;
3591 drag_info.last_trackview = clicked_trackview;
3592 drag_info.last_frame_position = latest_regionview->region.position();
3593 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
3595 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3599 Editor::cancel_selection ()
3601 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
3602 (*i)->hide_selection ();
3604 selection->clear ();
3605 clicked_selection = 0;
3609 Editor::start_selection_op (ArdourCanvas::Item* item, GdkEvent* event, SelectionOp op)
3611 jack_nframes_t start = 0;
3612 jack_nframes_t end = 0;
3618 drag_info.item = item;
3619 drag_info.motion_callback = &Editor::drag_selection;
3620 drag_info.finished_callback = &Editor::end_selection_op;
3625 case CreateSelection:
3627 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
3628 drag_info.copy = true;
3630 drag_info.copy = false;
3632 start_grab (event, selector_cursor);
3635 case SelectionStartTrim:
3636 clicked_trackview->order_selection_trims (item, true);
3637 start_grab (event, trimmer_cursor);
3638 start = selection->time[clicked_selection].start;
3639 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
3642 case SelectionEndTrim:
3643 clicked_trackview->order_selection_trims (item, false);
3644 start_grab (event, trimmer_cursor);
3645 end = selection->time[clicked_selection].end;
3646 drag_info.pointer_frame_offset = drag_info.grab_frame - end;
3650 start = selection->time[clicked_selection].start;
3652 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
3656 if (selection_op == SelectionMove) {
3657 show_verbose_time_cursor(start, 10);
3659 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
3664 Editor::drag_selection (ArdourCanvas::Item* item, GdkEvent* event)
3666 jack_nframes_t start = 0;
3667 jack_nframes_t end = 0;
3668 jack_nframes_t length;
3669 jack_nframes_t pending_position;
3671 if ((int32_t) drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
3672 pending_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
3675 pending_position = 0;
3678 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3679 snap_to (pending_position);
3682 /* only alter selection if the current frame is
3683 different from the last frame position (adjusted)
3686 if (pending_position == drag_info.last_pointer_frame) return;
3688 switch (selection_op) {
3689 case CreateSelection:
3691 if (drag_info.first_move) {
3692 snap_to (drag_info.grab_frame);
3695 if (pending_position < drag_info.grab_frame) {
3696 start = pending_position;
3697 end = drag_info.grab_frame;
3699 end = pending_position;
3700 start = drag_info.grab_frame;
3703 /* first drag: Either add to the selection
3704 or create a new selection->
3707 if (drag_info.first_move) {
3709 begin_reversible_command (_("range selection"));
3711 if (drag_info.copy) {
3712 /* adding to the selection */
3713 clicked_selection = selection->add (start, end);
3714 drag_info.copy = false;
3716 /* new selection-> */
3717 clicked_selection = selection->set (clicked_trackview, start, end);
3722 case SelectionStartTrim:
3724 if (drag_info.first_move) {
3725 begin_reversible_command (_("trim selection start"));
3728 start = selection->time[clicked_selection].start;
3729 end = selection->time[clicked_selection].end;
3731 if (pending_position > end) {
3734 start = pending_position;
3738 case SelectionEndTrim:
3740 if (drag_info.first_move) {
3741 begin_reversible_command (_("trim selection end"));
3744 start = selection->time[clicked_selection].start;
3745 end = selection->time[clicked_selection].end;
3747 if (pending_position < start) {
3750 end = pending_position;
3757 if (drag_info.first_move) {
3758 begin_reversible_command (_("move selection"));
3761 start = selection->time[clicked_selection].start;
3762 end = selection->time[clicked_selection].end;
3764 length = end - start;
3766 start = pending_position;
3769 end = start + length;
3774 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
3775 start_canvas_autoscroll (1);
3779 selection->replace (clicked_selection, start, end);
3782 drag_info.last_pointer_frame = pending_position;
3783 drag_info.first_move = false;
3785 if (selection_op == SelectionMove) {
3786 show_verbose_time_cursor(start, 10);
3788 show_verbose_time_cursor(pending_position, 10);
3793 Editor::end_selection_op (ArdourCanvas::Item* item, GdkEvent* event)
3795 if (!drag_info.first_move) {
3796 drag_selection (item, event);
3797 /* XXX this is not object-oriented programming at all. ick */
3798 if (selection->time.consolidate()) {
3799 selection->TimeChanged ();
3801 commit_reversible_command ();
3803 /* just a click, no pointer movement.*/
3805 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
3807 selection->clear_time();
3812 /* XXX what happens if its a music selection? */
3813 session->set_audio_range (selection->time);
3814 stop_canvas_autoscroll ();
3818 Editor::start_trim (ArdourCanvas::Item* item, GdkEvent* event)
3821 TimeAxisView* tvp = clicked_trackview;
3822 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
3824 if (tv && tv->is_audio_track()) {
3825 speed = tv->get_diskstream()->speed();
3828 jack_nframes_t region_start = (jack_nframes_t) (clicked_regionview->region.position() / speed);
3829 jack_nframes_t region_end = (jack_nframes_t) (clicked_regionview->region.last_frame() / speed);
3830 jack_nframes_t region_length = (jack_nframes_t) (clicked_regionview->region.length() / speed);
3832 motion_frozen_playlists.clear();
3834 //drag_info.item = clicked_regionview->get_name_highlight();
3835 drag_info.item = item;
3836 drag_info.motion_callback = &Editor::trim_motion_callback;
3837 drag_info.finished_callback = &Editor::trim_finished_callback;
3839 start_grab (event, trimmer_cursor);
3841 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
3842 trim_op = ContentsTrim;
3844 /* These will get overridden for a point trim.*/
3845 if (drag_info.current_pointer_frame < (region_start + region_length/2)) {
3846 /* closer to start */
3847 trim_op = StartTrim;
3848 } else if (drag_info.current_pointer_frame > (region_end - region_length/2)) {
3856 show_verbose_time_cursor(region_start, 10);
3859 show_verbose_time_cursor(region_end, 10);
3862 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
3868 Editor::trim_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
3870 AudioRegionView* rv = clicked_regionview;
3871 jack_nframes_t frame_delta = 0;
3872 bool left_direction;
3873 bool obey_snap = !Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier());
3875 /* snap modifier works differently here..
3876 its' current state has to be passed to the
3877 various trim functions in order to work properly
3881 TimeAxisView* tvp = clicked_trackview;
3882 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
3883 pair<set<Playlist*>::iterator,bool> insert_result;
3885 if (tv && tv->is_audio_track()) {
3886 speed = tv->get_diskstream()->speed();
3889 if (drag_info.last_pointer_frame > drag_info.current_pointer_frame) {
3890 left_direction = true;
3892 left_direction = false;
3896 snap_to (drag_info.current_pointer_frame);
3899 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
3903 if (drag_info.first_move) {
3909 trim_type = "Region start trim";
3912 trim_type = "Region end trim";
3915 trim_type = "Region content trim";
3919 begin_reversible_command (trim_type);
3921 for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin(); i != selection->audio_regions.by_layer().end(); ++i) {
3922 (*i)->region.freeze ();
3923 (*i)->temporarily_hide_envelope ();
3925 Playlist * pl = (*i)->region.playlist();
3926 insert_result = motion_frozen_playlists.insert (pl);
3927 if (insert_result.second) {
3928 session->add_undo (pl->get_memento());
3933 if (left_direction) {
3934 frame_delta = (drag_info.last_pointer_frame - drag_info.current_pointer_frame);
3936 frame_delta = (drag_info.current_pointer_frame - drag_info.last_pointer_frame);
3941 if ((left_direction == false) && (drag_info.current_pointer_frame <= rv->region.first_frame()/speed)) {
3944 for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin(); i != selection->audio_regions.by_layer().end(); ++i) {
3945 single_start_trim (**i, frame_delta, left_direction, obey_snap);
3951 if ((left_direction == true) && (drag_info.current_pointer_frame > (jack_nframes_t) (rv->region.last_frame()/speed))) {
3954 for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin(); i != selection->audio_regions.by_layer().end(); ++i) {
3955 single_end_trim (**i, frame_delta, left_direction, obey_snap);
3962 bool swap_direction = false;
3964 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
3965 swap_direction = true;
3968 for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin();
3969 i != selection->audio_regions.by_layer().end(); ++i)
3971 single_contents_trim (**i, frame_delta, left_direction, swap_direction, obey_snap);
3979 show_verbose_time_cursor((jack_nframes_t) (rv->region.position()/speed), 10);
3982 show_verbose_time_cursor((jack_nframes_t) (rv->region.last_frame()/speed), 10);
3985 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
3989 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
3990 drag_info.first_move = false;
3994 Editor::single_contents_trim (AudioRegionView& rv, jack_nframes_t frame_delta, bool left_direction, bool swap_direction, bool obey_snap)
3996 Region& region (rv.region);
3998 if (region.locked()) {
4002 jack_nframes_t new_bound;
4005 TimeAxisView* tvp = clicked_trackview;
4006 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
4008 if (tv && tv->is_audio_track()) {
4009 speed = tv->get_diskstream()->speed();
4012 if (left_direction) {
4013 if (swap_direction) {
4014 new_bound = (jack_nframes_t) (region.position()/speed) + frame_delta;
4016 new_bound = (jack_nframes_t) (region.position()/speed) - frame_delta;
4019 if (swap_direction) {
4020 new_bound = (jack_nframes_t) (region.position()/speed) - frame_delta;
4022 new_bound = (jack_nframes_t) (region.position()/speed) + frame_delta;
4027 snap_to (new_bound);
4029 region.trim_start ((jack_nframes_t) (new_bound * speed), this);
4030 rv.region_changed (StartChanged);
4034 Editor::single_start_trim (AudioRegionView& rv, jack_nframes_t frame_delta, bool left_direction, bool obey_snap)
4036 Region& region (rv.region);
4038 if (region.locked()) {
4042 jack_nframes_t new_bound;
4045 TimeAxisView* tvp = clicked_trackview;
4046 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
4048 if (tv && tv->is_audio_track()) {
4049 speed = tv->get_diskstream()->speed();
4052 if (left_direction) {
4053 new_bound = (jack_nframes_t) (region.position()/speed) - frame_delta;
4055 new_bound = (jack_nframes_t) (region.position()/speed) + frame_delta;
4059 snap_to (new_bound, (left_direction ? 0 : 1));
4062 region.trim_front ((jack_nframes_t) (new_bound * speed), this);
4064 rv.region_changed (Change (LengthChanged|PositionChanged|StartChanged));
4068 Editor::single_end_trim (AudioRegionView& rv, jack_nframes_t frame_delta, bool left_direction, bool obey_snap)
4070 Region& region (rv.region);
4072 if (region.locked()) {
4076 jack_nframes_t new_bound;
4079 TimeAxisView* tvp = clicked_trackview;
4080 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
4082 if (tv && tv->is_audio_track()) {
4083 speed = tv->get_diskstream()->speed();
4086 if (left_direction) {
4087 new_bound = (jack_nframes_t) ((region.last_frame() + 1)/speed) - frame_delta;
4089 new_bound = (jack_nframes_t) ((region.last_frame() + 1)/speed) + frame_delta;
4093 snap_to (new_bound);
4095 region.trim_end ((jack_nframes_t) (new_bound * speed), this);
4096 rv.region_changed (LengthChanged);
4100 Editor::trim_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
4102 if (!drag_info.first_move) {
4103 trim_motion_callback (item, event);
4105 if (!clicked_regionview->get_selected()) {
4106 thaw_region_after_trim (*clicked_regionview);
4109 for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin();
4110 i != selection->audio_regions.by_layer().end(); ++i)
4112 thaw_region_after_trim (**i);
4116 for (set<Playlist*>::iterator p = motion_frozen_playlists.begin(); p != motion_frozen_playlists.end(); ++p) {
4118 session->add_redo_no_execute ((*p)->get_memento());
4121 motion_frozen_playlists.clear ();
4123 commit_reversible_command();
4125 /* no mouse movement */
4131 Editor::point_trim (GdkEvent* event)
4133 AudioRegionView* rv = clicked_regionview;
4134 jack_nframes_t new_bound = drag_info.current_pointer_frame;
4136 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4137 snap_to (new_bound);
4140 /* Choose action dependant on which button was pressed */
4141 switch (event->button.button) {
4143 trim_op = StartTrim;
4144 begin_reversible_command (_("Start point trim"));
4146 if (rv->get_selected()) {
4148 for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin();
4149 i != selection->audio_regions.by_layer().end(); ++i)
4151 if (!(*i)->region.locked()) {
4152 session->add_undo ((*i)->region.playlist()->get_memento());
4153 (*i)->region.trim_front (new_bound, this);
4154 session->add_redo_no_execute ((*i)->region.playlist()->get_memento());
4160 if (!rv->region.locked()) {
4161 session->add_undo (rv->region.playlist()->get_memento());
4162 rv->region.trim_front (new_bound, this);
4163 session->add_redo_no_execute (rv->region.playlist()->get_memento());
4167 commit_reversible_command();
4172 begin_reversible_command (_("End point trim"));
4174 if (rv->get_selected()) {
4176 for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin(); i != selection->audio_regions.by_layer().end(); ++i)
4178 if (!(*i)->region.locked()) {
4179 session->add_undo ((*i)->region.playlist()->get_memento());
4180 (*i)->region.trim_end (new_bound, this);
4181 session->add_redo_no_execute ((*i)->region.playlist()->get_memento());
4187 if (!rv->region.locked()) {
4188 session->add_undo (rv->region.playlist()->get_memento());
4189 rv->region.trim_end (new_bound, this);
4190 session->add_redo_no_execute (rv->region.playlist()->get_memento());
4194 commit_reversible_command();
4203 Editor::thaw_region_after_trim (AudioRegionView& rv)
4205 Region& region (rv.region);
4207 if (region.locked()) {
4211 region.thaw (_("trimmed region"));
4212 session->add_redo_no_execute (region.playlist()->get_memento());
4214 rv.unhide_envelope ();
4218 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* event)
4223 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
4224 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
4228 Location* location = find_location_from_marker (marker, is_start);
4229 location->set_hidden (true, this);
4234 Editor::start_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event, RangeMarkerOp op)
4241 drag_info.item = item;
4242 drag_info.motion_callback = &Editor::drag_range_markerbar_op;
4243 drag_info.finished_callback = &Editor::end_range_markerbar_op;
4245 range_marker_op = op;
4247 if (!temp_location) {
4248 temp_location = new Location;
4252 case CreateRangeMarker:
4253 case CreateTransportMarker:
4255 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
4256 drag_info.copy = true;
4258 drag_info.copy = false;
4260 start_grab (event, selector_cursor);
4264 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4269 Editor::drag_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4271 jack_nframes_t start = 0;
4272 jack_nframes_t end = 0;
4273 ArdourCanvas::SimpleRect *crect = (range_marker_op == CreateRangeMarker) ? range_bar_drag_rect: transport_bar_drag_rect;
4275 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4276 snap_to (drag_info.current_pointer_frame);
4279 /* only alter selection if the current frame is
4280 different from the last frame position.
4283 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4285 switch (range_marker_op) {
4286 case CreateRangeMarker:
4287 case CreateTransportMarker:
4288 if (drag_info.first_move) {
4289 snap_to (drag_info.grab_frame);
4292 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4293 start = drag_info.current_pointer_frame;
4294 end = drag_info.grab_frame;
4296 end = drag_info.current_pointer_frame;
4297 start = drag_info.grab_frame;
4300 /* first drag: Either add to the selection
4301 or create a new selection->
4304 if (drag_info.first_move) {
4306 temp_location->set (start, end);
4310 update_marker_drag_item (temp_location);
4311 range_marker_drag_rect->show();
4312 range_marker_drag_rect->raise_to_top();
4318 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
4319 start_canvas_autoscroll (1);
4323 temp_location->set (start, end);
4325 double x1 = frame_to_pixel (start);
4326 double x2 = frame_to_pixel (end);
4327 crect->property_x1() = x1;
4328 crect->property_x2() = x2;
4330 update_marker_drag_item (temp_location);
4333 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4334 drag_info.first_move = false;
4336 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4341 Editor::end_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4343 Location * newloc = 0;
4345 if (!drag_info.first_move) {
4346 drag_range_markerbar_op (item, event);
4348 switch (range_marker_op) {
4349 case CreateRangeMarker:
4350 begin_reversible_command (_("new range marker"));
4351 session->add_undo (session->locations()->get_memento());
4352 newloc = new Location(temp_location->start(), temp_location->end(), "unnamed", Location::IsRangeMarker);
4353 session->locations()->add (newloc, true);
4354 session->add_redo_no_execute (session->locations()->get_memento());
4355 commit_reversible_command ();
4357 range_bar_drag_rect->hide();
4358 range_marker_drag_rect->hide();
4361 case CreateTransportMarker:
4362 // popup menu to pick loop or punch
4363 new_transport_marker_context_menu (&event->button, item);
4368 /* just a click, no pointer movement.*/
4370 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
4377 stop_canvas_autoscroll ();
4383 Editor::start_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4385 drag_info.item = item;
4386 drag_info.motion_callback = &Editor::drag_mouse_zoom;
4387 drag_info.finished_callback = &Editor::end_mouse_zoom;
4389 start_grab (event, zoom_cursor);
4391 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4395 Editor::drag_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4397 jack_nframes_t start;
4400 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4401 snap_to (drag_info.current_pointer_frame);
4403 if (drag_info.first_move) {
4404 snap_to (drag_info.grab_frame);
4408 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4410 /* base start and end on initial click position */
4411 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4412 start = drag_info.current_pointer_frame;
4413 end = drag_info.grab_frame;
4415 end = drag_info.current_pointer_frame;
4416 start = drag_info.grab_frame;
4421 if (drag_info.first_move) {
4423 zoom_rect->raise_to_top();
4426 reposition_zoom_rect(start, end);
4428 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4429 drag_info.first_move = false;
4431 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4436 Editor::end_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4438 if (!drag_info.first_move) {
4439 drag_mouse_zoom (item, event);
4441 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
4442 temporal_zoom_by_frame (drag_info.grab_frame, drag_info.last_pointer_frame, "mouse zoom");
4444 temporal_zoom_by_frame (drag_info.last_pointer_frame, drag_info.grab_frame, "mouse zoom");
4447 temporal_zoom_to_frame (false, drag_info.grab_frame);
4449 temporal_zoom_step (false);
4450 center_screen (drag_info.grab_frame);
4458 Editor::reposition_zoom_rect (jack_nframes_t start, jack_nframes_t end)
4460 double x1 = frame_to_pixel (start);
4461 double x2 = frame_to_pixel (end);
4462 double y2 = canvas_height - 2;
4464 zoom_rect->property_x1() = x1;
4465 zoom_rect->property_y1() = 1.0;
4466 zoom_rect->property_x2() = x2;
4467 zoom_rect->property_y2() = y2;
4471 Editor::start_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4473 drag_info.item = item;
4474 drag_info.motion_callback = &Editor::drag_rubberband_select;
4475 drag_info.finished_callback = &Editor::end_rubberband_select;
4477 start_grab (event, cross_hair_cursor);
4479 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4483 Editor::drag_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4485 jack_nframes_t start;
4490 /* use a bigger drag threshold than the default */
4492 if (abs ((int) (drag_info.current_pointer_frame - drag_info.grab_frame)) < 8) {
4496 // if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4497 // snap_to (drag_info.current_pointer_frame);
4499 // if (drag_info.first_move) {
4500 // snap_to (drag_info.grab_frame);
4505 /* base start and end on initial click position */
4506 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4507 start = drag_info.current_pointer_frame;
4508 end = drag_info.grab_frame;
4510 end = drag_info.current_pointer_frame;
4511 start = drag_info.grab_frame;
4514 if (drag_info.current_pointer_y < drag_info.grab_y) {
4515 y1 = drag_info.current_pointer_y;
4516 y2 = drag_info.grab_y;
4519 y2 = drag_info.current_pointer_y;
4520 y1 = drag_info.grab_y;
4524 if (start != end || y1 != y2) {
4526 double x1 = frame_to_pixel (start);
4527 double x2 = frame_to_pixel (end);
4529 rubberband_rect->property_x1() = x1;
4530 rubberband_rect->property_y1() = y1;
4531 rubberband_rect->property_x2() = x2;
4532 rubberband_rect->property_y2() = y2;
4534 rubberband_rect->show();
4535 rubberband_rect->raise_to_top();
4537 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4538 drag_info.first_move = false;
4540 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4545 Editor::end_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4547 if (!drag_info.first_move) {
4549 drag_rubberband_select (item, event);
4552 if (drag_info.current_pointer_y < drag_info.grab_y) {
4553 y1 = drag_info.current_pointer_y;
4554 y2 = drag_info.grab_y;
4557 y2 = drag_info.current_pointer_y;
4558 y1 = drag_info.grab_y;
4562 Selection::Operation op = Keyboard::selection_type (event->button.state);
4565 begin_reversible_command (_("select regions"));
4567 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
4568 commit = select_all_within (drag_info.grab_frame, drag_info.last_pointer_frame, y1, y2, op);
4570 commit = select_all_within (drag_info.last_pointer_frame, drag_info.grab_frame, y1, y2, op);
4574 commit_reversible_command ();
4578 selection->clear_audio_regions();
4579 selection->clear_points ();
4580 selection->clear_lines ();
4583 rubberband_rect->hide();
4588 Editor::mouse_rename_region (ArdourCanvas::Item* item, GdkEvent* event)
4590 using namespace Gtkmm2ext;
4592 ArdourPrompter prompter (false);
4594 prompter.set_prompt (_("Name for region:"));
4595 prompter.set_initial_text (clicked_regionview->region.name());
4596 prompter.show_all ();
4597 switch (prompter.run ()) {
4598 case Gtk::RESPONSE_ACCEPT:
4600 prompter.get_result(str);
4602 clicked_regionview->region.set_name (str);
4610 Editor::start_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
4612 drag_info.item = item;
4613 drag_info.motion_callback = &Editor::time_fx_motion;
4614 drag_info.finished_callback = &Editor::end_time_fx;
4618 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4622 Editor::time_fx_motion (ArdourCanvas::Item *item, GdkEvent* event)
4624 AudioRegionView* rv = clicked_regionview;
4626 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4627 snap_to (drag_info.current_pointer_frame);
4630 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
4634 if (drag_info.current_pointer_frame > rv->region.position()) {
4635 rv->get_time_axis_view().show_timestretch (rv->region.position(), drag_info.current_pointer_frame);
4638 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4639 drag_info.first_move = false;
4641 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4645 Editor::end_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
4647 clicked_regionview->get_time_axis_view().hide_timestretch ();
4649 if (drag_info.first_move) {
4653 jack_nframes_t newlen = drag_info.last_pointer_frame - clicked_regionview->region.position();
4654 float percentage = (float) ((double) newlen - (double) clicked_regionview->region.length()) / ((double) newlen) * 100.0f;
4656 begin_reversible_command (_("timestretch"));
4658 if (run_timestretch (selection->audio_regions, percentage) == 0) {
4659 session->commit_reversible_command ();
4664 Editor::mouse_brush_insert_region (AudioRegionView* rv, jack_nframes_t pos)
4666 /* no brushing without a useful snap setting */
4668 switch (snap_mode) {
4670 return; /* can't work because it allows region to be placed anywhere */
4675 switch (snap_type) {
4678 case SnapToEditCursor:
4685 /* don't brush a copy over the original */
4687 if (pos == rv->region.position()) {
4691 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*>(&rv->get_time_axis_view());
4693 if (atv == 0 || !atv->is_audio_track()) {
4697 Playlist* playlist = atv->playlist();
4698 double speed = atv->get_diskstream()->speed();
4700 session->add_undo (playlist->get_memento());
4701 playlist->add_region (*(new AudioRegion (rv->region)), (jack_nframes_t) (pos * speed));
4702 session->add_redo_no_execute (playlist->get_memento());
4704 // playlist is frozen, so we have to update manually
4706 playlist->StateChanged (Change (~0)); /* EMIT SIGNAL */
4710 Editor::track_height_step_timeout ()
4713 struct timeval delta;
4715 gettimeofday (&now, 0);
4716 timersub (&now, &last_track_height_step_timestamp, &delta);
4718 if (delta.tv_sec * 1000000 + delta.tv_usec > 250000) { /* milliseconds */
4719 current_stepping_trackview = 0;