2 Copyright (C) 2000-2001 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
28 #include <pbd/error.h>
29 #include <gtkmm2ext/utils.h>
31 #include "ardour_ui.h"
33 #include "time_axis_view.h"
34 #include "audio_time_axis.h"
35 #include "regionview.h"
37 #include "streamview.h"
38 #include "region_gain_line.h"
39 #include "automation_time_axis.h"
42 #include "selection.h"
45 #include "rgb_macros.h"
47 #include <ardour/types.h>
48 #include <ardour/route.h>
49 #include <ardour/audio_track.h>
50 #include <ardour/diskstream.h>
51 #include <ardour/playlist.h>
52 #include <ardour/audioplaylist.h>
53 #include <ardour/audioregion.h>
54 #include <ardour/dB.h>
55 #include <ardour/utils.h>
56 #include <ardour/region_factory.h>
63 using namespace ARDOUR;
66 using namespace Editing;
69 Editor::event_frame (GdkEvent* event, double* pcx, double* pcy)
83 switch (event->type) {
84 case GDK_BUTTON_RELEASE:
85 case GDK_BUTTON_PRESS:
86 case GDK_2BUTTON_PRESS:
87 case GDK_3BUTTON_PRESS:
88 track_canvas.w2c(event->button.x, event->button.y, *pcx, *pcy);
90 case GDK_MOTION_NOTIFY:
91 track_canvas.w2c(event->motion.x, event->motion.y, *pcx, *pcy);
93 case GDK_ENTER_NOTIFY:
94 case GDK_LEAVE_NOTIFY:
95 track_canvas.w2c(event->crossing.x, event->crossing.y, *pcx, *pcy);
100 // track_canvas.w2c(event->key.x, event->key.y, *pcx, *pcy);
103 warning << string_compose (_("Editor::event_frame() used on unhandled event type %1"), event->type) << endmsg;
107 /* note that pixel_to_frame() never returns less than zero, so even if the pixel
108 position is negative (as can be the case with motion events in particular),
109 the frame location is always positive.
112 return pixel_to_frame (*pcx);
116 Editor::mouse_mode_toggled (MouseMode m)
118 if (ignore_mouse_mode_toggle) {
124 if (mouse_select_button.get_active()) {
130 if (mouse_move_button.get_active()) {
136 if (mouse_gain_button.get_active()) {
142 if (mouse_zoom_button.get_active()) {
148 if (mouse_timefx_button.get_active()) {
154 if (mouse_audition_button.get_active()) {
165 Editor::set_mouse_mode (MouseMode m, bool force)
167 if (drag_info.item) {
171 if (m == mouse_mode && !force) {
179 if (mouse_mode != MouseRange) {
181 /* in all modes except range, hide the range selection,
182 show the object (region) selection.
185 for (AudioRegionSelection::iterator i = selection->audio_regions.begin(); i != selection->audio_regions.end(); ++i) {
186 (*i)->set_should_show_selection (true);
188 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
189 (*i)->hide_selection ();
195 in range mode,show the range selection.
198 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
199 if ((*i)->selected()) {
200 (*i)->show_selection (selection->time);
205 /* XXX the hack of unsetting all other buttongs should go
206 away once GTK2 allows us to use regular radio buttons drawn like
207 normal buttons, rather than my silly GroupedButton hack.
210 ignore_mouse_mode_toggle = true;
212 switch (mouse_mode) {
214 mouse_select_button.set_active (true);
215 current_canvas_cursor = selector_cursor;
219 mouse_move_button.set_active (true);
220 current_canvas_cursor = grabber_cursor;
224 mouse_gain_button.set_active (true);
225 current_canvas_cursor = cross_hair_cursor;
229 mouse_zoom_button.set_active (true);
230 current_canvas_cursor = zoom_cursor;
234 mouse_timefx_button.set_active (true);
235 current_canvas_cursor = time_fx_cursor; // just use playhead
239 mouse_audition_button.set_active (true);
240 current_canvas_cursor = speaker_cursor;
244 ignore_mouse_mode_toggle = false;
247 track_canvas.get_window()->set_cursor(*current_canvas_cursor);
252 Editor::step_mouse_mode (bool next)
254 switch (current_mouse_mode()) {
256 if (next) set_mouse_mode (MouseRange);
257 else set_mouse_mode (MouseTimeFX);
261 if (next) set_mouse_mode (MouseZoom);
262 else set_mouse_mode (MouseObject);
266 if (next) set_mouse_mode (MouseGain);
267 else set_mouse_mode (MouseRange);
271 if (next) set_mouse_mode (MouseTimeFX);
272 else set_mouse_mode (MouseZoom);
276 if (next) set_mouse_mode (MouseAudition);
277 else set_mouse_mode (MouseGain);
281 if (next) set_mouse_mode (MouseObject);
282 else set_mouse_mode (MouseTimeFX);
288 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
290 jack_nframes_t where = event_frame (event, 0, 0);
292 track_canvas.grab_focus();
294 if (session && session->actively_recording()) {
298 /* in object/audition/timefx mode, any button press sets
299 the selection if the object can be selected. this is a
300 bit of hack, because we want to avoid this if the
301 mouse operation is a region alignment.
304 if (((mouse_mode == MouseObject) ||
305 (mouse_mode == MouseAudition && item_type == RegionItem) ||
306 (mouse_mode == MouseTimeFX && item_type == RegionItem)) &&
307 event->type == GDK_BUTTON_PRESS &&
308 event->button.button <= 3) {
313 /* not dbl-click or triple-click */
317 set_selected_regionview_from_click (Keyboard::selection_type (event->button.state), true);
320 case AudioRegionViewNameHighlight:
321 case AudioRegionViewName:
322 if ((rv = static_cast<AudioRegionView *> (item->get_data ("regionview"))) != 0) {
323 set_selected_regionview_from_click (Keyboard::selection_type (event->button.state), true);
327 case GainAutomationControlPointItem:
328 case PanAutomationControlPointItem:
329 case RedirectAutomationControlPointItem:
330 if ((cp = static_cast<ControlPoint *> (item->get_data ("control_point"))) != 0) {
331 set_selected_control_point_from_click (Keyboard::selection_type (event->button.state), true);
336 set_selected_track_from_click (Keyboard::selection_type (event->button.state), true, true);
339 case AutomationTrackItem:
347 #define SELECT_TRACK_FROM_CANVAS_IN_RANGE_MODE
348 #ifdef SELECT_TRACK_FROM_CANVAS_IN_RANGE_MODE
349 /* in range mode, button 1/2/3 press potentially selects a track */
351 if (mouse_mode == MouseRange &&
352 event->type == GDK_BUTTON_PRESS &&
353 event->button.button <= 3) {
360 case AutomationTrackItem:
361 set_selected_track_from_click (Keyboard::selection_type (event->button.state), true, true);
364 case AudioRegionViewNameHighlight:
365 case AudioRegionViewName:
366 rv = reinterpret_cast<AudioRegionView *> (item->get_data ("regionview"));
373 if (drag_info.item == 0 &&
374 (Keyboard::is_delete_event (&event->button) ||
375 Keyboard::is_context_menu_event (&event->button) ||
376 Keyboard::is_edit_event (&event->button))) {
378 /* handled by button release */
382 switch (event->button.button) {
385 if (event->type == GDK_BUTTON_PRESS) {
387 if (drag_info.item) {
388 drag_info.item->ungrab (event->button.time);
391 /* single mouse clicks on any of these item types operate
392 independent of mouse mode, mostly because they are
393 not on the main track canvas or because we want
399 case PlayheadCursorItem:
400 start_cursor_grab (item, event);
404 if (Keyboard::modifier_state_equals (event->button.state,
405 Keyboard::ModifierMask(Keyboard::Control|Keyboard::Shift))) {
406 hide_marker (item, event);
408 start_marker_grab (item, event);
412 case TempoMarkerItem:
413 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
414 start_tempo_marker_copy_grab (item, event);
416 start_tempo_marker_grab (item, event);
420 case MeterMarkerItem:
421 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
422 start_meter_marker_copy_grab (item, event);
424 start_meter_marker_grab (item, event);
434 case RangeMarkerBarItem:
435 start_range_markerbar_op (item, event, CreateRangeMarker);
438 case TransportMarkerBarItem:
439 start_range_markerbar_op (item, event, CreateTransportMarker);
448 switch (mouse_mode) {
451 case StartSelectionTrimItem:
452 start_selection_op (item, event, SelectionStartTrim);
455 case EndSelectionTrimItem:
456 start_selection_op (item, event, SelectionEndTrim);
460 if (Keyboard::modifier_state_contains
461 (event->button.state, Keyboard::ModifierMask(Keyboard::Alt))) {
462 // contains and not equals because I can't use alt as a modifier alone.
463 start_selection_grab (item, event);
464 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
465 /* grab selection for moving */
466 start_selection_op (item, event, SelectionMove);
469 /* this was debated, but decided the more common action was to
470 make a new selection */
471 start_selection_op (item, event, CreateSelection);
476 start_selection_op (item, event, CreateSelection);
482 if (Keyboard::modifier_state_contains (event->button.state,
483 Keyboard::ModifierMask(Keyboard::Control|Keyboard::Alt))
484 && event->type == GDK_BUTTON_PRESS) {
486 start_rubberband_select (item, event);
488 } else if (event->type == GDK_BUTTON_PRESS) {
491 case FadeInHandleItem:
492 start_fade_in_grab (item, event);
495 case FadeOutHandleItem:
496 start_fade_out_grab (item, event);
500 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
501 start_region_copy_grab (item, event);
502 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
503 start_region_brush_grab (item, event);
505 start_region_grab (item, event);
509 case AudioRegionViewNameHighlight:
510 start_trim (item, event);
514 case AudioRegionViewName:
515 /* rename happens on edit clicks */
516 start_trim (clicked_regionview->get_name_highlight(), event);
520 case GainAutomationControlPointItem:
521 case PanAutomationControlPointItem:
522 case RedirectAutomationControlPointItem:
523 start_control_point_grab (item, event);
527 case GainAutomationLineItem:
528 case PanAutomationLineItem:
529 case RedirectAutomationLineItem:
530 start_line_grab_from_line (item, event);
535 case AutomationTrackItem:
536 start_rubberband_select (item, event);
539 /* <CMT Additions> */
540 case ImageFrameHandleStartItem:
541 imageframe_start_handle_op(item, event) ;
544 case ImageFrameHandleEndItem:
545 imageframe_end_handle_op(item, event) ;
548 case MarkerViewHandleStartItem:
549 markerview_item_start_handle_op(item, event) ;
552 case MarkerViewHandleEndItem:
553 markerview_item_end_handle_op(item, event) ;
556 /* </CMT Additions> */
558 /* <CMT Additions> */
560 start_markerview_grab(item, event) ;
563 start_imageframe_grab(item, event) ;
565 /* </CMT Additions> */
577 // start_line_grab_from_regionview (item, event);
580 case GainControlPointItem:
581 start_control_point_grab (item, event);
585 start_line_grab_from_line (item, event);
588 case GainAutomationControlPointItem:
589 case PanAutomationControlPointItem:
590 case RedirectAutomationControlPointItem:
591 start_control_point_grab (item, event);
602 case GainAutomationControlPointItem:
603 case PanAutomationControlPointItem:
604 case RedirectAutomationControlPointItem:
605 start_control_point_grab (item, event);
608 case GainAutomationLineItem:
609 case PanAutomationLineItem:
610 case RedirectAutomationLineItem:
611 start_line_grab_from_line (item, event);
615 // XXX need automation mode to identify which
617 // start_line_grab_from_regionview (item, event);
627 if (event->type == GDK_BUTTON_PRESS) {
628 start_mouse_zoom (item, event);
635 if (item_type == RegionItem) {
636 start_time_fx (item, event);
641 /* handled in release */
650 switch (mouse_mode) {
652 if (event->type == GDK_BUTTON_PRESS) {
655 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
656 start_region_copy_grab (item, event);
658 start_region_grab (item, event);
662 case GainAutomationControlPointItem:
663 case PanAutomationControlPointItem:
664 case RedirectAutomationControlPointItem:
665 start_control_point_grab (item, event);
676 case AudioRegionViewNameHighlight:
677 start_trim (item, event);
681 case AudioRegionViewName:
682 start_trim (clicked_regionview->get_name_highlight(), event);
693 if (event->type == GDK_BUTTON_PRESS) {
694 /* relax till release */
701 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
702 temporal_zoom_session();
704 temporal_zoom_to_frame (true, event_frame(event));
719 switch (mouse_mode) {
721 //temporal_zoom_to_frame (true, where);
722 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
723 temporal_zoom_to_frame (true, where);
726 temporal_zoom_step (true);
731 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::Alt))) {
732 scroll_backward (0.6f);
735 else if (Keyboard::no_modifier_keys_pressed (&event->button)) {
736 scroll_tracks_up_line ();
738 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
739 if (clicked_trackview) {
740 if (!current_stepping_trackview) {
741 step_timeout = Glib::signal_timeout().connect (mem_fun(*this, &Editor::track_height_step_timeout), 500);
742 current_stepping_trackview = clicked_trackview;
744 gettimeofday (&last_track_height_step_timestamp, 0);
745 current_stepping_trackview->step_height (true);
748 else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
749 temporal_zoom_to_frame (true, where);
756 switch (mouse_mode) {
758 // temporal_zoom_to_frame (false, where);
759 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
760 temporal_zoom_to_frame (false, where);
763 temporal_zoom_step (false);
768 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::Alt))) {
769 scroll_forward (0.6f);
772 else if (Keyboard::no_modifier_keys_pressed (&event->button)) {
773 scroll_tracks_down_line ();
775 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
776 if (clicked_trackview) {
777 if (!current_stepping_trackview) {
778 step_timeout = Glib::signal_timeout().connect (mem_fun(*this, &Editor::track_height_step_timeout), 500);
779 current_stepping_trackview = clicked_trackview;
781 gettimeofday (&last_track_height_step_timestamp, 0);
782 current_stepping_trackview->step_height (false);
784 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
785 temporal_zoom_to_frame (false, where);
800 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
802 jack_nframes_t where = event_frame (event, 0, 0);
804 /* no action if we're recording */
806 if (session && session->actively_recording()) {
810 /* first, see if we're finishing a drag ... */
812 if (drag_info.item) {
813 if (end_grab (item, event)) {
814 /* grab dragged, so do nothing else */
819 /* edit events get handled here */
821 if (drag_info.item == 0 && Keyboard::is_edit_event (&event->button)) {
827 case TempoMarkerItem:
828 edit_tempo_marker (item);
831 case MeterMarkerItem:
832 edit_meter_marker (item);
835 case AudioRegionViewName:
836 if (clicked_regionview->name_active()) {
837 return mouse_rename_region (item, event);
847 /* context menu events get handled here */
849 if (Keyboard::is_context_menu_event (&event->button)) {
851 if (drag_info.item == 0) {
853 /* no matter which button pops up the context menu, tell the menu
854 widget to use button 1 to drive menu selection.
859 case FadeInHandleItem:
861 case FadeOutHandleItem:
862 popup_fade_context_menu (1, event->button.time, item, item_type);
866 popup_track_context_menu (1, event->button.time, item_type, false, where);
870 case AudioRegionViewNameHighlight:
871 case AudioRegionViewName:
872 popup_track_context_menu (1, event->button.time, item_type, false, where);
876 popup_track_context_menu (1, event->button.time, item_type, true, where);
879 case AutomationTrackItem:
880 popup_track_context_menu (1, event->button.time, item_type, false, where);
884 case RangeMarkerBarItem:
885 case TransportMarkerBarItem:
888 popup_ruler_menu (pixel_to_frame(event->button.x), item_type);
892 marker_context_menu (&event->button, item);
895 case TempoMarkerItem:
896 tm_marker_context_menu (&event->button, item);
899 case MeterMarkerItem:
900 tm_marker_context_menu (&event->button, item);
903 case CrossfadeViewItem:
904 popup_track_context_menu (1, event->button.time, item_type, false, where);
907 /* <CMT Additions> */
909 popup_imageframe_edit_menu(1, event->button.time, item, true) ;
911 case ImageFrameTimeAxisItem:
912 popup_imageframe_edit_menu(1, event->button.time, item, false) ;
915 popup_marker_time_axis_edit_menu(1, event->button.time, item, true) ;
917 case MarkerTimeAxisItem:
918 popup_marker_time_axis_edit_menu(1, event->button.time, item, false) ;
920 /* <CMT Additions> */
931 /* delete events get handled here */
933 if (drag_info.item == 0 && Keyboard::is_delete_event (&event->button)) {
936 case TempoMarkerItem:
937 remove_tempo_marker (item);
940 case MeterMarkerItem:
941 remove_meter_marker (item);
946 remove_marker (*item, event);
951 if (mouse_mode == MouseObject) {
952 remove_clicked_region ();
956 case GainControlPointItem:
957 if (mouse_mode == MouseGain) {
958 remove_gain_control_point (item, event);
962 case GainAutomationControlPointItem:
963 case PanAutomationControlPointItem:
964 case RedirectAutomationControlPointItem:
965 remove_control_point (item, event);
974 switch (event->button.button) {
978 /* see comments in button_press_handler */
980 case PlayheadCursorItem:
983 case GainAutomationLineItem:
984 case PanAutomationLineItem:
985 case RedirectAutomationLineItem:
986 case StartSelectionTrimItem:
987 case EndSelectionTrimItem:
991 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
992 snap_to (where, 0, true);
994 mouse_add_new_marker (where);
998 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1001 mouse_add_new_tempo_event (where);
1005 mouse_add_new_meter_event (pixel_to_frame (event->button.x));
1013 switch (mouse_mode) {
1015 switch (item_type) {
1016 case AutomationTrackItem:
1017 dynamic_cast<AutomationTimeAxisView*>(clicked_trackview)->add_automation_event
1031 switch (item_type) {
1033 clicked_regionview->add_gain_point_event (item, event);
1037 case AutomationTrackItem:
1038 dynamic_cast<AutomationTimeAxisView*>(clicked_trackview)->
1039 add_automation_event (item, event, where, event->button.y);
1048 switch (item_type) {
1050 audition_selected_region ();
1067 switch (mouse_mode) {
1070 switch (item_type) {
1072 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
1074 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::Shift|Keyboard::Alt))) {
1077 // Button2 click is unused
1090 // x_style_paste (where, 1.0);
1110 Editor::maybe_autoscroll (GdkEvent* event)
1112 jack_nframes_t one_page = (jack_nframes_t) rint (canvas_width * frames_per_unit);
1113 jack_nframes_t rightmost_frame = leftmost_frame + one_page;
1115 jack_nframes_t frame = drag_info.current_pointer_frame;
1117 if (autoscroll_timeout_tag < 0) {
1118 if (frame > rightmost_frame) {
1119 if (rightmost_frame < max_frames) {
1120 start_canvas_autoscroll (1);
1122 } else if (frame < leftmost_frame) {
1123 if (leftmost_frame > 0) {
1124 start_canvas_autoscroll (-1);
1128 if (frame >= leftmost_frame && frame < rightmost_frame) {
1129 stop_canvas_autoscroll ();
1135 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1141 switch (item_type) {
1142 case GainControlPointItem:
1143 if (mouse_mode == MouseGain) {
1144 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1145 cp->set_visible (true);
1149 at_y = cp->get_y ();
1150 cp->item->i2w (at_x, at_y);
1154 fraction = 1.0 - (cp->get_y() / cp->line.height());
1156 set_verbose_canvas_cursor (cp->line.get_verbose_cursor_string (fraction), at_x, at_y);
1157 show_verbose_canvas_cursor ();
1159 if (is_drawable()) {
1160 track_canvas.get_window()->set_cursor (*fader_cursor);
1165 case GainAutomationControlPointItem:
1166 case PanAutomationControlPointItem:
1167 case RedirectAutomationControlPointItem:
1168 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1169 cp->set_visible (true);
1173 at_y = cp->get_y ();
1174 cp->item->i2w (at_x, at_y);
1178 fraction = 1.0 - (cp->get_y() / cp->line.height());
1180 set_verbose_canvas_cursor (cp->line.get_verbose_cursor_string (fraction), at_x, at_y);
1181 show_verbose_canvas_cursor ();
1183 if (is_drawable()) {
1184 track_canvas.get_window()->set_cursor (*fader_cursor);
1189 if (mouse_mode == MouseGain) {
1190 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1192 line->property_fill_color_rgba() = color_map[cEnteredGainLine];
1193 if (is_drawable()) {
1194 track_canvas.get_window()->set_cursor (*fader_cursor);
1199 case GainAutomationLineItem:
1200 case RedirectAutomationLineItem:
1201 case PanAutomationLineItem:
1203 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1205 line->property_fill_color_rgba() = color_map[cEnteredAutomationLine];
1207 if (is_drawable()) {
1208 track_canvas.get_window()->set_cursor (*fader_cursor);
1212 case AudioRegionViewNameHighlight:
1213 if (is_drawable() && mouse_mode == MouseObject) {
1214 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1218 case StartSelectionTrimItem:
1219 case EndSelectionTrimItem:
1220 /* <CMT Additions> */
1221 case ImageFrameHandleStartItem:
1222 case ImageFrameHandleEndItem:
1223 case MarkerViewHandleStartItem:
1224 case MarkerViewHandleEndItem:
1225 /* </CMT Additions> */
1227 if (is_drawable()) {
1228 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1232 case EditCursorItem:
1233 case PlayheadCursorItem:
1234 if (is_drawable()) {
1235 track_canvas.get_window()->set_cursor (*grabber_cursor);
1239 case AudioRegionViewName:
1241 /* when the name is not an active item, the entire name highlight is for trimming */
1243 if (!reinterpret_cast<AudioRegionView *> (item->get_data ("regionview"))->name_active()) {
1244 if (mouse_mode == MouseObject && is_drawable()) {
1245 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1251 case AutomationTrackItem:
1252 if (is_drawable()) {
1253 Gdk::Cursor *cursor;
1254 switch (mouse_mode) {
1256 cursor = selector_cursor;
1259 cursor = zoom_cursor;
1262 cursor = cross_hair_cursor;
1266 track_canvas.get_window()->set_cursor (*cursor);
1268 AutomationTimeAxisView* atv;
1269 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1270 clear_entered_track = false;
1271 set_entered_track (atv);
1277 case RangeMarkerBarItem:
1278 case TransportMarkerBarItem:
1281 if (is_drawable()) {
1282 time_canvas.get_window()->set_cursor (*timebar_cursor);
1287 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1290 marker->set_color_rgba (color_map[cEnteredMarker]);
1292 case MeterMarkerItem:
1293 case TempoMarkerItem:
1294 if (is_drawable()) {
1295 time_canvas.get_window()->set_cursor (*timebar_cursor);
1298 case FadeInHandleItem:
1299 case FadeOutHandleItem:
1300 if (mouse_mode == MouseObject) {
1301 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1303 rect->property_fill_color_rgba() = 0;
1304 rect->property_outline_pixels() = 1;
1313 /* second pass to handle entered track status in a comprehensible way.
1316 switch (item_type) {
1318 case GainAutomationLineItem:
1319 case RedirectAutomationLineItem:
1320 case PanAutomationLineItem:
1321 case GainControlPointItem:
1322 case GainAutomationControlPointItem:
1323 case PanAutomationControlPointItem:
1324 case RedirectAutomationControlPointItem:
1325 /* these do not affect the current entered track state */
1326 clear_entered_track = false;
1329 case AutomationTrackItem:
1330 /* handled above already */
1334 set_entered_track (0);
1342 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1348 AudioRegionView* rv;
1351 switch (item_type) {
1352 case GainControlPointItem:
1353 case GainAutomationControlPointItem:
1354 case PanAutomationControlPointItem:
1355 case RedirectAutomationControlPointItem:
1356 cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
1357 if (cp->line.npoints() > 1) {
1358 if (!cp->selected) {
1359 cp->set_visible (false);
1363 if (is_drawable()) {
1364 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1367 hide_verbose_canvas_cursor ();
1370 case AudioRegionViewNameHighlight:
1371 case StartSelectionTrimItem:
1372 case EndSelectionTrimItem:
1373 case EditCursorItem:
1374 case PlayheadCursorItem:
1375 /* <CMT Additions> */
1376 case ImageFrameHandleStartItem:
1377 case ImageFrameHandleEndItem:
1378 case MarkerViewHandleStartItem:
1379 case MarkerViewHandleEndItem:
1380 /* </CMT Additions> */
1381 if (is_drawable()) {
1382 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1387 case GainAutomationLineItem:
1388 case RedirectAutomationLineItem:
1389 case PanAutomationLineItem:
1390 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1392 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1394 line->property_fill_color_rgba() = al->get_line_color();
1396 if (is_drawable()) {
1397 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1401 case AudioRegionViewName:
1402 /* see enter_handler() for notes */
1403 if (!reinterpret_cast<AudioRegionView *> (item->get_data ("regionview"))->name_active()) {
1404 if (is_drawable() && mouse_mode == MouseObject) {
1405 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1410 case RangeMarkerBarItem:
1411 case TransportMarkerBarItem:
1415 if (is_drawable()) {
1416 time_canvas.get_window()->set_cursor (*timebar_cursor);
1421 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1424 loc = find_location_from_marker (marker, is_start);
1425 if (loc) location_flags_changed (loc, this);
1427 case MeterMarkerItem:
1428 case TempoMarkerItem:
1430 if (is_drawable()) {
1431 time_canvas.get_window()->set_cursor (*timebar_cursor);
1436 case FadeInHandleItem:
1437 case FadeOutHandleItem:
1438 rv = static_cast<AudioRegionView*>(item->get_data ("regionview"));
1440 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1442 rect->property_fill_color_rgba() = rv->get_fill_color();
1443 rect->property_outline_pixels() = 0;
1448 case AutomationTrackItem:
1449 if (is_drawable()) {
1450 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1451 clear_entered_track = true;
1452 Glib::signal_idle().connect (mem_fun(*this, &Editor::left_automation_track));
1464 Editor::left_automation_track ()
1466 if (clear_entered_track) {
1467 set_entered_track (0);
1468 clear_entered_track = false;
1474 Editor::motion_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1478 /* We call this so that MOTION_NOTIFY events continue to be
1479 delivered to the canvas. We need to do this because we set
1480 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
1481 the density of the events, at the expense of a round-trip
1482 to the server. Given that this will mostly occur on cases
1483 where DISPLAY = :0.0, and given the cost of what the motion
1484 event might do, its a good tradeoff.
1487 track_canvas.get_pointer (x, y);
1489 if (current_stepping_trackview) {
1490 /* don't keep the persistent stepped trackview if the mouse moves */
1491 current_stepping_trackview = 0;
1492 step_timeout.disconnect ();
1495 if (session && session->actively_recording()) {
1496 /* Sorry. no dragging stuff around while we record */
1500 drag_info.current_pointer_frame = event_frame (event, &drag_info.current_pointer_x,
1501 &drag_info.current_pointer_y);
1503 if (drag_info.item) {
1504 /* item != 0 is the best test i can think of for
1507 if (!drag_info.move_threshold_passsed) {
1508 drag_info.move_threshold_passsed = (abs ((int) (drag_info.current_pointer_x - drag_info.grab_x)) > 4);
1510 // and change the initial grab loc/frame if this drag info wants us to
1511 if (drag_info.want_move_threshold && drag_info.move_threshold_passsed) {
1512 drag_info.grab_frame = drag_info.current_pointer_frame;
1513 drag_info.grab_x = drag_info.current_pointer_x;
1514 drag_info.grab_y = drag_info.current_pointer_y;
1515 drag_info.last_pointer_frame = drag_info.grab_frame;
1516 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
1521 switch (item_type) {
1522 case PlayheadCursorItem:
1523 case EditCursorItem:
1525 case GainControlPointItem:
1526 case RedirectAutomationControlPointItem:
1527 case GainAutomationControlPointItem:
1528 case PanAutomationControlPointItem:
1529 case TempoMarkerItem:
1530 case MeterMarkerItem:
1531 case AudioRegionViewNameHighlight:
1532 case StartSelectionTrimItem:
1533 case EndSelectionTrimItem:
1536 case RedirectAutomationLineItem:
1537 case GainAutomationLineItem:
1538 case PanAutomationLineItem:
1539 case FadeInHandleItem:
1540 case FadeOutHandleItem:
1541 /* <CMT Additions> */
1542 case ImageFrameHandleStartItem:
1543 case ImageFrameHandleEndItem:
1544 case MarkerViewHandleStartItem:
1545 case MarkerViewHandleEndItem:
1546 /* </CMT Additions> */
1547 if (drag_info.item && (event->motion.state & Gdk::BUTTON1_MASK ||
1548 (event->motion.state & Gdk::BUTTON2_MASK))) {
1549 maybe_autoscroll (event);
1550 (this->*(drag_info.motion_callback)) (item, event);
1559 switch (mouse_mode) {
1564 if (drag_info.item && (event->motion.state & GDK_BUTTON1_MASK ||
1565 (event->motion.state & GDK_BUTTON2_MASK))) {
1566 maybe_autoscroll (event);
1567 (this->*(drag_info.motion_callback)) (item, event);
1578 track_canvas_motion (event);
1586 Editor::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
1588 if (drag_info.item == 0) {
1589 fatal << _("programming error: start_grab called without drag item") << endmsg;
1595 cursor = grabber_cursor;
1598 // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
1600 if (event->button.button == 2) {
1601 drag_info.x_constrained = true;
1603 drag_info.x_constrained = false;
1606 drag_info.grab_frame = event_frame(event, &drag_info.grab_x, &drag_info.grab_y);
1607 drag_info.last_pointer_frame = drag_info.grab_frame;
1608 drag_info.current_pointer_frame = drag_info.grab_frame;
1609 drag_info.current_pointer_x = drag_info.grab_x;
1610 drag_info.current_pointer_y = drag_info.grab_y;
1611 drag_info.cumulative_x_drag = 0;
1612 drag_info.cumulative_y_drag = 0;
1613 drag_info.first_move = true;
1614 drag_info.move_threshold_passsed = false;
1615 drag_info.want_move_threshold = false;
1616 drag_info.pointer_frame_offset = 0;
1617 drag_info.brushing = false;
1618 drag_info.copied_location = 0;
1620 drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK,
1622 event->button.time);
1624 if (session && session->transport_rolling()) {
1625 drag_info.was_rolling = true;
1627 drag_info.was_rolling = false;
1630 switch (snap_type) {
1631 case SnapToRegionStart:
1632 case SnapToRegionEnd:
1633 case SnapToRegionSync:
1634 case SnapToRegionBoundary:
1635 build_region_boundary_cache ();
1643 Editor::end_grab (ArdourCanvas::Item* item, GdkEvent* event)
1645 bool did_drag = false;
1647 stop_canvas_autoscroll ();
1649 if (drag_info.item == 0) {
1653 drag_info.item->ungrab (event->button.time);
1655 if (drag_info.finished_callback) {
1656 (this->*(drag_info.finished_callback)) (item, event);
1659 did_drag = !drag_info.first_move;
1661 hide_verbose_canvas_cursor();
1664 drag_info.copy = false;
1665 drag_info.motion_callback = 0;
1666 drag_info.finished_callback = 0;
1667 drag_info.last_trackview = 0;
1668 drag_info.last_frame_position = 0;
1669 drag_info.grab_frame = 0;
1670 drag_info.last_pointer_frame = 0;
1671 drag_info.current_pointer_frame = 0;
1672 drag_info.brushing = false;
1674 if (drag_info.copied_location) {
1675 delete drag_info.copied_location;
1676 drag_info.copied_location = 0;
1683 Editor::set_edit_cursor (GdkEvent* event)
1685 jack_nframes_t pointer_frame = event_frame (event);
1687 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1688 if (snap_type != SnapToEditCursor) {
1689 snap_to (pointer_frame);
1693 edit_cursor->set_position (pointer_frame);
1694 edit_cursor_clock.set (pointer_frame);
1698 Editor::set_playhead_cursor (GdkEvent* event)
1700 jack_nframes_t pointer_frame = event_frame (event);
1702 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1703 snap_to (pointer_frame);
1707 session->request_locate (pointer_frame, session->transport_rolling());
1712 Editor::start_fade_in_grab (ArdourCanvas::Item* item, GdkEvent* event)
1714 drag_info.item = item;
1715 drag_info.motion_callback = &Editor::fade_in_drag_motion_callback;
1716 drag_info.finished_callback = &Editor::fade_in_drag_finished_callback;
1720 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
1721 fatal << _("programming error: fade in canvas item has no regionview data pointer!") << endmsg;
1725 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1727 drag_info.pointer_frame_offset = drag_info.grab_frame - ((jack_nframes_t) arv->region.fade_in().back()->when + arv->region.position());
1731 Editor::fade_in_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1733 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1735 jack_nframes_t fade_length;
1737 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1738 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1744 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1748 if (pos < (arv->region.position() + 64)) {
1749 fade_length = 64; // this should be a minimum defined somewhere
1750 } else if (pos > arv->region.last_frame()) {
1751 fade_length = arv->region.length();
1753 fade_length = pos - arv->region.position();
1756 arv->reset_fade_in_shape_width (fade_length);
1758 show_verbose_duration_cursor (arv->region.position(), arv->region.position() + fade_length, 10);
1760 drag_info.first_move = false;
1764 Editor::fade_in_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1766 if (drag_info.first_move) return;
1768 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1770 jack_nframes_t fade_length;
1772 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1773 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1779 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1783 if (pos < (arv->region.position() + 64)) {
1784 fade_length = 64; // this should be a minimum defined somewhere
1786 else if (pos > arv->region.last_frame()) {
1787 fade_length = arv->region.length();
1790 fade_length = pos - arv->region.position();
1793 begin_reversible_command (_("change fade in length"));
1794 session->add_undo (arv->region.get_memento());
1795 arv->region.set_fade_in_length (fade_length);
1796 session->add_redo_no_execute (arv->region.get_memento());
1797 commit_reversible_command ();
1798 fade_in_drag_motion_callback (item, event);
1802 Editor::start_fade_out_grab (ArdourCanvas::Item* item, GdkEvent* event)
1804 drag_info.item = item;
1805 drag_info.motion_callback = &Editor::fade_out_drag_motion_callback;
1806 drag_info.finished_callback = &Editor::fade_out_drag_finished_callback;
1810 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
1811 fatal << _("programming error: fade out canvas item has no regionview data pointer!") << endmsg;
1815 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1817 drag_info.pointer_frame_offset = drag_info.grab_frame - (arv->region.length() - (jack_nframes_t) arv->region.fade_out().back()->when + arv->region.position());
1821 Editor::fade_out_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1823 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1825 jack_nframes_t fade_length;
1827 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1828 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1834 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1838 if (pos > (arv->region.last_frame() - 64)) {
1839 fade_length = 64; // this should really be a minimum fade defined somewhere
1841 else if (pos < arv->region.position()) {
1842 fade_length = arv->region.length();
1845 fade_length = arv->region.last_frame() - pos;
1848 arv->reset_fade_out_shape_width (fade_length);
1850 show_verbose_duration_cursor (arv->region.last_frame() - fade_length, arv->region.last_frame(), 10);
1852 drag_info.first_move = false;
1856 Editor::fade_out_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1858 if (drag_info.first_move) return;
1860 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1862 jack_nframes_t fade_length;
1864 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1865 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1871 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1875 if (pos > (arv->region.last_frame() - 64)) {
1876 fade_length = 64; // this should really be a minimum fade defined somewhere
1878 else if (pos < arv->region.position()) {
1879 fade_length = arv->region.length();
1882 fade_length = arv->region.last_frame() - pos;
1885 begin_reversible_command (_("change fade out length"));
1886 session->add_undo (arv->region.get_memento());
1887 arv->region.set_fade_out_length (fade_length);
1888 session->add_redo_no_execute (arv->region.get_memento());
1889 commit_reversible_command ();
1891 fade_out_drag_motion_callback (item, event);
1895 Editor::start_cursor_grab (ArdourCanvas::Item* item, GdkEvent* event)
1897 drag_info.item = item;
1898 drag_info.motion_callback = &Editor::cursor_drag_motion_callback;
1899 drag_info.finished_callback = &Editor::cursor_drag_finished_callback;
1903 if ((drag_info.data = (item->get_data ("cursor"))) == 0) {
1904 fatal << _("programming error: cursor canvas item has no cursor data pointer!") << endmsg;
1908 Cursor* cursor = (Cursor *) drag_info.data;
1910 if (session && cursor == playhead_cursor) {
1911 if (drag_info.was_rolling) {
1912 session->request_stop ();
1916 drag_info.pointer_frame_offset = drag_info.grab_frame - cursor->current_frame;
1918 show_verbose_time_cursor (cursor->current_frame, 10);
1922 Editor::cursor_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1924 Cursor* cursor = (Cursor *) drag_info.data;
1925 jack_nframes_t adjusted_frame;
1927 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1928 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1934 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1935 if (cursor != edit_cursor || snap_type != SnapToEditCursor) {
1936 snap_to (adjusted_frame);
1940 if (adjusted_frame == drag_info.last_pointer_frame) return;
1942 cursor->set_position (adjusted_frame);
1944 if (cursor == edit_cursor) {
1945 edit_cursor_clock.set (cursor->current_frame);
1948 show_verbose_time_cursor (cursor->current_frame, 10);
1950 drag_info.last_pointer_frame = adjusted_frame;
1951 drag_info.first_move = false;
1955 Editor::cursor_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1957 if (drag_info.first_move) return;
1959 cursor_drag_motion_callback (item, event);
1961 if (item == &playhead_cursor->canvas_item) {
1963 session->request_locate (playhead_cursor->current_frame, drag_info.was_rolling);
1965 } else if (item == &edit_cursor->canvas_item) {
1966 edit_cursor->set_position (edit_cursor->current_frame);
1967 edit_cursor_clock.set (edit_cursor->current_frame);
1972 Editor::update_marker_drag_item (Location *location)
1974 double x1 = frame_to_pixel (location->start());
1975 double x2 = frame_to_pixel (location->end());
1977 if (location->is_mark()) {
1978 marker_drag_line_points.front().set_x(x1);
1979 marker_drag_line_points.back().set_x(x1);
1980 marker_drag_line->property_points() = marker_drag_line_points;
1983 range_marker_drag_rect->property_x1() = x1;
1984 range_marker_drag_rect->property_x2() = x2;
1989 Editor::start_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
1993 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1994 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2000 Location *location = find_location_from_marker (marker, is_start);
2002 drag_info.item = item;
2003 drag_info.data = marker;
2004 drag_info.motion_callback = &Editor::marker_drag_motion_callback;
2005 drag_info.finished_callback = &Editor::marker_drag_finished_callback;
2009 drag_info.copied_location = new Location (*location);
2010 drag_info.pointer_frame_offset = drag_info.grab_frame - (is_start ? location->start() : location->end());
2012 update_marker_drag_item (location);
2014 if (location->is_mark()) {
2015 marker_drag_line->show();
2016 marker_drag_line->raise_to_top();
2019 range_marker_drag_rect->show();
2020 range_marker_drag_rect->raise_to_top();
2023 if (is_start) show_verbose_time_cursor (location->start(), 10);
2024 else show_verbose_time_cursor (location->end(), 10);
2028 Editor::marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2030 jack_nframes_t f_delta;
2031 Marker* marker = (Marker *) drag_info.data;
2032 Location *real_location;
2033 Location *copy_location;
2035 bool move_both = false;
2037 jack_nframes_t newframe;
2038 if (drag_info.pointer_frame_offset <= (long) drag_info.current_pointer_frame) {
2039 newframe = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2045 jack_nframes_t next = newframe;
2047 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2048 snap_to (newframe, 0, true);
2051 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
2053 /* call this to find out if its the start or end */
2055 real_location = find_location_from_marker (marker, is_start);
2057 /* use the copy that we're "dragging" around */
2059 copy_location = drag_info.copied_location;
2061 f_delta = copy_location->end() - copy_location->start();
2063 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
2067 if (is_start) { // start marker
2070 copy_location->set_start (newframe);
2071 copy_location->set_end (newframe + f_delta);
2072 } else if (newframe < copy_location->end()) {
2073 copy_location->set_start (newframe);
2075 snap_to (next, 1, true);
2076 copy_location->set_end (next);
2077 copy_location->set_start (newframe);
2080 } else { // end marker
2083 copy_location->set_end (newframe);
2084 copy_location->set_start (newframe - f_delta);
2085 } else if (newframe > copy_location->start()) {
2086 copy_location->set_end (newframe);
2088 } else if (newframe > 0) {
2089 snap_to (next, -1, true);
2090 copy_location->set_start (next);
2091 copy_location->set_end (newframe);
2095 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
2096 drag_info.first_move = false;
2098 update_marker_drag_item (copy_location);
2100 LocationMarkers* lm = find_location_markers (real_location);
2101 lm->set_position (copy_location->start(), copy_location->end());
2103 show_verbose_time_cursor (newframe, 10);
2107 Editor::marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2109 if (drag_info.first_move) {
2110 marker_drag_motion_callback (item, event);
2114 Marker* marker = (Marker *) drag_info.data;
2119 begin_reversible_command ( _("move marker") );
2120 session->add_undo( session->locations()->get_memento() );
2122 Location * location = find_location_from_marker (marker, is_start);
2125 location->set (drag_info.copied_location->start(), drag_info.copied_location->end());
2128 session->add_redo_no_execute( session->locations()->get_memento() );
2129 commit_reversible_command ();
2131 marker_drag_line->hide();
2132 range_marker_drag_rect->hide();
2136 Editor::start_meter_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2139 MeterMarker* meter_marker;
2141 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2142 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2146 meter_marker = dynamic_cast<MeterMarker*> (marker);
2148 MetricSection& section (meter_marker->meter());
2150 if (!section.movable()) {
2154 drag_info.item = item;
2155 drag_info.data = marker;
2156 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2157 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2161 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2163 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2167 Editor::start_meter_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2170 MeterMarker* meter_marker;
2172 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2173 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2177 meter_marker = dynamic_cast<MeterMarker*> (marker);
2179 // create a dummy marker for visual representation of moving the copy.
2180 // The actual copying is not done before we reach the finish callback.
2182 snprintf (name, sizeof(name), "%g/%g", meter_marker->meter().beats_per_bar(), meter_marker->meter().note_divisor ());
2183 MeterMarker* new_marker = new MeterMarker(*this, *meter_group, color_map[cMeterMarker], name,
2184 *new MeterSection(meter_marker->meter()));
2186 drag_info.item = &new_marker->the_item();
2187 drag_info.copy = true;
2188 drag_info.data = new_marker;
2189 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2190 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2194 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2196 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2200 Editor::meter_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2202 MeterMarker* marker = (MeterMarker *) drag_info.data;
2203 jack_nframes_t adjusted_frame;
2205 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2206 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2212 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2213 snap_to (adjusted_frame);
2216 if (adjusted_frame == drag_info.last_pointer_frame) return;
2218 marker->set_position (adjusted_frame);
2221 drag_info.last_pointer_frame = adjusted_frame;
2222 drag_info.first_move = false;
2224 show_verbose_time_cursor (adjusted_frame, 10);
2228 Editor::meter_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2230 if (drag_info.first_move) return;
2232 meter_marker_drag_motion_callback (drag_info.item, event);
2234 MeterMarker* marker = (MeterMarker *) drag_info.data;
2237 TempoMap& map (session->tempo_map());
2238 map.bbt_time (drag_info.last_pointer_frame, when);
2240 if (drag_info.copy == true) {
2241 begin_reversible_command (_("copy meter mark"));
2242 session->add_undo (map.get_memento());
2243 map.add_meter (marker->meter(), when);
2244 session->add_redo_no_execute (map.get_memento());
2245 commit_reversible_command ();
2247 // delete the dummy marker we used for visual representation of copying.
2248 // a new visual marker will show up automatically.
2251 begin_reversible_command (_("move meter mark"));
2252 session->add_undo (map.get_memento());
2253 map.move_meter (marker->meter(), when);
2254 session->add_redo_no_execute (map.get_memento());
2255 commit_reversible_command ();
2260 Editor::start_tempo_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2263 TempoMarker* tempo_marker;
2265 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2266 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2270 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2271 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2275 MetricSection& section (tempo_marker->tempo());
2277 if (!section.movable()) {
2281 drag_info.item = item;
2282 drag_info.data = marker;
2283 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2284 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2288 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2289 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2293 Editor::start_tempo_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2296 TempoMarker* tempo_marker;
2298 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2299 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2303 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2304 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2308 // create a dummy marker for visual representation of moving the copy.
2309 // The actual copying is not done before we reach the finish callback.
2311 snprintf (name, sizeof (name), "%.2f", tempo_marker->tempo().beats_per_minute());
2312 TempoMarker* new_marker = new TempoMarker(*this, *tempo_group, color_map[cTempoMarker], name,
2313 *new TempoSection(tempo_marker->tempo()));
2315 drag_info.item = &new_marker->the_item();
2316 drag_info.copy = true;
2317 drag_info.data = new_marker;
2318 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2319 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2323 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2325 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2329 Editor::tempo_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2331 TempoMarker* marker = (TempoMarker *) drag_info.data;
2332 jack_nframes_t adjusted_frame;
2334 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2335 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2341 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2342 snap_to (adjusted_frame);
2345 if (adjusted_frame == drag_info.last_pointer_frame) return;
2347 /* OK, we've moved far enough to make it worth actually move the thing. */
2349 marker->set_position (adjusted_frame);
2351 show_verbose_time_cursor (adjusted_frame, 10);
2353 drag_info.last_pointer_frame = adjusted_frame;
2354 drag_info.first_move = false;
2358 Editor::tempo_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2360 if (drag_info.first_move) return;
2362 tempo_marker_drag_motion_callback (drag_info.item, event);
2364 TempoMarker* marker = (TempoMarker *) drag_info.data;
2367 TempoMap& map (session->tempo_map());
2368 map.bbt_time (drag_info.last_pointer_frame, when);
2370 if (drag_info.copy == true) {
2371 begin_reversible_command (_("copy tempo mark"));
2372 session->add_undo (map.get_memento());
2373 map.add_tempo (marker->tempo(), when);
2374 session->add_redo_no_execute (map.get_memento());
2375 commit_reversible_command ();
2377 // delete the dummy marker we used for visual representation of copying.
2378 // a new visual marker will show up automatically.
2381 begin_reversible_command (_("move tempo mark"));
2382 session->add_undo (map.get_memento());
2383 map.move_tempo (marker->tempo(), when);
2384 session->add_redo_no_execute (map.get_memento());
2385 commit_reversible_command ();
2390 Editor::remove_gain_control_point (ArdourCanvas::Item*item, GdkEvent* event)
2392 ControlPoint* control_point;
2394 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2395 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2399 // We shouldn't remove the first or last gain point
2400 if (control_point->line.is_last_point(*control_point) ||
2401 control_point->line.is_first_point(*control_point)) {
2405 control_point->line.remove_point (*control_point);
2409 Editor::remove_control_point (ArdourCanvas::Item*item, GdkEvent* event)
2411 ControlPoint* control_point;
2413 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2414 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2418 control_point->line.remove_point (*control_point);
2422 Editor::start_control_point_grab (ArdourCanvas::Item* item, GdkEvent* event)
2424 ControlPoint* control_point;
2426 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2427 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2431 drag_info.item = item;
2432 drag_info.data = control_point;
2433 drag_info.motion_callback = &Editor::control_point_drag_motion_callback;
2434 drag_info.finished_callback = &Editor::control_point_drag_finished_callback;
2436 start_grab (event, fader_cursor);
2438 control_point->line.start_drag (control_point, 0);
2440 float fraction = 1.0 - (control_point->get_y() / control_point->line.height());
2441 set_verbose_canvas_cursor (control_point->line.get_verbose_cursor_string (fraction),
2442 drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
2444 show_verbose_canvas_cursor ();
2448 Editor::control_point_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2450 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2452 double cx = drag_info.current_pointer_x;
2453 double cy = drag_info.current_pointer_y;
2455 drag_info.cumulative_x_drag = cx - drag_info.grab_x ;
2456 drag_info.cumulative_y_drag = cy - drag_info.grab_y ;
2458 bool x_constrained = false;
2460 if (drag_info.x_constrained) {
2461 if (fabs(drag_info.cumulative_x_drag) < fabs(drag_info.cumulative_y_drag)) {
2462 cx = drag_info.grab_x;
2463 x_constrained = true;
2466 cy = drag_info.grab_y;
2471 cp->line.parent_group().w2i (cx, cy);
2475 cy = min ((double) cp->line.height(), cy);
2477 //translate cx to frames
2478 jack_nframes_t cx_frames = (jack_nframes_t) floor (cx * frames_per_unit);
2480 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier()) && !x_constrained) {
2481 snap_to (cx_frames);
2484 float fraction = 1.0 - (cy / cp->line.height());
2488 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
2494 cp->line.point_drag (*cp, cx_frames , fraction, push);
2496 set_verbose_canvas_cursor_text (cp->line.get_verbose_cursor_string (fraction));
2500 Editor::control_point_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2502 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2503 control_point_drag_motion_callback (item, event);
2504 cp->line.end_drag (cp);
2508 Editor::start_line_grab_from_regionview (ArdourCanvas::Item* item, GdkEvent* event)
2510 switch (mouse_mode) {
2512 start_line_grab (clicked_regionview->get_gain_line(), event);
2520 Editor::start_line_grab_from_line (ArdourCanvas::Item* item, GdkEvent* event)
2524 if ((al = reinterpret_cast<AutomationLine*> (item->get_data ("line"))) == 0) {
2525 fatal << _("programming error: line canvas item has no line pointer!") << endmsg;
2529 start_line_grab (al, event);
2533 Editor::start_line_grab (AutomationLine* line, GdkEvent* event)
2537 jack_nframes_t frame_within_region;
2539 /* need to get x coordinate in terms of parent (TimeAxisItemView)
2543 cx = event->button.x;
2544 cy = event->button.y;
2545 line->parent_group().w2i (cx, cy);
2546 frame_within_region = (jack_nframes_t) floor (cx * frames_per_unit);
2548 if (!line->control_points_adjacent (frame_within_region, current_line_drag_info.before,
2549 current_line_drag_info.after)) {
2550 /* no adjacent points */
2554 drag_info.item = &line->grab_item();
2555 drag_info.data = line;
2556 drag_info.motion_callback = &Editor::line_drag_motion_callback;
2557 drag_info.finished_callback = &Editor::line_drag_finished_callback;
2559 start_grab (event, fader_cursor);
2561 double fraction = 1.0 - (cy / line->height());
2563 line->start_drag (0, fraction);
2565 set_verbose_canvas_cursor (line->get_verbose_cursor_string (fraction),
2566 drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
2567 show_verbose_canvas_cursor ();
2571 Editor::line_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2573 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
2574 double cx = drag_info.current_pointer_x;
2575 double cy = drag_info.current_pointer_y;
2577 line->parent_group().w2i (cx, cy);
2580 fraction = 1.0 - (cy / line->height());
2584 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
2590 line->line_drag (current_line_drag_info.before, current_line_drag_info.after, fraction, push);
2592 set_verbose_canvas_cursor_text (line->get_verbose_cursor_string (fraction));
2596 Editor::line_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2598 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
2599 line_drag_motion_callback (item, event);
2604 Editor::start_region_grab (ArdourCanvas::Item* item, GdkEvent* event)
2606 if (selection->audio_regions.empty() || clicked_regionview == 0) {
2610 drag_info.copy = false;
2611 drag_info.item = item;
2612 drag_info.data = clicked_regionview;
2613 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2614 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2619 TimeAxisView* tvp = clicked_trackview;
2620 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
2622 if (tv && tv->is_audio_track()) {
2623 speed = tv->get_diskstream()->speed();
2626 drag_info.last_frame_position = (jack_nframes_t) (clicked_regionview->region.position() / speed);
2627 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2628 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2629 // we want a move threshold
2630 drag_info.want_move_threshold = true;
2632 show_verbose_time_cursor (drag_info.last_frame_position, 10);
2634 begin_reversible_command (_("move region(s)"));
2638 Editor::start_region_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2640 if (selection->audio_regions.empty() || clicked_regionview == 0) {
2644 /* this is committed in the grab finished callback. */
2646 begin_reversible_command (_("Drag region copy"));
2648 /* duplicate the region(s) */
2650 vector<AudioRegionView*> new_regionviews;
2652 set<Playlist*> affected_playlists;
2653 pair<set<Playlist*>::iterator,bool> insert_result;
2655 for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin(); i != selection->audio_regions.by_layer().end(); ++i) {
2656 AudioRegionView* rv;
2660 Playlist* to_playlist = rv->region.playlist();
2661 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*>(&rv->get_time_axis_view());
2663 insert_result = affected_playlists.insert (to_playlist);
2664 if (insert_result.second) {
2665 session->add_undo (to_playlist->get_memento ());
2668 latest_regionview = 0;
2670 sigc::connection c = atv->view->AudioRegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
2672 /* create a new region with the same name.
2675 AudioRegion* newregion = new AudioRegion (rv->region);
2677 /* if the original region was locked, we don't care */
2679 newregion->set_locked (false);
2681 to_playlist->add_region (*newregion, (jack_nframes_t) (rv->region.position() * atv->get_diskstream()->speed()));
2685 if (latest_regionview) {
2686 new_regionviews.push_back (latest_regionview);
2690 if (new_regionviews.empty()) {
2694 /* reset selection to new regionviews */
2696 selection->set (new_regionviews);
2698 /* reset drag_info data to reflect the fact that we are dragging the copies */
2700 drag_info.data = new_regionviews.front();
2701 drag_info.item = new_regionviews.front()->get_canvas_group ();
2703 drag_info.copy = true;
2704 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2705 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2709 TimeAxisView* tv = &clicked_regionview->get_time_axis_view();
2710 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*>(tv);
2713 if (atv && atv->is_audio_track()) {
2714 speed = atv->get_diskstream()->speed();
2717 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2718 drag_info.last_frame_position = (jack_nframes_t) (clicked_regionview->region.position() / speed);
2719 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2720 // we want a move threshold
2721 drag_info.want_move_threshold = true;
2723 show_verbose_time_cursor (drag_info.last_frame_position, 10);
2725 //begin_reversible_command (_("copy region(s)"));
2729 Editor::start_region_brush_grab (ArdourCanvas::Item* item, GdkEvent* event)
2731 if (selection->audio_regions.empty() || clicked_regionview == 0) {
2735 drag_info.copy = false;
2736 drag_info.item = item;
2737 drag_info.data = clicked_regionview;
2738 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2739 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2744 TimeAxisView* tvp = clicked_trackview;
2745 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
2747 if (tv && tv->is_audio_track()) {
2748 speed = tv->get_diskstream()->speed();
2751 drag_info.last_frame_position = (jack_nframes_t) (clicked_regionview->region.position() / speed);
2752 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2753 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2754 // we want a move threshold
2755 drag_info.want_move_threshold = true;
2756 drag_info.brushing = true;
2758 begin_reversible_command (_("Drag region brush"));
2762 Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2766 AudioRegionView *rv = reinterpret_cast<AudioRegionView*> (drag_info.data);
2767 jack_nframes_t pending_region_position = 0;
2768 int32_t pointer_y_span = 0, canvas_pointer_y_span = 0, original_pointer_order;
2769 int32_t visible_y_high = 0, visible_y_low = 512; //high meaning higher numbered.. not the height on the screen
2770 bool clamp_y_axis = false;
2771 vector<int32_t> height_list(512) ;
2772 vector<int32_t>::iterator j;
2774 /* Which trackview is this ? */
2776 TimeAxisView* tvp = trackview_by_y_position (drag_info.current_pointer_y);
2777 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
2779 /* The region motion is only processed if the pointer is over
2783 if (!tv || !tv->is_audio_track()) {
2784 /* To make sure we hide the verbose canvas cursor when the mouse is
2785 not held over and audiotrack.
2787 hide_verbose_canvas_cursor ();
2791 original_pointer_order = drag_info.last_trackview->order;
2793 /************************************************************
2795 ************************************************************/
2797 if (drag_info.brushing) {
2798 clamp_y_axis = true;
2803 if ((pointer_y_span = (drag_info.last_trackview->order - tv->order)) != 0) {
2805 int32_t children = 0, numtracks = 0;
2806 // XXX hard coding track limit, oh my, so very very bad
2807 bitset <1024> tracks (0x00);
2808 /* get a bitmask representing the visible tracks */
2810 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2811 TimeAxisView *tracklist_timeview;
2812 tracklist_timeview = (*i);
2813 AudioTimeAxisView* atv2 = dynamic_cast<AudioTimeAxisView*>(tracklist_timeview);
2814 list<TimeAxisView*> children_list;
2816 /* zeroes are audio tracks. ones are other types. */
2818 if (!atv2->hidden()) {
2820 if (visible_y_high < atv2->order) {
2821 visible_y_high = atv2->order;
2823 if (visible_y_low > atv2->order) {
2824 visible_y_low = atv2->order;
2827 if (!atv2->is_audio_track()) {
2828 tracks = tracks |= (0x01 << atv2->order);
2831 height_list[atv2->order] = (*i)->height;
2833 if ((children_list = atv2->get_child_list()).size() > 0) {
2834 for (list<TimeAxisView*>::iterator j = children_list.begin(); j != children_list.end(); ++j) {
2835 tracks = tracks |= (0x01 << (atv2->order + children));
2836 height_list[atv2->order + children] = (*j)->height;
2844 /* find the actual span according to the canvas */
2846 canvas_pointer_y_span = pointer_y_span;
2847 if (drag_info.last_trackview->order >= tv->order) {
2849 for (y = tv->order; y < drag_info.last_trackview->order; y++) {
2850 if (height_list[y] == 0 ) {
2851 canvas_pointer_y_span--;
2856 for (y = drag_info.last_trackview->order;y <= tv->order; y++) {
2857 if ( height_list[y] == 0 ) {
2858 canvas_pointer_y_span++;
2863 for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin(); i != selection->audio_regions.by_layer().end(); ++i) {
2864 AudioRegionView* rv2;
2866 double ix1, ix2, iy1, iy2;
2869 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
2870 rv2->get_canvas_group()->i2w (ix1, iy1);
2871 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
2872 AudioTimeAxisView* atv2 = dynamic_cast<AudioTimeAxisView*>(tvp2);
2874 if (atv2->order != original_pointer_order) {
2875 /* this isn't the pointer track */
2877 if (canvas_pointer_y_span > 0) {
2879 /* moving up the canvas */
2880 if ((atv2->order - canvas_pointer_y_span) >= visible_y_low) {
2882 int32_t visible_tracks = 0;
2883 while (visible_tracks < canvas_pointer_y_span ) {
2886 while (height_list[atv2->order - (visible_tracks - n)] == 0) {
2887 /* we're passing through a hidden track */
2892 if (tracks[atv2->order - (canvas_pointer_y_span - n)] != 0x00) {
2893 clamp_y_axis = true;
2897 clamp_y_axis = true;
2900 } else if (canvas_pointer_y_span < 0) {
2902 /*moving down the canvas*/
2904 if ((atv2->order - (canvas_pointer_y_span - n)) <= visible_y_high) { // we will overflow
2907 int32_t visible_tracks = 0;
2909 while (visible_tracks > canvas_pointer_y_span ) {
2912 while (height_list[atv2->order - (visible_tracks - n)] == 0) {
2916 if ( tracks[atv2->order - ( canvas_pointer_y_span - n)] != 0x00) {
2917 clamp_y_axis = true;
2922 clamp_y_axis = true;
2928 /* this is the pointer's track */
2929 if ((atv2->order - pointer_y_span) > visible_y_high) { // we will overflow
2930 clamp_y_axis = true;
2931 } else if ((atv2->order - pointer_y_span) < visible_y_low) { // we will underflow
2932 clamp_y_axis = true;
2940 } else if (drag_info.last_trackview == tv) {
2941 clamp_y_axis = true;
2945 if (!clamp_y_axis) {
2946 drag_info.last_trackview = tv;
2949 /************************************************************
2951 ************************************************************/
2953 /* compute the amount of pointer motion in frames, and where
2954 the region would be if we moved it by that much.
2957 if (drag_info.move_threshold_passsed) {
2959 if ((int32_t)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2961 jack_nframes_t sync_frame;
2962 jack_nframes_t sync_offset;
2965 pending_region_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2967 sync_offset = rv->region.sync_offset (sync_dir);
2968 sync_frame = rv->region.adjust_to_sync (pending_region_position);
2970 /* we snap if the snap modifier is not enabled.
2973 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2974 snap_to (sync_frame);
2977 if (sync_frame - sync_offset <= sync_frame) {
2978 pending_region_position = sync_frame - (sync_dir*sync_offset);
2980 pending_region_position = 0;
2984 pending_region_position = 0;
2987 if (pending_region_position > max_frames - rv->region.length()) {
2988 pending_region_position = drag_info.last_frame_position;
2991 // printf ("3: pending_region_position= %lu %lu\n", pending_region_position, drag_info.last_frame_position );
2993 if (pending_region_position != drag_info.last_frame_position && !drag_info.x_constrained) {
2995 /* now compute the canvas unit distance we need to move the regiondrag_info.last_trackview->order
2996 to make it appear at the new location.
2999 if (pending_region_position > drag_info.last_frame_position) {
3000 x_delta = ((double) (pending_region_position - drag_info.last_frame_position) / frames_per_unit);
3002 x_delta = -((double) (drag_info.last_frame_position - pending_region_position) / frames_per_unit);
3005 drag_info.last_frame_position = pending_region_position;
3012 /* threshold not passed */
3017 /*************************************************************
3019 ************************************************************/
3021 if (x_delta == 0 && (pointer_y_span == 0)) {
3022 /* haven't reached next snap point, and we're not switching
3023 trackviews. nothing to do.
3029 for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin(); i != selection->audio_regions.by_layer().end(); ++i) {
3031 AudioRegionView* rv2;
3034 /* if any regionview is at zero, we need to know so we can
3035 stop further leftward motion.
3038 double ix1, ix2, iy1, iy2;
3039 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3040 rv2->get_canvas_group()->i2w (ix1, iy1);
3049 /*************************************************************
3051 ************************************************************/
3053 pair<set<Playlist*>::iterator,bool> insert_result;
3054 const list<AudioRegionView*>& layered_regions = selection->audio_regions.by_layer();
3056 for (list<AudioRegionView*>::const_iterator i = layered_regions.begin(); i != layered_regions.end(); ++i) {
3058 AudioRegionView* rv;
3060 double ix1, ix2, iy1, iy2;
3061 int32_t temp_pointer_y_span = pointer_y_span;
3063 /* get item BBox, which will be relative to parent. so we have
3064 to query on a child, then convert to world coordinates using
3068 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3069 rv->get_canvas_group()->i2w (ix1, iy1);
3070 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3071 AudioTimeAxisView* canvas_atv = dynamic_cast<AudioTimeAxisView*>(tvp2);
3072 AudioTimeAxisView* temp_atv;
3074 if ((pointer_y_span != 0) && !clamp_y_axis) {
3077 for (j = height_list.begin(); j!= height_list.end(); j++) {
3078 if (x == canvas_atv->order) {
3079 /* we found the track the region is on */
3080 if (x != original_pointer_order) {
3081 /*this isn't from the same track we're dragging from */
3082 temp_pointer_y_span = canvas_pointer_y_span;
3084 while (temp_pointer_y_span > 0) {
3085 /* we're moving up canvas-wise,
3086 so we need to find the next track height
3088 if (j != height_list.begin()) {
3091 if (x != original_pointer_order) {
3092 /* we're not from the dragged track, so ignore hidden tracks. */
3094 temp_pointer_y_span++;
3098 temp_pointer_y_span--;
3100 while (temp_pointer_y_span < 0) {
3102 if (x != original_pointer_order) {
3104 temp_pointer_y_span--;
3108 if (j != height_list.end()) {
3111 temp_pointer_y_span++;
3113 /* find out where we'll be when we move and set height accordingly */
3115 tvp2 = trackview_by_y_position (iy1 + y_delta);
3116 temp_atv = dynamic_cast<AudioTimeAxisView*>(tvp2);
3117 rv->set_height (temp_atv->height);
3119 /* if you un-comment the following, the region colours will follow the track colours whilst dragging,
3120 personally, i think this can confuse things, but never mind.
3123 //const GdkColor& col (temp_atv->view->get_region_color());
3124 //rv->set_color (const_cast<GdkColor&>(col));
3131 /* prevent the regionview from being moved to before
3132 the zero position on the canvas.
3137 if (-x_delta > ix1) {
3140 } else if ((x_delta > 0) &&(rv->region.last_frame() > max_frames - x_delta)) {
3141 x_delta = max_frames - rv->region.last_frame();
3144 if (drag_info.first_move) {
3146 /* hide any dependent views */
3148 // rv->get_time_axis_view().hide_dependent_views (*rv);
3150 /* this is subtle. raising the regionview itself won't help,
3151 because raise_to_top() just puts the item on the top of
3152 its parent's stack. so, we need to put the trackview canvas_display group
3153 on the top, since its parent is the whole canvas.
3156 rv->get_canvas_group()->raise_to_top();
3157 rv->get_time_axis_view().canvas_display->raise_to_top();
3158 cursor_group->raise_to_top();
3160 /* freeze the playlists from notifying till
3164 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*> (&rv->get_time_axis_view());
3165 if (atv && atv->is_audio_track()) {
3166 AudioPlaylist* pl = atv->get_diskstream()->playlist();
3168 /* only freeze and capture state once */
3170 insert_result = motion_frozen_playlists.insert (pl);
3171 if (insert_result.second) {
3173 session->add_undo(pl->get_memento());
3179 if (drag_info.brushing) {
3180 mouse_brush_insert_region (rv, pending_region_position);
3182 rv->move (x_delta, y_delta);
3186 if (drag_info.first_move) {
3187 cursor_group->raise_to_top();
3190 drag_info.first_move = false;
3192 if (x_delta != 0 && !drag_info.brushing) {
3193 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3199 Editor::region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3201 jack_nframes_t where;
3202 AudioRegionView* rv = reinterpret_cast<AudioRegionView *> (drag_info.data);
3203 pair<set<Playlist*>::iterator,bool> insert_result;
3204 bool nocommit = true;
3206 AudioTimeAxisView* atv;
3207 bool regionview_y_movement;
3208 bool regionview_x_movement;
3210 /* first_move is set to false if the regionview has been moved in the
3214 if (drag_info.first_move) {
3221 /* The regionview has been moved at some stage during the grab so we need
3222 to account for any mouse movement between this event and the last one.
3225 region_drag_motion_callback (item, event);
3227 if (drag_info.brushing) {
3228 /* all changes were made during motion event handlers */
3232 /* adjust for track speed */
3235 atv = dynamic_cast<AudioTimeAxisView*> (drag_info.last_trackview);
3236 if (atv && atv->get_diskstream()) {
3237 speed = atv->get_diskstream()->speed();
3240 regionview_x_movement = (drag_info.last_frame_position != (jack_nframes_t) (rv->region.position()/speed));
3241 regionview_y_movement = (drag_info.last_trackview != &rv->get_time_axis_view());
3243 //printf ("last_frame: %s position is %lu %g\n", rv->get_time_axis_view().name().c_str(), drag_info.last_frame_position, speed);
3244 //printf ("last_rackview: %s \n", drag_info.last_trackview->name().c_str());
3246 if (regionview_y_movement) {
3248 /* motion between tracks */
3250 list<AudioRegionView*> new_selection;
3252 /* moved to a different audio track. */
3254 for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin(); i != selection->audio_regions.by_layer().end(); ) {
3256 AudioRegionView* rv2 = (*i);
3258 /* the region that used to be in the old playlist is not
3259 moved to the new one - we make a copy of it. as a result,
3260 any existing editor for the region should no longer be
3264 if (!drag_info.copy) {
3265 rv2->hide_region_editor();
3267 new_selection.push_back (rv2);
3271 /* first, freeze the target tracks */
3273 for (list<AudioRegionView*>::const_iterator i = new_selection.begin(); i != new_selection.end();i++ ) {
3275 Playlist* from_playlist;
3276 Playlist* to_playlist;
3278 double ix1, ix2, iy1, iy2;
3280 (*i)->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3281 (*i)->get_canvas_group()->i2w (ix1, iy1);
3282 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3283 AudioTimeAxisView* atv2 = dynamic_cast<AudioTimeAxisView*>(tvp2);
3285 from_playlist = (*i)->region.playlist();
3286 to_playlist = atv2->playlist();
3288 /* the from_playlist was frozen in the "first_move" case
3289 of the motion handler. the insert can fail,
3290 but that doesn't matter. it just means
3291 we already have the playlist in the list.
3294 motion_frozen_playlists.insert (from_playlist);
3296 /* only freeze the to_playlist once */
3298 insert_result = motion_frozen_playlists.insert(to_playlist);
3299 if (insert_result.second) {
3300 to_playlist->freeze();
3301 session->add_undo(to_playlist->get_memento());
3306 /* now do it again with the actual operations */
3308 for (list<AudioRegionView*>::const_iterator i = new_selection.begin(); i != new_selection.end();i++ ) {
3310 Playlist* from_playlist;
3311 Playlist* to_playlist;
3313 double ix1, ix2, iy1, iy2;
3315 (*i)->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3316 (*i)->get_canvas_group()->i2w (ix1, iy1);
3317 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3318 AudioTimeAxisView* atv2 = dynamic_cast<AudioTimeAxisView*>(tvp2);
3320 from_playlist = (*i)->region.playlist();
3321 to_playlist = atv2->playlist();
3323 latest_regionview = 0;
3325 where = (jack_nframes_t) (unit_to_frame (ix1) * speed);
3326 Region* new_region = createRegion ((*i)->region);
3328 from_playlist->remove_region (&((*i)->region));
3330 sigc::connection c = atv2->view->AudioRegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3331 to_playlist->add_region (*new_region, where);
3334 if (latest_regionview) {
3335 selection->add (latest_regionview);
3341 /* motion within a single track */
3343 for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin(); i != selection->audio_regions.by_layer().end(); ++i) {
3347 if (rv->region.locked()) {
3351 if (regionview_x_movement) {
3352 double ownspeed = 1.0;
3353 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*> (&(rv->get_time_axis_view()));
3355 if (atv && atv->get_diskstream()) {
3356 ownspeed = atv->get_diskstream()->speed();
3359 /* base the new region position on the current position of the regionview.*/
3361 double ix1, ix2, iy1, iy2;
3363 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3364 rv->get_canvas_group()->i2w (ix1, iy1);
3365 where = (jack_nframes_t) (unit_to_frame (ix1) * ownspeed);
3369 where = rv->region.position();
3372 rv->get_time_axis_view().reveal_dependent_views (*rv);
3374 /* no need to add an undo here, we did that when we added this playlist to motion_frozen playlists */
3376 rv->region.set_position (where, (void *) this);
3381 for (set<Playlist*>::iterator p = motion_frozen_playlists.begin(); p != motion_frozen_playlists.end(); ++p) {
3383 session->add_redo_no_execute ((*p)->get_memento());
3386 motion_frozen_playlists.clear ();
3389 commit_reversible_command ();
3394 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
3396 /* Either add to or set the set the region selection, unless
3397 this is an alignment click (control used)
3400 if (Keyboard::modifier_state_contains (event->state, Keyboard::Control)) {
3401 TimeAxisView* tv = &rv.get_time_axis_view();
3402 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*>(tv);
3404 if (atv && atv->is_audio_track()) {
3405 speed = atv->get_diskstream()->speed();
3408 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Alt))) {
3410 align_region (rv.region, SyncPoint, (jack_nframes_t) (edit_cursor->current_frame * speed));
3412 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Shift))) {
3414 align_region (rv.region, End, (jack_nframes_t) (edit_cursor->current_frame * speed));
3418 align_region (rv.region, Start, (jack_nframes_t) (edit_cursor->current_frame * speed));
3424 Editor::show_verbose_time_cursor (jack_nframes_t frame, double offset, double xpos, double ypos)
3435 switch (ARDOUR_UI::instance()->secondary_clock.mode ()) {
3436 case AudioClock::BBT:
3437 session->bbt_time (frame, bbt);
3438 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, bbt.bars, bbt.beats, bbt.ticks);
3441 case AudioClock::SMPTE:
3442 session->smpte_time (frame, smpte);
3443 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
3446 case AudioClock::MinSec:
3447 /* XXX fix this to compute min/sec properly */
3448 session->smpte_time (frame, smpte);
3449 secs = smpte.seconds + ((float) smpte.frames / session->smpte_frames_per_second);
3450 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", smpte.hours, smpte.minutes, secs);
3454 snprintf (buf, sizeof(buf), "%u", frame);
3458 if (xpos >= 0 && ypos >=0) {
3459 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
3462 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
3464 show_verbose_canvas_cursor ();
3468 Editor::show_verbose_duration_cursor (jack_nframes_t start, jack_nframes_t end, double offset, double xpos, double ypos)
3475 Meter meter_at_start(session->tempo_map().meter_at(start));
3481 switch (ARDOUR_UI::instance()->secondary_clock.mode ()) {
3482 case AudioClock::BBT:
3483 session->bbt_time (start, sbbt);
3484 session->bbt_time (end, ebbt);
3487 /* XXX this computation won't work well if the
3488 user makes a selection that spans any meter changes.
3491 ebbt.bars -= sbbt.bars;
3492 if (ebbt.beats >= sbbt.beats) {
3493 ebbt.beats -= sbbt.beats;
3496 ebbt.beats = int(meter_at_start.beats_per_bar()) + ebbt.beats - sbbt.beats;
3498 if (ebbt.ticks >= sbbt.ticks) {
3499 ebbt.ticks -= sbbt.ticks;
3502 ebbt.ticks = int(Meter::ticks_per_beat) + ebbt.ticks - sbbt.ticks;
3505 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, ebbt.bars, ebbt.beats, ebbt.ticks);
3508 case AudioClock::SMPTE:
3509 session->smpte_duration (end - start, smpte);
3510 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
3513 case AudioClock::MinSec:
3514 /* XXX fix this to compute min/sec properly */
3515 session->smpte_duration (end - start, smpte);
3516 secs = smpte.seconds + ((float) smpte.frames / session->smpte_frames_per_second);
3517 snprintf (buf, sizeof (buf), "%02ld:%02ld:%.4f", smpte.hours, smpte.minutes, secs);
3521 snprintf (buf, sizeof(buf), "%u", end - start);
3525 if (xpos >= 0 && ypos >=0) {
3526 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
3529 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
3531 show_verbose_canvas_cursor ();
3535 Editor::collect_new_region_view (AudioRegionView* rv)
3537 latest_regionview = rv;
3541 Editor::start_selection_grab (ArdourCanvas::Item* item, GdkEvent* event)
3543 if (clicked_regionview == 0) {
3547 /* lets try to create new Region for the selection */
3549 vector<AudioRegion*> new_regions;
3550 create_region_from_selection (new_regions);
3552 if (new_regions.empty()) {
3556 /* XXX fix me one day to use all new regions */
3558 Region* region = new_regions.front();
3560 /* add it to the current stream/playlist.
3562 tricky: the streamview for the track will add a new regionview. we will
3563 catch the signal it sends when it creates the regionview to
3564 set the regionview we want to then drag.
3567 latest_regionview = 0;
3568 sigc::connection c = clicked_audio_trackview->view->AudioRegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3570 /* A selection grab currently creates two undo/redo operations, one for
3571 creating the new region and another for moving it.
3574 begin_reversible_command (_("selection grab"));
3576 Playlist* playlist = clicked_trackview->playlist();
3578 session->add_undo (playlist->get_memento ());
3579 clicked_trackview->playlist()->add_region (*region, selection->time[clicked_selection].start);
3580 session->add_redo_no_execute (playlist->get_memento ());
3582 commit_reversible_command ();
3586 if (latest_regionview == 0) {
3587 /* something went wrong */
3591 /* we need to deselect all other regionviews, and select this one
3592 i'm ignoring undo stuff, because the region creation will take care of it */
3593 selection->set (latest_regionview);
3595 drag_info.item = latest_regionview->get_canvas_group();
3596 drag_info.data = latest_regionview;
3597 drag_info.motion_callback = &Editor::region_drag_motion_callback;
3598 drag_info.finished_callback = &Editor::region_drag_finished_callback;
3602 drag_info.last_trackview = clicked_trackview;
3603 drag_info.last_frame_position = latest_regionview->region.position();
3604 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
3606 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3610 Editor::cancel_selection ()
3612 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
3613 (*i)->hide_selection ();
3615 begin_reversible_command (_("cancel selection"));
3616 selection->clear ();
3617 clicked_selection = 0;
3618 commit_reversible_command ();
3622 Editor::start_selection_op (ArdourCanvas::Item* item, GdkEvent* event, SelectionOp op)
3624 jack_nframes_t start = 0;
3625 jack_nframes_t end = 0;
3631 drag_info.item = item;
3632 drag_info.motion_callback = &Editor::drag_selection;
3633 drag_info.finished_callback = &Editor::end_selection_op;
3638 case CreateSelection:
3640 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
3641 drag_info.copy = true;
3643 drag_info.copy = false;
3645 start_grab (event, selector_cursor);
3648 case SelectionStartTrim:
3649 clicked_trackview->order_selection_trims (item, true);
3650 start_grab (event, trimmer_cursor);
3651 start = selection->time[clicked_selection].start;
3652 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
3655 case SelectionEndTrim:
3656 clicked_trackview->order_selection_trims (item, false);
3657 start_grab (event, trimmer_cursor);
3658 end = selection->time[clicked_selection].end;
3659 drag_info.pointer_frame_offset = drag_info.grab_frame - end;
3663 start = selection->time[clicked_selection].start;
3665 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
3669 if (selection_op == SelectionMove) {
3670 show_verbose_time_cursor(start, 10);
3672 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
3677 Editor::drag_selection (ArdourCanvas::Item* item, GdkEvent* event)
3679 jack_nframes_t start = 0;
3680 jack_nframes_t end = 0;
3681 jack_nframes_t length;
3682 jack_nframes_t pending_position;
3684 if ((int32_t) drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
3685 pending_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
3688 pending_position = 0;
3691 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3692 snap_to (pending_position);
3695 /* only alter selection if the current frame is
3696 different from the last frame position (adjusted)
3699 if (pending_position == drag_info.last_pointer_frame) return;
3701 switch (selection_op) {
3702 case CreateSelection:
3704 if (drag_info.first_move) {
3705 snap_to (drag_info.grab_frame);
3708 if (pending_position < drag_info.grab_frame) {
3709 start = pending_position;
3710 end = drag_info.grab_frame;
3712 end = pending_position;
3713 start = drag_info.grab_frame;
3716 /* first drag: Either add to the selection
3717 or create a new selection->
3720 if (drag_info.first_move) {
3722 begin_reversible_command (_("range selection"));
3724 if (drag_info.copy) {
3725 /* adding to the selection */
3726 clicked_selection = selection->add (start, end);
3727 drag_info.copy = false;
3729 /* new selection-> */
3730 clicked_selection = selection->set (clicked_trackview, start, end);
3735 case SelectionStartTrim:
3737 if (drag_info.first_move) {
3738 begin_reversible_command (_("trim selection start"));
3741 start = selection->time[clicked_selection].start;
3742 end = selection->time[clicked_selection].end;
3744 if (pending_position > end) {
3747 start = pending_position;
3751 case SelectionEndTrim:
3753 if (drag_info.first_move) {
3754 begin_reversible_command (_("trim selection end"));
3757 start = selection->time[clicked_selection].start;
3758 end = selection->time[clicked_selection].end;
3760 if (pending_position < start) {
3763 end = pending_position;
3770 if (drag_info.first_move) {
3771 begin_reversible_command (_("move selection"));
3774 start = selection->time[clicked_selection].start;
3775 end = selection->time[clicked_selection].end;
3777 length = end - start;
3779 start = pending_position;
3782 end = start + length;
3787 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
3788 start_canvas_autoscroll (1);
3792 selection->replace (clicked_selection, start, end);
3795 drag_info.last_pointer_frame = pending_position;
3796 drag_info.first_move = false;
3798 if (selection_op == SelectionMove) {
3799 show_verbose_time_cursor(start, 10);
3801 show_verbose_time_cursor(pending_position, 10);
3806 Editor::end_selection_op (ArdourCanvas::Item* item, GdkEvent* event)
3808 if (!drag_info.first_move) {
3809 drag_selection (item, event);
3810 /* XXX this is not object-oriented programming at all. ick */
3811 if (selection->time.consolidate()) {
3812 selection->TimeChanged ();
3814 commit_reversible_command ();
3816 /* just a click, no pointer movement.*/
3818 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
3820 selection->clear_time();
3825 /* XXX what happens if its a music selection? */
3826 session->set_audio_range (selection->time);
3827 stop_canvas_autoscroll ();
3831 Editor::start_trim (ArdourCanvas::Item* item, GdkEvent* event)
3834 TimeAxisView* tvp = clicked_trackview;
3835 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
3837 if (tv && tv->is_audio_track()) {
3838 speed = tv->get_diskstream()->speed();
3841 jack_nframes_t region_start = (jack_nframes_t) (clicked_regionview->region.position() / speed);
3842 jack_nframes_t region_end = (jack_nframes_t) (clicked_regionview->region.last_frame() / speed);
3843 jack_nframes_t region_length = (jack_nframes_t) (clicked_regionview->region.length() / speed);
3845 motion_frozen_playlists.clear();
3847 //drag_info.item = clicked_regionview->get_name_highlight();
3848 drag_info.item = item;
3849 drag_info.motion_callback = &Editor::trim_motion_callback;
3850 drag_info.finished_callback = &Editor::trim_finished_callback;
3852 start_grab (event, trimmer_cursor);
3854 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
3855 trim_op = ContentsTrim;
3857 /* These will get overridden for a point trim.*/
3858 if (drag_info.current_pointer_frame < (region_start + region_length/2)) {
3859 /* closer to start */
3860 trim_op = StartTrim;
3861 } else if (drag_info.current_pointer_frame > (region_end - region_length/2)) {
3869 show_verbose_time_cursor(region_start, 10);
3872 show_verbose_time_cursor(region_end, 10);
3875 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
3881 Editor::trim_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
3883 AudioRegionView* rv = clicked_regionview;
3884 jack_nframes_t frame_delta = 0;
3885 bool left_direction;
3886 bool obey_snap = !Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier());
3888 /* snap modifier works differently here..
3889 its' current state has to be passed to the
3890 various trim functions in order to work properly
3894 TimeAxisView* tvp = clicked_trackview;
3895 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
3896 pair<set<Playlist*>::iterator,bool> insert_result;
3898 if (tv && tv->is_audio_track()) {
3899 speed = tv->get_diskstream()->speed();
3902 if (drag_info.last_pointer_frame > drag_info.current_pointer_frame) {
3903 left_direction = true;
3905 left_direction = false;
3909 snap_to (drag_info.current_pointer_frame);
3912 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
3916 if (drag_info.first_move) {
3922 trim_type = "Region start trim";
3925 trim_type = "Region end trim";
3928 trim_type = "Region content trim";
3932 begin_reversible_command (trim_type);
3934 for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin(); i != selection->audio_regions.by_layer().end(); ++i) {
3935 (*i)->region.freeze ();
3936 (*i)->temporarily_hide_envelope ();
3938 Playlist * pl = (*i)->region.playlist();
3939 insert_result = motion_frozen_playlists.insert (pl);
3940 if (insert_result.second) {
3941 session->add_undo (pl->get_memento());
3946 if (left_direction) {
3947 frame_delta = (drag_info.last_pointer_frame - drag_info.current_pointer_frame);
3949 frame_delta = (drag_info.current_pointer_frame - drag_info.last_pointer_frame);
3954 if ((left_direction == false) && (drag_info.current_pointer_frame <= rv->region.first_frame()/speed)) {
3957 for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin(); i != selection->audio_regions.by_layer().end(); ++i) {
3958 single_start_trim (**i, frame_delta, left_direction, obey_snap);
3964 if ((left_direction == true) && (drag_info.current_pointer_frame > (jack_nframes_t) (rv->region.last_frame()/speed))) {
3967 for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin(); i != selection->audio_regions.by_layer().end(); ++i) {
3968 single_end_trim (**i, frame_delta, left_direction, obey_snap);
3975 bool swap_direction = false;
3977 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
3978 swap_direction = true;
3981 for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin();
3982 i != selection->audio_regions.by_layer().end(); ++i)
3984 single_contents_trim (**i, frame_delta, left_direction, swap_direction, obey_snap);
3992 show_verbose_time_cursor((jack_nframes_t) (rv->region.position()/speed), 10);
3995 show_verbose_time_cursor((jack_nframes_t) (rv->region.last_frame()/speed), 10);
3998 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4002 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4003 drag_info.first_move = false;
4007 Editor::single_contents_trim (AudioRegionView& rv, jack_nframes_t frame_delta, bool left_direction, bool swap_direction, bool obey_snap)
4009 Region& region (rv.region);
4011 if (region.locked()) {
4015 jack_nframes_t new_bound;
4018 TimeAxisView* tvp = clicked_trackview;
4019 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
4021 if (tv && tv->is_audio_track()) {
4022 speed = tv->get_diskstream()->speed();
4025 if (left_direction) {
4026 if (swap_direction) {
4027 new_bound = (jack_nframes_t) (region.position()/speed) + frame_delta;
4029 new_bound = (jack_nframes_t) (region.position()/speed) - frame_delta;
4032 if (swap_direction) {
4033 new_bound = (jack_nframes_t) (region.position()/speed) - frame_delta;
4035 new_bound = (jack_nframes_t) (region.position()/speed) + frame_delta;
4040 snap_to (new_bound);
4042 region.trim_start ((jack_nframes_t) (new_bound * speed), this);
4043 rv.region_changed (StartChanged);
4047 Editor::single_start_trim (AudioRegionView& rv, jack_nframes_t frame_delta, bool left_direction, bool obey_snap)
4049 Region& region (rv.region);
4051 if (region.locked()) {
4055 jack_nframes_t new_bound;
4058 TimeAxisView* tvp = clicked_trackview;
4059 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
4061 if (tv && tv->is_audio_track()) {
4062 speed = tv->get_diskstream()->speed();
4065 if (left_direction) {
4066 new_bound = (jack_nframes_t) (region.position()/speed) - frame_delta;
4068 new_bound = (jack_nframes_t) (region.position()/speed) + frame_delta;
4072 snap_to (new_bound, (left_direction ? 0 : 1));
4075 region.trim_front ((jack_nframes_t) (new_bound * speed), this);
4077 rv.region_changed (Change (LengthChanged|PositionChanged|StartChanged));
4081 Editor::single_end_trim (AudioRegionView& rv, jack_nframes_t frame_delta, bool left_direction, bool obey_snap)
4083 Region& region (rv.region);
4085 if (region.locked()) {
4089 jack_nframes_t new_bound;
4092 TimeAxisView* tvp = clicked_trackview;
4093 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
4095 if (tv && tv->is_audio_track()) {
4096 speed = tv->get_diskstream()->speed();
4099 if (left_direction) {
4100 new_bound = (jack_nframes_t) ((region.last_frame() + 1)/speed) - frame_delta;
4102 new_bound = (jack_nframes_t) ((region.last_frame() + 1)/speed) + frame_delta;
4106 snap_to (new_bound);
4108 region.trim_end ((jack_nframes_t) (new_bound * speed), this);
4109 rv.region_changed (LengthChanged);
4113 Editor::trim_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
4115 if (!drag_info.first_move) {
4116 trim_motion_callback (item, event);
4118 if (!clicked_regionview->get_selected()) {
4119 thaw_region_after_trim (*clicked_regionview);
4122 for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin();
4123 i != selection->audio_regions.by_layer().end(); ++i)
4125 thaw_region_after_trim (**i);
4129 for (set<Playlist*>::iterator p = motion_frozen_playlists.begin(); p != motion_frozen_playlists.end(); ++p) {
4131 session->add_redo_no_execute ((*p)->get_memento());
4134 motion_frozen_playlists.clear ();
4136 commit_reversible_command();
4138 /* no mouse movement */
4144 Editor::point_trim (GdkEvent* event)
4146 AudioRegionView* rv = clicked_regionview;
4147 jack_nframes_t new_bound = drag_info.current_pointer_frame;
4149 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4150 snap_to (new_bound);
4153 /* Choose action dependant on which button was pressed */
4154 switch (event->button.button) {
4156 trim_op = StartTrim;
4157 begin_reversible_command (_("Start point trim"));
4159 if (rv->get_selected()) {
4161 for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin();
4162 i != selection->audio_regions.by_layer().end(); ++i)
4164 if (!(*i)->region.locked()) {
4165 session->add_undo ((*i)->region.playlist()->get_memento());
4166 (*i)->region.trim_front (new_bound, this);
4167 session->add_redo_no_execute ((*i)->region.playlist()->get_memento());
4173 if (!rv->region.locked()) {
4174 session->add_undo (rv->region.playlist()->get_memento());
4175 rv->region.trim_front (new_bound, this);
4176 session->add_redo_no_execute (rv->region.playlist()->get_memento());
4180 commit_reversible_command();
4185 begin_reversible_command (_("End point trim"));
4187 if (rv->get_selected()) {
4189 for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin(); i != selection->audio_regions.by_layer().end(); ++i)
4191 if (!(*i)->region.locked()) {
4192 session->add_undo ((*i)->region.playlist()->get_memento());
4193 (*i)->region.trim_end (new_bound, this);
4194 session->add_redo_no_execute ((*i)->region.playlist()->get_memento());
4200 if (!rv->region.locked()) {
4201 session->add_undo (rv->region.playlist()->get_memento());
4202 rv->region.trim_end (new_bound, this);
4203 session->add_redo_no_execute (rv->region.playlist()->get_memento());
4207 commit_reversible_command();
4216 Editor::thaw_region_after_trim (AudioRegionView& rv)
4218 Region& region (rv.region);
4220 if (region.locked()) {
4224 region.thaw (_("trimmed region"));
4225 session->add_redo_no_execute (region.playlist()->get_memento());
4227 rv.unhide_envelope ();
4231 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* event)
4236 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
4237 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
4241 Location* location = find_location_from_marker (marker, is_start);
4242 location->set_hidden (true, this);
4247 Editor::start_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event, RangeMarkerOp op)
4254 drag_info.item = item;
4255 drag_info.motion_callback = &Editor::drag_range_markerbar_op;
4256 drag_info.finished_callback = &Editor::end_range_markerbar_op;
4258 range_marker_op = op;
4260 if (!temp_location) {
4261 temp_location = new Location;
4265 case CreateRangeMarker:
4266 case CreateTransportMarker:
4268 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
4269 drag_info.copy = true;
4271 drag_info.copy = false;
4273 start_grab (event, selector_cursor);
4277 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4282 Editor::drag_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4284 jack_nframes_t start = 0;
4285 jack_nframes_t end = 0;
4286 ArdourCanvas::SimpleRect *crect = (range_marker_op == CreateRangeMarker) ? range_bar_drag_rect: transport_bar_drag_rect;
4288 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4289 snap_to (drag_info.current_pointer_frame);
4292 /* only alter selection if the current frame is
4293 different from the last frame position.
4296 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4298 switch (range_marker_op) {
4299 case CreateRangeMarker:
4300 case CreateTransportMarker:
4301 if (drag_info.first_move) {
4302 snap_to (drag_info.grab_frame);
4305 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4306 start = drag_info.current_pointer_frame;
4307 end = drag_info.grab_frame;
4309 end = drag_info.current_pointer_frame;
4310 start = drag_info.grab_frame;
4313 /* first drag: Either add to the selection
4314 or create a new selection->
4317 if (drag_info.first_move) {
4319 temp_location->set (start, end);
4323 update_marker_drag_item (temp_location);
4324 range_marker_drag_rect->show();
4325 range_marker_drag_rect->raise_to_top();
4331 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
4332 start_canvas_autoscroll (1);
4336 temp_location->set (start, end);
4338 double x1 = frame_to_pixel (start);
4339 double x2 = frame_to_pixel (end);
4340 crect->property_x1() = x1;
4341 crect->property_x2() = x2;
4343 update_marker_drag_item (temp_location);
4346 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4347 drag_info.first_move = false;
4349 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4354 Editor::end_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4356 Location * newloc = 0;
4358 if (!drag_info.first_move) {
4359 drag_range_markerbar_op (item, event);
4361 switch (range_marker_op) {
4362 case CreateRangeMarker:
4363 begin_reversible_command (_("new range marker"));
4364 session->add_undo (session->locations()->get_memento());
4365 newloc = new Location(temp_location->start(), temp_location->end(), "unnamed", Location::IsRangeMarker);
4366 session->locations()->add (newloc, true);
4367 session->add_redo_no_execute (session->locations()->get_memento());
4368 commit_reversible_command ();
4370 range_bar_drag_rect->hide();
4371 range_marker_drag_rect->hide();
4374 case CreateTransportMarker:
4375 // popup menu to pick loop or punch
4376 new_transport_marker_context_menu (&event->button, item);
4381 /* just a click, no pointer movement.*/
4383 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
4390 stop_canvas_autoscroll ();
4396 Editor::start_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4398 drag_info.item = item;
4399 drag_info.motion_callback = &Editor::drag_mouse_zoom;
4400 drag_info.finished_callback = &Editor::end_mouse_zoom;
4402 start_grab (event, zoom_cursor);
4404 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4408 Editor::drag_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4410 jack_nframes_t start;
4413 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4414 snap_to (drag_info.current_pointer_frame);
4416 if (drag_info.first_move) {
4417 snap_to (drag_info.grab_frame);
4421 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4423 /* base start and end on initial click position */
4424 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4425 start = drag_info.current_pointer_frame;
4426 end = drag_info.grab_frame;
4428 end = drag_info.current_pointer_frame;
4429 start = drag_info.grab_frame;
4434 if (drag_info.first_move) {
4436 zoom_rect->raise_to_top();
4439 reposition_zoom_rect(start, end);
4441 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4442 drag_info.first_move = false;
4444 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4449 Editor::end_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4451 if (!drag_info.first_move) {
4452 drag_mouse_zoom (item, event);
4454 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
4455 temporal_zoom_by_frame (drag_info.grab_frame, drag_info.last_pointer_frame, "mouse zoom");
4457 temporal_zoom_by_frame (drag_info.last_pointer_frame, drag_info.grab_frame, "mouse zoom");
4460 temporal_zoom_to_frame (false, drag_info.grab_frame);
4462 temporal_zoom_step (false);
4463 center_screen (drag_info.grab_frame);
4471 Editor::reposition_zoom_rect (jack_nframes_t start, jack_nframes_t end)
4473 double x1 = frame_to_pixel (start);
4474 double x2 = frame_to_pixel (end);
4475 double y2 = canvas_height - 2;
4477 zoom_rect->property_x1() = x1;
4478 zoom_rect->property_y1() = 1.0;
4479 zoom_rect->property_x2() = x2;
4480 zoom_rect->property_y2() = y2;
4484 Editor::start_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4486 drag_info.item = item;
4487 drag_info.motion_callback = &Editor::drag_rubberband_select;
4488 drag_info.finished_callback = &Editor::end_rubberband_select;
4490 start_grab (event, cross_hair_cursor);
4492 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4496 Editor::drag_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4498 jack_nframes_t start;
4503 /* use a bigger drag threshold than the default */
4505 if (abs ((int) (drag_info.current_pointer_frame - drag_info.grab_frame)) < 8) {
4509 // if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4510 // snap_to (drag_info.current_pointer_frame);
4512 // if (drag_info.first_move) {
4513 // snap_to (drag_info.grab_frame);
4518 /* base start and end on initial click position */
4519 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4520 start = drag_info.current_pointer_frame;
4521 end = drag_info.grab_frame;
4523 end = drag_info.current_pointer_frame;
4524 start = drag_info.grab_frame;
4527 if (drag_info.current_pointer_y < drag_info.grab_y) {
4528 y1 = drag_info.current_pointer_y;
4529 y2 = drag_info.grab_y;
4532 y2 = drag_info.current_pointer_y;
4533 y1 = drag_info.grab_y;
4537 if (start != end || y1 != y2) {
4539 double x1 = frame_to_pixel (start);
4540 double x2 = frame_to_pixel (end);
4542 rubberband_rect->property_x1() = x1;
4543 rubberband_rect->property_y1() = y1;
4544 rubberband_rect->property_x2() = x2;
4545 rubberband_rect->property_y2() = y2;
4547 rubberband_rect->show();
4548 rubberband_rect->raise_to_top();
4550 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4551 drag_info.first_move = false;
4553 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4558 Editor::end_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4560 if (!drag_info.first_move) {
4562 drag_rubberband_select (item, event);
4565 if (drag_info.current_pointer_y < drag_info.grab_y) {
4566 y1 = drag_info.current_pointer_y;
4567 y2 = drag_info.grab_y;
4570 y2 = drag_info.current_pointer_y;
4571 y1 = drag_info.grab_y;
4575 Selection::Operation op = Keyboard::selection_type (event->button.state);
4578 begin_reversible_command (_("select regions"));
4580 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
4581 commit = select_all_within (drag_info.grab_frame, drag_info.last_pointer_frame, y1, y2, op);
4583 commit = select_all_within (drag_info.last_pointer_frame, drag_info.grab_frame, y1, y2, op);
4587 commit_reversible_command ();
4591 selection->clear_audio_regions();
4592 selection->clear_points ();
4593 selection->clear_lines ();
4596 rubberband_rect->hide();
4601 Editor::mouse_rename_region (ArdourCanvas::Item* item, GdkEvent* event)
4603 using namespace Gtkmm2ext;
4605 ArdourPrompter prompter (false);
4607 prompter.set_prompt (_("Name for region:"));
4608 prompter.set_initial_text (clicked_regionview->region.name());
4609 prompter.show_all ();
4610 switch (prompter.run ()) {
4611 case Gtk::RESPONSE_ACCEPT:
4613 prompter.get_result(str);
4615 clicked_regionview->region.set_name (str);
4623 Editor::start_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
4625 drag_info.item = item;
4626 drag_info.motion_callback = &Editor::time_fx_motion;
4627 drag_info.finished_callback = &Editor::end_time_fx;
4631 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4635 Editor::time_fx_motion (ArdourCanvas::Item *item, GdkEvent* event)
4637 AudioRegionView* rv = clicked_regionview;
4639 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4640 snap_to (drag_info.current_pointer_frame);
4643 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
4647 if (drag_info.current_pointer_frame > rv->region.position()) {
4648 rv->get_time_axis_view().show_timestretch (rv->region.position(), drag_info.current_pointer_frame);
4651 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4652 drag_info.first_move = false;
4654 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4658 Editor::end_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
4660 clicked_regionview->get_time_axis_view().hide_timestretch ();
4662 if (drag_info.first_move) {
4666 jack_nframes_t newlen = drag_info.last_pointer_frame - clicked_regionview->region.position();
4667 float percentage = (float) ((double) newlen - (double) clicked_regionview->region.length()) / ((double) newlen) * 100.0f;
4669 begin_reversible_command (_("timestretch"));
4671 if (run_timestretch (selection->audio_regions, percentage) == 0) {
4672 session->commit_reversible_command ();
4677 Editor::mouse_brush_insert_region (AudioRegionView* rv, jack_nframes_t pos)
4679 /* no brushing without a useful snap setting */
4681 switch (snap_mode) {
4683 return; /* can't work because it allows region to be placed anywhere */
4688 switch (snap_type) {
4691 case SnapToEditCursor:
4698 /* don't brush a copy over the original */
4700 if (pos == rv->region.position()) {
4704 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*>(&rv->get_time_axis_view());
4706 if (atv == 0 || !atv->is_audio_track()) {
4710 Playlist* playlist = atv->playlist();
4711 double speed = atv->get_diskstream()->speed();
4713 session->add_undo (playlist->get_memento());
4714 playlist->add_region (*(new AudioRegion (rv->region)), (jack_nframes_t) (pos * speed));
4715 session->add_redo_no_execute (playlist->get_memento());
4717 // playlist is frozen, so we have to update manually
4719 playlist->StateChanged (Change (~0)); /* EMIT SIGNAL */
4723 Editor::track_height_step_timeout ()
4726 struct timeval delta;
4728 gettimeofday (&now, 0);
4729 timersub (&now, &last_track_height_step_timestamp, &delta);
4731 if (delta.tv_sec * 1000000 + delta.tv_usec > 250000) { /* milliseconds */
4732 current_stepping_trackview = 0;