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);
945 remove_marker (*item, event);
949 if (mouse_mode == MouseObject) {
950 remove_clicked_region ();
954 case GainControlPointItem:
955 if (mouse_mode == MouseGain) {
956 remove_gain_control_point (item, event);
960 case GainAutomationControlPointItem:
961 case PanAutomationControlPointItem:
962 case RedirectAutomationControlPointItem:
963 remove_control_point (item, event);
972 switch (event->button.button) {
976 /* see comments in button_press_handler */
978 case PlayheadCursorItem:
981 case GainAutomationLineItem:
982 case PanAutomationLineItem:
983 case RedirectAutomationLineItem:
984 case StartSelectionTrimItem:
985 case EndSelectionTrimItem:
989 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
990 snap_to (where, 0, true);
992 mouse_add_new_marker (where);
996 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
999 mouse_add_new_tempo_event (where);
1003 mouse_add_new_meter_event (pixel_to_frame (event->button.x));
1011 switch (mouse_mode) {
1013 switch (item_type) {
1014 case AutomationTrackItem:
1015 dynamic_cast<AutomationTimeAxisView*>(clicked_trackview)->add_automation_event
1029 switch (item_type) {
1031 clicked_regionview->add_gain_point_event (item, event);
1035 case AutomationTrackItem:
1036 dynamic_cast<AutomationTimeAxisView*>(clicked_trackview)->
1037 add_automation_event (item, event, where, event->button.y);
1046 switch (item_type) {
1048 audition_selected_region ();
1065 switch (mouse_mode) {
1068 switch (item_type) {
1070 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
1072 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::Shift|Keyboard::Alt))) {
1075 // Button2 click is unused
1088 // x_style_paste (where, 1.0);
1108 Editor::maybe_autoscroll (GdkEvent* event)
1110 jack_nframes_t one_page = (jack_nframes_t) rint (canvas_width * frames_per_unit);
1111 jack_nframes_t rightmost_frame = leftmost_frame + one_page;
1113 jack_nframes_t frame = drag_info.current_pointer_frame;
1115 if (autoscroll_timeout_tag < 0) {
1116 if (frame > rightmost_frame) {
1117 if (rightmost_frame < max_frames) {
1118 start_canvas_autoscroll (1);
1120 } else if (frame < leftmost_frame) {
1121 if (leftmost_frame > 0) {
1122 start_canvas_autoscroll (-1);
1126 if (frame >= leftmost_frame && frame < rightmost_frame) {
1127 stop_canvas_autoscroll ();
1133 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1139 switch (item_type) {
1140 case GainControlPointItem:
1141 if (mouse_mode == MouseGain) {
1142 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1143 cp->set_visible (true);
1147 at_y = cp->get_y ();
1148 cp->item->i2w (at_x, at_y);
1152 fraction = 1.0 - (cp->get_y() / cp->line.height());
1154 set_verbose_canvas_cursor (cp->line.get_verbose_cursor_string (fraction), at_x, at_y);
1155 show_verbose_canvas_cursor ();
1157 if (is_drawable()) {
1158 track_canvas.get_window()->set_cursor (*fader_cursor);
1163 case GainAutomationControlPointItem:
1164 case PanAutomationControlPointItem:
1165 case RedirectAutomationControlPointItem:
1166 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1167 cp->set_visible (true);
1171 at_y = cp->get_y ();
1172 cp->item->i2w (at_x, at_y);
1176 fraction = 1.0 - (cp->get_y() / cp->line.height());
1178 set_verbose_canvas_cursor (cp->line.get_verbose_cursor_string (fraction), at_x, at_y);
1179 show_verbose_canvas_cursor ();
1181 if (is_drawable()) {
1182 track_canvas.get_window()->set_cursor (*fader_cursor);
1187 if (mouse_mode == MouseGain) {
1188 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1190 line->property_fill_color_rgba() = color_map[cEnteredGainLine];
1191 if (is_drawable()) {
1192 track_canvas.get_window()->set_cursor (*fader_cursor);
1197 case GainAutomationLineItem:
1198 case RedirectAutomationLineItem:
1199 case PanAutomationLineItem:
1201 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1203 line->property_fill_color_rgba() = color_map[cEnteredAutomationLine];
1205 if (is_drawable()) {
1206 track_canvas.get_window()->set_cursor (*fader_cursor);
1210 case AudioRegionViewNameHighlight:
1211 if (is_drawable() && mouse_mode == MouseObject) {
1212 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1216 case StartSelectionTrimItem:
1217 case EndSelectionTrimItem:
1218 /* <CMT Additions> */
1219 case ImageFrameHandleStartItem:
1220 case ImageFrameHandleEndItem:
1221 case MarkerViewHandleStartItem:
1222 case MarkerViewHandleEndItem:
1223 /* </CMT Additions> */
1225 if (is_drawable()) {
1226 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1230 case EditCursorItem:
1231 case PlayheadCursorItem:
1232 if (is_drawable()) {
1233 track_canvas.get_window()->set_cursor (*grabber_cursor);
1237 case AudioRegionViewName:
1239 /* when the name is not an active item, the entire name highlight is for trimming */
1241 if (!reinterpret_cast<AudioRegionView *> (item->get_data ("regionview"))->name_active()) {
1242 if (mouse_mode == MouseObject && is_drawable()) {
1243 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1249 case AutomationTrackItem:
1250 if (is_drawable()) {
1251 Gdk::Cursor *cursor;
1252 switch (mouse_mode) {
1254 cursor = selector_cursor;
1257 cursor = zoom_cursor;
1260 cursor = cross_hair_cursor;
1264 track_canvas.get_window()->set_cursor (*cursor);
1266 AutomationTimeAxisView* atv;
1267 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1268 clear_entered_track = false;
1269 set_entered_track (atv);
1275 case RangeMarkerBarItem:
1276 case TransportMarkerBarItem:
1279 if (is_drawable()) {
1280 time_canvas.get_window()->set_cursor (*timebar_cursor);
1285 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1288 marker->set_color_rgba (color_map[cEnteredMarker]);
1290 case MeterMarkerItem:
1291 case TempoMarkerItem:
1292 if (is_drawable()) {
1293 time_canvas.get_window()->set_cursor (*timebar_cursor);
1296 case FadeInHandleItem:
1297 case FadeOutHandleItem:
1298 if (mouse_mode == MouseObject) {
1299 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1301 rect->property_fill_color_rgba() = 0;
1302 rect->property_outline_pixels() = 1;
1311 /* second pass to handle entered track status in a comprehensible way.
1314 switch (item_type) {
1316 case GainAutomationLineItem:
1317 case RedirectAutomationLineItem:
1318 case PanAutomationLineItem:
1319 case GainControlPointItem:
1320 case GainAutomationControlPointItem:
1321 case PanAutomationControlPointItem:
1322 case RedirectAutomationControlPointItem:
1323 /* these do not affect the current entered track state */
1324 clear_entered_track = false;
1327 case AutomationTrackItem:
1328 /* handled above already */
1332 set_entered_track (0);
1340 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1346 AudioRegionView* rv;
1349 switch (item_type) {
1350 case GainControlPointItem:
1351 case GainAutomationControlPointItem:
1352 case PanAutomationControlPointItem:
1353 case RedirectAutomationControlPointItem:
1354 cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
1355 if (cp->line.npoints() > 1) {
1356 if (!cp->selected) {
1357 cp->set_visible (false);
1361 if (is_drawable()) {
1362 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1365 hide_verbose_canvas_cursor ();
1368 case AudioRegionViewNameHighlight:
1369 case StartSelectionTrimItem:
1370 case EndSelectionTrimItem:
1371 case EditCursorItem:
1372 case PlayheadCursorItem:
1373 /* <CMT Additions> */
1374 case ImageFrameHandleStartItem:
1375 case ImageFrameHandleEndItem:
1376 case MarkerViewHandleStartItem:
1377 case MarkerViewHandleEndItem:
1378 /* </CMT Additions> */
1379 if (is_drawable()) {
1380 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1385 case GainAutomationLineItem:
1386 case RedirectAutomationLineItem:
1387 case PanAutomationLineItem:
1388 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1390 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1392 line->property_fill_color_rgba() = al->get_line_color();
1394 if (is_drawable()) {
1395 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1399 case AudioRegionViewName:
1400 /* see enter_handler() for notes */
1401 if (!reinterpret_cast<AudioRegionView *> (item->get_data ("regionview"))->name_active()) {
1402 if (is_drawable() && mouse_mode == MouseObject) {
1403 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1408 case RangeMarkerBarItem:
1409 case TransportMarkerBarItem:
1413 if (is_drawable()) {
1414 time_canvas.get_window()->set_cursor (*timebar_cursor);
1419 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1422 loc = find_location_from_marker (marker, is_start);
1423 if (loc) location_flags_changed (loc, this);
1425 case MeterMarkerItem:
1426 case TempoMarkerItem:
1428 if (is_drawable()) {
1429 time_canvas.get_window()->set_cursor (*timebar_cursor);
1434 case FadeInHandleItem:
1435 case FadeOutHandleItem:
1436 rv = static_cast<AudioRegionView*>(item->get_data ("regionview"));
1438 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1440 rect->property_fill_color_rgba() = rv->get_fill_color();
1441 rect->property_outline_pixels() = 0;
1446 case AutomationTrackItem:
1447 if (is_drawable()) {
1448 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1449 clear_entered_track = true;
1450 Glib::signal_idle().connect (mem_fun(*this, &Editor::left_automation_track));
1462 Editor::left_automation_track ()
1464 if (clear_entered_track) {
1465 set_entered_track (0);
1466 clear_entered_track = false;
1472 Editor::motion_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1476 /* We call this so that MOTION_NOTIFY events continue to be
1477 delivered to the canvas. We need to do this because we set
1478 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
1479 the density of the events, at the expense of a round-trip
1480 to the server. Given that this will mostly occur on cases
1481 where DISPLAY = :0.0, and given the cost of what the motion
1482 event might do, its a good tradeoff.
1485 track_canvas.get_pointer (x, y);
1487 if (current_stepping_trackview) {
1488 /* don't keep the persistent stepped trackview if the mouse moves */
1489 current_stepping_trackview = 0;
1490 step_timeout.disconnect ();
1493 if (session && session->actively_recording()) {
1494 /* Sorry. no dragging stuff around while we record */
1498 drag_info.current_pointer_frame = event_frame (event, &drag_info.current_pointer_x,
1499 &drag_info.current_pointer_y);
1501 if (drag_info.item) {
1502 /* item != 0 is the best test i can think of for
1505 if (!drag_info.move_threshold_passsed) {
1506 drag_info.move_threshold_passsed = (abs ((int) (drag_info.current_pointer_x - drag_info.grab_x)) > 4);
1508 // and change the initial grab loc/frame if this drag info wants us to
1509 if (drag_info.want_move_threshold && drag_info.move_threshold_passsed) {
1510 drag_info.grab_frame = drag_info.current_pointer_frame;
1511 drag_info.grab_x = drag_info.current_pointer_x;
1512 drag_info.grab_y = drag_info.current_pointer_y;
1513 drag_info.last_pointer_frame = drag_info.grab_frame;
1514 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
1519 switch (item_type) {
1520 case PlayheadCursorItem:
1521 case EditCursorItem:
1523 case GainControlPointItem:
1524 case RedirectAutomationControlPointItem:
1525 case GainAutomationControlPointItem:
1526 case PanAutomationControlPointItem:
1527 case TempoMarkerItem:
1528 case MeterMarkerItem:
1529 case AudioRegionViewNameHighlight:
1530 case StartSelectionTrimItem:
1531 case EndSelectionTrimItem:
1534 case RedirectAutomationLineItem:
1535 case GainAutomationLineItem:
1536 case PanAutomationLineItem:
1537 case FadeInHandleItem:
1538 case FadeOutHandleItem:
1539 /* <CMT Additions> */
1540 case ImageFrameHandleStartItem:
1541 case ImageFrameHandleEndItem:
1542 case MarkerViewHandleStartItem:
1543 case MarkerViewHandleEndItem:
1544 /* </CMT Additions> */
1545 if (drag_info.item && (event->motion.state & Gdk::BUTTON1_MASK ||
1546 (event->motion.state & Gdk::BUTTON2_MASK))) {
1547 maybe_autoscroll (event);
1548 (this->*(drag_info.motion_callback)) (item, event);
1557 switch (mouse_mode) {
1562 if (drag_info.item && (event->motion.state & GDK_BUTTON1_MASK ||
1563 (event->motion.state & GDK_BUTTON2_MASK))) {
1564 maybe_autoscroll (event);
1565 (this->*(drag_info.motion_callback)) (item, event);
1576 track_canvas_motion (event);
1584 Editor::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
1586 if (drag_info.item == 0) {
1587 fatal << _("programming error: start_grab called without drag item") << endmsg;
1593 cursor = grabber_cursor;
1596 // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
1598 if (event->button.button == 2) {
1599 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Alt)) {
1600 drag_info.y_constrained = true;
1601 drag_info.x_constrained = false;
1603 drag_info.y_constrained = false;
1604 drag_info.x_constrained = true;
1607 drag_info.x_constrained = false;
1608 drag_info.y_constrained = false;
1611 drag_info.grab_frame = event_frame(event, &drag_info.grab_x, &drag_info.grab_y);
1612 drag_info.last_pointer_frame = drag_info.grab_frame;
1613 drag_info.current_pointer_frame = drag_info.grab_frame;
1614 drag_info.current_pointer_x = drag_info.grab_x;
1615 drag_info.current_pointer_y = drag_info.grab_y;
1616 drag_info.cumulative_x_drag = 0;
1617 drag_info.cumulative_y_drag = 0;
1618 drag_info.first_move = true;
1619 drag_info.move_threshold_passsed = false;
1620 drag_info.want_move_threshold = false;
1621 drag_info.pointer_frame_offset = 0;
1622 drag_info.brushing = false;
1623 drag_info.copied_location = 0;
1625 drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK,
1627 event->button.time);
1629 if (session && session->transport_rolling()) {
1630 drag_info.was_rolling = true;
1632 drag_info.was_rolling = false;
1635 switch (snap_type) {
1636 case SnapToRegionStart:
1637 case SnapToRegionEnd:
1638 case SnapToRegionSync:
1639 case SnapToRegionBoundary:
1640 build_region_boundary_cache ();
1648 Editor::end_grab (ArdourCanvas::Item* item, GdkEvent* event)
1650 bool did_drag = false;
1652 stop_canvas_autoscroll ();
1654 if (drag_info.item == 0) {
1658 drag_info.item->ungrab (event->button.time);
1660 if (drag_info.finished_callback) {
1661 (this->*(drag_info.finished_callback)) (item, event);
1664 did_drag = !drag_info.first_move;
1666 hide_verbose_canvas_cursor();
1669 drag_info.copy = false;
1670 drag_info.motion_callback = 0;
1671 drag_info.finished_callback = 0;
1672 drag_info.last_trackview = 0;
1673 drag_info.last_frame_position = 0;
1674 drag_info.grab_frame = 0;
1675 drag_info.last_pointer_frame = 0;
1676 drag_info.current_pointer_frame = 0;
1677 drag_info.brushing = false;
1679 if (drag_info.copied_location) {
1680 delete drag_info.copied_location;
1681 drag_info.copied_location = 0;
1688 Editor::set_edit_cursor (GdkEvent* event)
1690 jack_nframes_t pointer_frame = event_frame (event);
1692 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1693 if (snap_type != SnapToEditCursor) {
1694 snap_to (pointer_frame);
1698 edit_cursor->set_position (pointer_frame);
1699 edit_cursor_clock.set (pointer_frame);
1703 Editor::set_playhead_cursor (GdkEvent* event)
1705 jack_nframes_t pointer_frame = event_frame (event);
1707 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1708 snap_to (pointer_frame);
1712 session->request_locate (pointer_frame, session->transport_rolling());
1717 Editor::start_fade_in_grab (ArdourCanvas::Item* item, GdkEvent* event)
1719 drag_info.item = item;
1720 drag_info.motion_callback = &Editor::fade_in_drag_motion_callback;
1721 drag_info.finished_callback = &Editor::fade_in_drag_finished_callback;
1725 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
1726 fatal << _("programming error: fade in canvas item has no regionview data pointer!") << endmsg;
1730 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1732 drag_info.pointer_frame_offset = drag_info.grab_frame - ((jack_nframes_t) arv->region.fade_in().back()->when + arv->region.position());
1736 Editor::fade_in_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1738 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1740 jack_nframes_t fade_length;
1742 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1743 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1749 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1753 if (pos < (arv->region.position() + 64)) {
1754 fade_length = 64; // this should be a minimum defined somewhere
1755 } else if (pos > arv->region.last_frame()) {
1756 fade_length = arv->region.length();
1758 fade_length = pos - arv->region.position();
1761 arv->reset_fade_in_shape_width (fade_length);
1763 show_verbose_duration_cursor (arv->region.position(), arv->region.position() + fade_length, 10);
1765 drag_info.first_move = false;
1769 Editor::fade_in_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1771 if (drag_info.first_move) return;
1773 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1775 jack_nframes_t fade_length;
1777 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1778 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1784 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1788 if (pos < (arv->region.position() + 64)) {
1789 fade_length = 64; // this should be a minimum defined somewhere
1791 else if (pos > arv->region.last_frame()) {
1792 fade_length = arv->region.length();
1795 fade_length = pos - arv->region.position();
1798 begin_reversible_command (_("change fade in length"));
1799 session->add_undo (arv->region.get_memento());
1800 arv->region.set_fade_in_length (fade_length);
1801 session->add_redo_no_execute (arv->region.get_memento());
1802 commit_reversible_command ();
1803 fade_in_drag_motion_callback (item, event);
1807 Editor::start_fade_out_grab (ArdourCanvas::Item* item, GdkEvent* event)
1809 drag_info.item = item;
1810 drag_info.motion_callback = &Editor::fade_out_drag_motion_callback;
1811 drag_info.finished_callback = &Editor::fade_out_drag_finished_callback;
1815 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
1816 fatal << _("programming error: fade out canvas item has no regionview data pointer!") << endmsg;
1820 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1822 drag_info.pointer_frame_offset = drag_info.grab_frame - (arv->region.length() - (jack_nframes_t) arv->region.fade_out().back()->when + arv->region.position());
1826 Editor::fade_out_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1828 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1830 jack_nframes_t fade_length;
1832 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1833 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1839 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1843 if (pos > (arv->region.last_frame() - 64)) {
1844 fade_length = 64; // this should really be a minimum fade defined somewhere
1846 else if (pos < arv->region.position()) {
1847 fade_length = arv->region.length();
1850 fade_length = arv->region.last_frame() - pos;
1853 arv->reset_fade_out_shape_width (fade_length);
1855 show_verbose_duration_cursor (arv->region.last_frame() - fade_length, arv->region.last_frame(), 10);
1857 drag_info.first_move = false;
1861 Editor::fade_out_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1863 if (drag_info.first_move) return;
1865 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1867 jack_nframes_t fade_length;
1869 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1870 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1876 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1880 if (pos > (arv->region.last_frame() - 64)) {
1881 fade_length = 64; // this should really be a minimum fade defined somewhere
1883 else if (pos < arv->region.position()) {
1884 fade_length = arv->region.length();
1887 fade_length = arv->region.last_frame() - pos;
1890 begin_reversible_command (_("change fade out length"));
1891 session->add_undo (arv->region.get_memento());
1892 arv->region.set_fade_out_length (fade_length);
1893 session->add_redo_no_execute (arv->region.get_memento());
1894 commit_reversible_command ();
1896 fade_out_drag_motion_callback (item, event);
1900 Editor::start_cursor_grab (ArdourCanvas::Item* item, GdkEvent* event)
1902 drag_info.item = item;
1903 drag_info.motion_callback = &Editor::cursor_drag_motion_callback;
1904 drag_info.finished_callback = &Editor::cursor_drag_finished_callback;
1908 if ((drag_info.data = (item->get_data ("cursor"))) == 0) {
1909 fatal << _("programming error: cursor canvas item has no cursor data pointer!") << endmsg;
1913 Cursor* cursor = (Cursor *) drag_info.data;
1915 if (session && cursor == playhead_cursor) {
1916 if (drag_info.was_rolling) {
1917 session->request_stop ();
1921 drag_info.pointer_frame_offset = drag_info.grab_frame - cursor->current_frame;
1923 show_verbose_time_cursor (cursor->current_frame, 10);
1927 Editor::cursor_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1929 Cursor* cursor = (Cursor *) drag_info.data;
1930 jack_nframes_t adjusted_frame;
1932 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1933 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1939 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1940 if (cursor != edit_cursor || snap_type != SnapToEditCursor) {
1941 snap_to (adjusted_frame);
1945 if (adjusted_frame == drag_info.last_pointer_frame) return;
1947 cursor->set_position (adjusted_frame);
1949 if (cursor == edit_cursor) {
1950 edit_cursor_clock.set (cursor->current_frame);
1953 show_verbose_time_cursor (cursor->current_frame, 10);
1955 drag_info.last_pointer_frame = adjusted_frame;
1956 drag_info.first_move = false;
1960 Editor::cursor_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1962 if (drag_info.first_move) return;
1964 cursor_drag_motion_callback (item, event);
1966 if (item == &playhead_cursor->canvas_item) {
1968 session->request_locate (playhead_cursor->current_frame, drag_info.was_rolling);
1970 } else if (item == &edit_cursor->canvas_item) {
1971 edit_cursor->set_position (edit_cursor->current_frame);
1972 edit_cursor_clock.set (edit_cursor->current_frame);
1977 Editor::update_marker_drag_item (Location *location)
1979 double x1 = frame_to_pixel (location->start());
1980 double x2 = frame_to_pixel (location->end());
1982 if (location->is_mark()) {
1983 marker_drag_line_points.front().set_x(x1);
1984 marker_drag_line_points.back().set_x(x1);
1985 marker_drag_line->property_points() = marker_drag_line_points;
1988 range_marker_drag_rect->property_x1() = x1;
1989 range_marker_drag_rect->property_x2() = x2;
1994 Editor::start_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
1998 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1999 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2005 Location *location = find_location_from_marker (marker, is_start);
2007 drag_info.item = item;
2008 drag_info.data = marker;
2009 drag_info.motion_callback = &Editor::marker_drag_motion_callback;
2010 drag_info.finished_callback = &Editor::marker_drag_finished_callback;
2014 drag_info.copied_location = new Location (*location);
2015 drag_info.pointer_frame_offset = drag_info.grab_frame - (is_start ? location->start() : location->end());
2017 update_marker_drag_item (location);
2019 if (location->is_mark()) {
2020 marker_drag_line->show();
2021 marker_drag_line->raise_to_top();
2024 range_marker_drag_rect->show();
2025 range_marker_drag_rect->raise_to_top();
2028 if (is_start) show_verbose_time_cursor (location->start(), 10);
2029 else show_verbose_time_cursor (location->end(), 10);
2033 Editor::marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2035 jack_nframes_t f_delta;
2036 Marker* marker = (Marker *) drag_info.data;
2037 Location *real_location;
2038 Location *copy_location;
2040 bool move_both = false;
2042 jack_nframes_t newframe;
2043 if (drag_info.pointer_frame_offset <= (long) drag_info.current_pointer_frame) {
2044 newframe = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2050 jack_nframes_t next = newframe;
2052 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2053 snap_to (newframe, 0, true);
2056 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
2058 /* call this to find out if its the start or end */
2060 real_location = find_location_from_marker (marker, is_start);
2062 /* use the copy that we're "dragging" around */
2064 copy_location = drag_info.copied_location;
2066 f_delta = copy_location->end() - copy_location->start();
2068 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
2072 if (is_start) { // start marker
2075 copy_location->set_start (newframe);
2076 copy_location->set_end (newframe + f_delta);
2077 } else if (newframe < copy_location->end()) {
2078 copy_location->set_start (newframe);
2080 snap_to (next, 1, true);
2081 copy_location->set_end (next);
2082 copy_location->set_start (newframe);
2085 } else { // end marker
2088 copy_location->set_end (newframe);
2089 copy_location->set_start (newframe - f_delta);
2090 } else if (newframe > copy_location->start()) {
2091 copy_location->set_end (newframe);
2093 } else if (newframe > 0) {
2094 snap_to (next, -1, true);
2095 copy_location->set_start (next);
2096 copy_location->set_end (newframe);
2100 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
2101 drag_info.first_move = false;
2103 update_marker_drag_item (copy_location);
2105 LocationMarkers* lm = find_location_markers (real_location);
2106 lm->set_position (copy_location->start(), copy_location->end());
2108 show_verbose_time_cursor (newframe, 10);
2112 Editor::marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2114 if (drag_info.first_move) {
2115 marker_drag_motion_callback (item, event);
2119 Marker* marker = (Marker *) drag_info.data;
2124 begin_reversible_command ( _("move marker") );
2125 session->add_undo( session->locations()->get_memento() );
2127 Location * location = find_location_from_marker (marker, is_start);
2130 location->set (drag_info.copied_location->start(), drag_info.copied_location->end());
2133 session->add_redo_no_execute( session->locations()->get_memento() );
2134 commit_reversible_command ();
2136 marker_drag_line->hide();
2137 range_marker_drag_rect->hide();
2141 Editor::start_meter_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2144 MeterMarker* meter_marker;
2146 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2147 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2151 meter_marker = dynamic_cast<MeterMarker*> (marker);
2153 MetricSection& section (meter_marker->meter());
2155 if (!section.movable()) {
2159 drag_info.item = item;
2160 drag_info.data = marker;
2161 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2162 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2166 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2168 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2172 Editor::start_meter_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2175 MeterMarker* meter_marker;
2177 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2178 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2182 meter_marker = dynamic_cast<MeterMarker*> (marker);
2184 // create a dummy marker for visual representation of moving the copy.
2185 // The actual copying is not done before we reach the finish callback.
2187 snprintf (name, sizeof(name), "%g/%g", meter_marker->meter().beats_per_bar(), meter_marker->meter().note_divisor ());
2188 MeterMarker* new_marker = new MeterMarker(*this, *meter_group, color_map[cMeterMarker], name,
2189 *new MeterSection(meter_marker->meter()));
2191 drag_info.item = &new_marker->the_item();
2192 drag_info.copy = true;
2193 drag_info.data = new_marker;
2194 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2195 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2199 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2201 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2205 Editor::meter_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2207 MeterMarker* marker = (MeterMarker *) drag_info.data;
2208 jack_nframes_t adjusted_frame;
2210 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2211 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2217 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2218 snap_to (adjusted_frame);
2221 if (adjusted_frame == drag_info.last_pointer_frame) return;
2223 marker->set_position (adjusted_frame);
2226 drag_info.last_pointer_frame = adjusted_frame;
2227 drag_info.first_move = false;
2229 show_verbose_time_cursor (adjusted_frame, 10);
2233 Editor::meter_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2235 if (drag_info.first_move) return;
2237 meter_marker_drag_motion_callback (drag_info.item, event);
2239 MeterMarker* marker = (MeterMarker *) drag_info.data;
2242 TempoMap& map (session->tempo_map());
2243 map.bbt_time (drag_info.last_pointer_frame, when);
2245 if (drag_info.copy == true) {
2246 begin_reversible_command (_("copy meter mark"));
2247 session->add_undo (map.get_memento());
2248 map.add_meter (marker->meter(), when);
2249 session->add_redo_no_execute (map.get_memento());
2250 commit_reversible_command ();
2252 // delete the dummy marker we used for visual representation of copying.
2253 // a new visual marker will show up automatically.
2256 begin_reversible_command (_("move meter mark"));
2257 session->add_undo (map.get_memento());
2258 map.move_meter (marker->meter(), when);
2259 session->add_redo_no_execute (map.get_memento());
2260 commit_reversible_command ();
2265 Editor::start_tempo_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2268 TempoMarker* tempo_marker;
2270 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2271 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2275 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2276 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2280 MetricSection& section (tempo_marker->tempo());
2282 if (!section.movable()) {
2286 drag_info.item = item;
2287 drag_info.data = marker;
2288 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2289 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2293 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2294 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2298 Editor::start_tempo_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2301 TempoMarker* tempo_marker;
2303 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2304 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2308 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2309 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2313 // create a dummy marker for visual representation of moving the copy.
2314 // The actual copying is not done before we reach the finish callback.
2316 snprintf (name, sizeof (name), "%.2f", tempo_marker->tempo().beats_per_minute());
2317 TempoMarker* new_marker = new TempoMarker(*this, *tempo_group, color_map[cTempoMarker], name,
2318 *new TempoSection(tempo_marker->tempo()));
2320 drag_info.item = &new_marker->the_item();
2321 drag_info.copy = true;
2322 drag_info.data = new_marker;
2323 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2324 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2328 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2330 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2334 Editor::tempo_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2336 TempoMarker* marker = (TempoMarker *) drag_info.data;
2337 jack_nframes_t adjusted_frame;
2339 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2340 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2346 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2347 snap_to (adjusted_frame);
2350 if (adjusted_frame == drag_info.last_pointer_frame) return;
2352 /* OK, we've moved far enough to make it worth actually move the thing. */
2354 marker->set_position (adjusted_frame);
2356 show_verbose_time_cursor (adjusted_frame, 10);
2358 drag_info.last_pointer_frame = adjusted_frame;
2359 drag_info.first_move = false;
2363 Editor::tempo_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2365 if (drag_info.first_move) return;
2367 tempo_marker_drag_motion_callback (drag_info.item, event);
2369 TempoMarker* marker = (TempoMarker *) drag_info.data;
2372 TempoMap& map (session->tempo_map());
2373 map.bbt_time (drag_info.last_pointer_frame, when);
2375 if (drag_info.copy == true) {
2376 begin_reversible_command (_("copy tempo mark"));
2377 session->add_undo (map.get_memento());
2378 map.add_tempo (marker->tempo(), when);
2379 session->add_redo_no_execute (map.get_memento());
2380 commit_reversible_command ();
2382 // delete the dummy marker we used for visual representation of copying.
2383 // a new visual marker will show up automatically.
2386 begin_reversible_command (_("move tempo mark"));
2387 session->add_undo (map.get_memento());
2388 map.move_tempo (marker->tempo(), when);
2389 session->add_redo_no_execute (map.get_memento());
2390 commit_reversible_command ();
2395 Editor::remove_gain_control_point (ArdourCanvas::Item*item, GdkEvent* event)
2397 ControlPoint* control_point;
2399 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2400 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2404 // We shouldn't remove the first or last gain point
2405 if (control_point->line.is_last_point(*control_point) ||
2406 control_point->line.is_first_point(*control_point)) {
2410 control_point->line.remove_point (*control_point);
2414 Editor::remove_control_point (ArdourCanvas::Item*item, GdkEvent* event)
2416 ControlPoint* control_point;
2418 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2419 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2423 control_point->line.remove_point (*control_point);
2427 Editor::start_control_point_grab (ArdourCanvas::Item* item, GdkEvent* event)
2429 ControlPoint* control_point;
2431 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2432 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2436 drag_info.item = item;
2437 drag_info.data = control_point;
2438 drag_info.motion_callback = &Editor::control_point_drag_motion_callback;
2439 drag_info.finished_callback = &Editor::control_point_drag_finished_callback;
2441 start_grab (event, fader_cursor);
2443 control_point->line.start_drag (control_point, 0);
2445 float fraction = 1.0 - (control_point->get_y() / control_point->line.height());
2446 set_verbose_canvas_cursor (control_point->line.get_verbose_cursor_string (fraction),
2447 drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
2449 show_verbose_canvas_cursor ();
2453 Editor::control_point_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2455 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2457 double cx = drag_info.current_pointer_x;
2458 double cy = drag_info.current_pointer_y;
2460 drag_info.cumulative_x_drag = cx - drag_info.grab_x ;
2461 drag_info.cumulative_y_drag = cy - drag_info.grab_y ;
2463 if (drag_info.x_constrained) {
2464 cx = drag_info.grab_x;
2466 if (drag_info.y_constrained) {
2467 cy = drag_info.grab_y;
2470 cp->line.parent_group().w2i (cx, cy);
2474 cy = min ((double) cp->line.height(), cy);
2476 //translate cx to frames
2477 jack_nframes_t cx_frames = unit_to_frame (cx);
2479 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier()) && !drag_info.x_constrained) {
2480 snap_to (cx_frames);
2483 float fraction = 1.0 - (cy / cp->line.height());
2487 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
2493 cp->line.point_drag (*cp, cx_frames , fraction, push);
2495 set_verbose_canvas_cursor_text (cp->line.get_verbose_cursor_string (fraction));
2499 Editor::control_point_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2501 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2502 control_point_drag_motion_callback (item, event);
2503 cp->line.end_drag (cp);
2507 Editor::start_line_grab_from_regionview (ArdourCanvas::Item* item, GdkEvent* event)
2509 switch (mouse_mode) {
2511 start_line_grab (clicked_regionview->get_gain_line(), event);
2519 Editor::start_line_grab_from_line (ArdourCanvas::Item* item, GdkEvent* event)
2523 if ((al = reinterpret_cast<AutomationLine*> (item->get_data ("line"))) == 0) {
2524 fatal << _("programming error: line canvas item has no line pointer!") << endmsg;
2528 start_line_grab (al, event);
2532 Editor::start_line_grab (AutomationLine* line, GdkEvent* event)
2536 jack_nframes_t frame_within_region;
2538 /* need to get x coordinate in terms of parent (TimeAxisItemView)
2542 cx = event->button.x;
2543 cy = event->button.y;
2544 line->parent_group().w2i (cx, cy);
2545 frame_within_region = (jack_nframes_t) floor (cx * frames_per_unit);
2547 if (!line->control_points_adjacent (frame_within_region, current_line_drag_info.before,
2548 current_line_drag_info.after)) {
2549 /* no adjacent points */
2553 drag_info.item = &line->grab_item();
2554 drag_info.data = line;
2555 drag_info.motion_callback = &Editor::line_drag_motion_callback;
2556 drag_info.finished_callback = &Editor::line_drag_finished_callback;
2558 start_grab (event, fader_cursor);
2560 double fraction = 1.0 - (cy / line->height());
2562 line->start_drag (0, fraction);
2564 set_verbose_canvas_cursor (line->get_verbose_cursor_string (fraction),
2565 drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
2566 show_verbose_canvas_cursor ();
2570 Editor::line_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2572 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
2573 double cx = drag_info.current_pointer_x;
2574 double cy = drag_info.current_pointer_y;
2576 line->parent_group().w2i (cx, cy);
2579 fraction = 1.0 - (cy / line->height());
2583 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
2589 line->line_drag (current_line_drag_info.before, current_line_drag_info.after, fraction, push);
2591 set_verbose_canvas_cursor_text (line->get_verbose_cursor_string (fraction));
2595 Editor::line_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2597 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
2598 line_drag_motion_callback (item, event);
2603 Editor::start_region_grab (ArdourCanvas::Item* item, GdkEvent* event)
2605 if (selection->audio_regions.empty() || clicked_regionview == 0) {
2609 drag_info.copy = false;
2610 drag_info.item = item;
2611 drag_info.data = clicked_regionview;
2612 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2613 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2618 TimeAxisView* tvp = clicked_trackview;
2619 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
2621 if (tv && tv->is_audio_track()) {
2622 speed = tv->get_diskstream()->speed();
2625 drag_info.last_frame_position = (jack_nframes_t) (clicked_regionview->region.position() / speed);
2626 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2627 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2628 // we want a move threshold
2629 drag_info.want_move_threshold = true;
2631 show_verbose_time_cursor (drag_info.last_frame_position, 10);
2633 begin_reversible_command (_("move region(s)"));
2637 Editor::start_region_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2639 if (selection->audio_regions.empty() || clicked_regionview == 0) {
2643 /* this is committed in the grab finished callback. */
2645 begin_reversible_command (_("Drag region copy"));
2647 /* duplicate the region(s) */
2649 vector<AudioRegionView*> new_regionviews;
2651 set<Playlist*> affected_playlists;
2652 pair<set<Playlist*>::iterator,bool> insert_result;
2654 for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin(); i != selection->audio_regions.by_layer().end(); ++i) {
2655 AudioRegionView* rv;
2659 Playlist* to_playlist = rv->region.playlist();
2660 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*>(&rv->get_time_axis_view());
2662 insert_result = affected_playlists.insert (to_playlist);
2663 if (insert_result.second) {
2664 session->add_undo (to_playlist->get_memento ());
2667 latest_regionview = 0;
2669 sigc::connection c = atv->view->AudioRegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
2671 /* create a new region with the same name.
2674 AudioRegion* newregion = new AudioRegion (rv->region);
2676 /* if the original region was locked, we don't care */
2678 newregion->set_locked (false);
2680 to_playlist->add_region (*newregion, (jack_nframes_t) (rv->region.position() * atv->get_diskstream()->speed()));
2684 if (latest_regionview) {
2685 new_regionviews.push_back (latest_regionview);
2689 if (new_regionviews.empty()) {
2693 /* reset selection to new regionviews */
2695 selection->set (new_regionviews);
2697 /* reset drag_info data to reflect the fact that we are dragging the copies */
2699 drag_info.data = new_regionviews.front();
2700 drag_info.item = new_regionviews.front()->get_canvas_group ();
2702 drag_info.copy = true;
2703 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2704 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2708 TimeAxisView* tv = &clicked_regionview->get_time_axis_view();
2709 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*>(tv);
2712 if (atv && atv->is_audio_track()) {
2713 speed = atv->get_diskstream()->speed();
2716 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2717 drag_info.last_frame_position = (jack_nframes_t) (clicked_regionview->region.position() / speed);
2718 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2719 // we want a move threshold
2720 drag_info.want_move_threshold = true;
2722 show_verbose_time_cursor (drag_info.last_frame_position, 10);
2724 //begin_reversible_command (_("copy region(s)"));
2728 Editor::start_region_brush_grab (ArdourCanvas::Item* item, GdkEvent* event)
2730 if (selection->audio_regions.empty() || clicked_regionview == 0) {
2734 drag_info.copy = false;
2735 drag_info.item = item;
2736 drag_info.data = clicked_regionview;
2737 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2738 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2743 TimeAxisView* tvp = clicked_trackview;
2744 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
2746 if (tv && tv->is_audio_track()) {
2747 speed = tv->get_diskstream()->speed();
2750 drag_info.last_frame_position = (jack_nframes_t) (clicked_regionview->region.position() / speed);
2751 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2752 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2753 // we want a move threshold
2754 drag_info.want_move_threshold = true;
2755 drag_info.brushing = true;
2757 begin_reversible_command (_("Drag region brush"));
2761 Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2765 AudioRegionView *rv = reinterpret_cast<AudioRegionView*> (drag_info.data);
2766 jack_nframes_t pending_region_position = 0;
2767 int32_t pointer_y_span = 0, canvas_pointer_y_span = 0, original_pointer_order;
2768 int32_t visible_y_high = 0, visible_y_low = 512; //high meaning higher numbered.. not the height on the screen
2769 bool clamp_y_axis = false;
2770 vector<int32_t> height_list(512) ;
2771 vector<int32_t>::iterator j;
2773 /* Which trackview is this ? */
2775 TimeAxisView* tvp = trackview_by_y_position (drag_info.current_pointer_y);
2776 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
2778 /* The region motion is only processed if the pointer is over
2782 if (!tv || !tv->is_audio_track()) {
2783 /* To make sure we hide the verbose canvas cursor when the mouse is
2784 not held over and audiotrack.
2786 hide_verbose_canvas_cursor ();
2790 original_pointer_order = drag_info.last_trackview->order;
2792 /************************************************************
2794 ************************************************************/
2796 if (drag_info.brushing) {
2797 clamp_y_axis = true;
2802 if ((pointer_y_span = (drag_info.last_trackview->order - tv->order)) != 0) {
2804 int32_t children = 0, numtracks = 0;
2805 // XXX hard coding track limit, oh my, so very very bad
2806 bitset <1024> tracks (0x00);
2807 /* get a bitmask representing the visible tracks */
2809 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2810 TimeAxisView *tracklist_timeview;
2811 tracklist_timeview = (*i);
2812 AudioTimeAxisView* atv2 = dynamic_cast<AudioTimeAxisView*>(tracklist_timeview);
2813 list<TimeAxisView*> children_list;
2815 /* zeroes are audio tracks. ones are other types. */
2817 if (!atv2->hidden()) {
2819 if (visible_y_high < atv2->order) {
2820 visible_y_high = atv2->order;
2822 if (visible_y_low > atv2->order) {
2823 visible_y_low = atv2->order;
2826 if (!atv2->is_audio_track()) {
2827 tracks = tracks |= (0x01 << atv2->order);
2830 height_list[atv2->order] = (*i)->height;
2832 if ((children_list = atv2->get_child_list()).size() > 0) {
2833 for (list<TimeAxisView*>::iterator j = children_list.begin(); j != children_list.end(); ++j) {
2834 tracks = tracks |= (0x01 << (atv2->order + children));
2835 height_list[atv2->order + children] = (*j)->height;
2843 /* find the actual span according to the canvas */
2845 canvas_pointer_y_span = pointer_y_span;
2846 if (drag_info.last_trackview->order >= tv->order) {
2848 for (y = tv->order; y < drag_info.last_trackview->order; y++) {
2849 if (height_list[y] == 0 ) {
2850 canvas_pointer_y_span--;
2855 for (y = drag_info.last_trackview->order;y <= tv->order; y++) {
2856 if ( height_list[y] == 0 ) {
2857 canvas_pointer_y_span++;
2862 for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin(); i != selection->audio_regions.by_layer().end(); ++i) {
2863 AudioRegionView* rv2;
2865 double ix1, ix2, iy1, iy2;
2868 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
2869 rv2->get_canvas_group()->i2w (ix1, iy1);
2870 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
2871 AudioTimeAxisView* atv2 = dynamic_cast<AudioTimeAxisView*>(tvp2);
2873 if (atv2->order != original_pointer_order) {
2874 /* this isn't the pointer track */
2876 if (canvas_pointer_y_span > 0) {
2878 /* moving up the canvas */
2879 if ((atv2->order - canvas_pointer_y_span) >= visible_y_low) {
2881 int32_t visible_tracks = 0;
2882 while (visible_tracks < canvas_pointer_y_span ) {
2885 while (height_list[atv2->order - (visible_tracks - n)] == 0) {
2886 /* we're passing through a hidden track */
2891 if (tracks[atv2->order - (canvas_pointer_y_span - n)] != 0x00) {
2892 clamp_y_axis = true;
2896 clamp_y_axis = true;
2899 } else if (canvas_pointer_y_span < 0) {
2901 /*moving down the canvas*/
2903 if ((atv2->order - (canvas_pointer_y_span - n)) <= visible_y_high) { // we will overflow
2906 int32_t visible_tracks = 0;
2908 while (visible_tracks > canvas_pointer_y_span ) {
2911 while (height_list[atv2->order - (visible_tracks - n)] == 0) {
2915 if ( tracks[atv2->order - ( canvas_pointer_y_span - n)] != 0x00) {
2916 clamp_y_axis = true;
2921 clamp_y_axis = true;
2927 /* this is the pointer's track */
2928 if ((atv2->order - pointer_y_span) > visible_y_high) { // we will overflow
2929 clamp_y_axis = true;
2930 } else if ((atv2->order - pointer_y_span) < visible_y_low) { // we will underflow
2931 clamp_y_axis = true;
2939 } else if (drag_info.last_trackview == tv) {
2940 clamp_y_axis = true;
2944 if (!clamp_y_axis) {
2945 drag_info.last_trackview = tv;
2948 /************************************************************
2950 ************************************************************/
2952 /* compute the amount of pointer motion in frames, and where
2953 the region would be if we moved it by that much.
2956 if (drag_info.move_threshold_passsed) {
2958 if ((int32_t)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2960 jack_nframes_t sync_frame;
2961 jack_nframes_t sync_offset;
2964 pending_region_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2966 sync_offset = rv->region.sync_offset (sync_dir);
2967 sync_frame = rv->region.adjust_to_sync (pending_region_position);
2969 /* we snap if the snap modifier is not enabled.
2972 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2973 snap_to (sync_frame);
2976 if (sync_frame - sync_offset <= sync_frame) {
2977 pending_region_position = sync_frame - (sync_dir*sync_offset);
2979 pending_region_position = 0;
2983 pending_region_position = 0;
2986 if (pending_region_position > max_frames - rv->region.length()) {
2987 pending_region_position = drag_info.last_frame_position;
2990 // printf ("3: pending_region_position= %lu %lu\n", pending_region_position, drag_info.last_frame_position );
2992 if (pending_region_position != drag_info.last_frame_position && !drag_info.x_constrained) {
2994 /* now compute the canvas unit distance we need to move the regiondrag_info.last_trackview->order
2995 to make it appear at the new location.
2998 if (pending_region_position > drag_info.last_frame_position) {
2999 x_delta = ((double) (pending_region_position - drag_info.last_frame_position) / frames_per_unit);
3001 x_delta = -((double) (drag_info.last_frame_position - pending_region_position) / frames_per_unit);
3004 drag_info.last_frame_position = pending_region_position;
3011 /* threshold not passed */
3016 /*************************************************************
3018 ************************************************************/
3020 if (x_delta == 0 && (pointer_y_span == 0)) {
3021 /* haven't reached next snap point, and we're not switching
3022 trackviews. nothing to do.
3028 for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin(); i != selection->audio_regions.by_layer().end(); ++i) {
3030 AudioRegionView* rv2;
3033 /* if any regionview is at zero, we need to know so we can
3034 stop further leftward motion.
3037 double ix1, ix2, iy1, iy2;
3038 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3039 rv2->get_canvas_group()->i2w (ix1, iy1);
3048 /*************************************************************
3050 ************************************************************/
3052 pair<set<Playlist*>::iterator,bool> insert_result;
3053 const list<AudioRegionView*>& layered_regions = selection->audio_regions.by_layer();
3055 for (list<AudioRegionView*>::const_iterator i = layered_regions.begin(); i != layered_regions.end(); ++i) {
3057 AudioRegionView* rv;
3059 double ix1, ix2, iy1, iy2;
3060 int32_t temp_pointer_y_span = pointer_y_span;
3062 /* get item BBox, which will be relative to parent. so we have
3063 to query on a child, then convert to world coordinates using
3067 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3068 rv->get_canvas_group()->i2w (ix1, iy1);
3069 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3070 AudioTimeAxisView* canvas_atv = dynamic_cast<AudioTimeAxisView*>(tvp2);
3071 AudioTimeAxisView* temp_atv;
3073 if ((pointer_y_span != 0) && !clamp_y_axis) {
3076 for (j = height_list.begin(); j!= height_list.end(); j++) {
3077 if (x == canvas_atv->order) {
3078 /* we found the track the region is on */
3079 if (x != original_pointer_order) {
3080 /*this isn't from the same track we're dragging from */
3081 temp_pointer_y_span = canvas_pointer_y_span;
3083 while (temp_pointer_y_span > 0) {
3084 /* we're moving up canvas-wise,
3085 so we need to find the next track height
3087 if (j != height_list.begin()) {
3090 if (x != original_pointer_order) {
3091 /* we're not from the dragged track, so ignore hidden tracks. */
3093 temp_pointer_y_span++;
3097 temp_pointer_y_span--;
3099 while (temp_pointer_y_span < 0) {
3101 if (x != original_pointer_order) {
3103 temp_pointer_y_span--;
3107 if (j != height_list.end()) {
3110 temp_pointer_y_span++;
3112 /* find out where we'll be when we move and set height accordingly */
3114 tvp2 = trackview_by_y_position (iy1 + y_delta);
3115 temp_atv = dynamic_cast<AudioTimeAxisView*>(tvp2);
3116 rv->set_height (temp_atv->height);
3118 /* if you un-comment the following, the region colours will follow the track colours whilst dragging,
3119 personally, i think this can confuse things, but never mind.
3122 //const GdkColor& col (temp_atv->view->get_region_color());
3123 //rv->set_color (const_cast<GdkColor&>(col));
3130 /* prevent the regionview from being moved to before
3131 the zero position on the canvas.
3136 if (-x_delta > ix1) {
3139 } else if ((x_delta > 0) &&(rv->region.last_frame() > max_frames - x_delta)) {
3140 x_delta = max_frames - rv->region.last_frame();
3143 if (drag_info.first_move) {
3145 /* hide any dependent views */
3147 // rv->get_time_axis_view().hide_dependent_views (*rv);
3149 /* this is subtle. raising the regionview itself won't help,
3150 because raise_to_top() just puts the item on the top of
3151 its parent's stack. so, we need to put the trackview canvas_display group
3152 on the top, since its parent is the whole canvas.
3155 rv->get_canvas_group()->raise_to_top();
3156 rv->get_time_axis_view().canvas_display->raise_to_top();
3157 cursor_group->raise_to_top();
3159 /* freeze the playlists from notifying till
3163 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*> (&rv->get_time_axis_view());
3164 if (atv && atv->is_audio_track()) {
3165 AudioPlaylist* pl = atv->get_diskstream()->playlist();
3167 /* only freeze and capture state once */
3169 insert_result = motion_frozen_playlists.insert (pl);
3170 if (insert_result.second) {
3172 session->add_undo(pl->get_memento());
3178 if (drag_info.brushing) {
3179 mouse_brush_insert_region (rv, pending_region_position);
3181 rv->move (x_delta, y_delta);
3185 if (drag_info.first_move) {
3186 cursor_group->raise_to_top();
3189 drag_info.first_move = false;
3191 if (x_delta != 0 && !drag_info.brushing) {
3192 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3198 Editor::region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3200 jack_nframes_t where;
3201 AudioRegionView* rv = reinterpret_cast<AudioRegionView *> (drag_info.data);
3202 pair<set<Playlist*>::iterator,bool> insert_result;
3203 bool nocommit = true;
3205 AudioTimeAxisView* atv;
3206 bool regionview_y_movement;
3207 bool regionview_x_movement;
3209 /* first_move is set to false if the regionview has been moved in the
3213 if (drag_info.first_move) {
3220 /* The regionview has been moved at some stage during the grab so we need
3221 to account for any mouse movement between this event and the last one.
3224 region_drag_motion_callback (item, event);
3226 if (drag_info.brushing) {
3227 /* all changes were made during motion event handlers */
3231 /* adjust for track speed */
3234 atv = dynamic_cast<AudioTimeAxisView*> (drag_info.last_trackview);
3235 if (atv && atv->get_diskstream()) {
3236 speed = atv->get_diskstream()->speed();
3239 regionview_x_movement = (drag_info.last_frame_position != (jack_nframes_t) (rv->region.position()/speed));
3240 regionview_y_movement = (drag_info.last_trackview != &rv->get_time_axis_view());
3242 //printf ("last_frame: %s position is %lu %g\n", rv->get_time_axis_view().name().c_str(), drag_info.last_frame_position, speed);
3243 //printf ("last_rackview: %s \n", drag_info.last_trackview->name().c_str());
3245 if (regionview_y_movement) {
3247 /* motion between tracks */
3249 list<AudioRegionView*> new_selection;
3251 /* moved to a different audio track. */
3253 for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin(); i != selection->audio_regions.by_layer().end(); ) {
3255 AudioRegionView* rv2 = (*i);
3257 /* the region that used to be in the old playlist is not
3258 moved to the new one - we make a copy of it. as a result,
3259 any existing editor for the region should no longer be
3263 if (!drag_info.copy) {
3264 rv2->hide_region_editor();
3266 new_selection.push_back (rv2);
3270 /* first, freeze the target tracks */
3272 for (list<AudioRegionView*>::const_iterator i = new_selection.begin(); i != new_selection.end();i++ ) {
3274 Playlist* from_playlist;
3275 Playlist* to_playlist;
3277 double ix1, ix2, iy1, iy2;
3279 (*i)->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3280 (*i)->get_canvas_group()->i2w (ix1, iy1);
3281 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3282 AudioTimeAxisView* atv2 = dynamic_cast<AudioTimeAxisView*>(tvp2);
3284 from_playlist = (*i)->region.playlist();
3285 to_playlist = atv2->playlist();
3287 /* the from_playlist was frozen in the "first_move" case
3288 of the motion handler. the insert can fail,
3289 but that doesn't matter. it just means
3290 we already have the playlist in the list.
3293 motion_frozen_playlists.insert (from_playlist);
3295 /* only freeze the to_playlist once */
3297 insert_result = motion_frozen_playlists.insert(to_playlist);
3298 if (insert_result.second) {
3299 to_playlist->freeze();
3300 session->add_undo(to_playlist->get_memento());
3305 /* now do it again with the actual operations */
3307 for (list<AudioRegionView*>::const_iterator i = new_selection.begin(); i != new_selection.end();i++ ) {
3309 Playlist* from_playlist;
3310 Playlist* to_playlist;
3312 double ix1, ix2, iy1, iy2;
3314 (*i)->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3315 (*i)->get_canvas_group()->i2w (ix1, iy1);
3316 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3317 AudioTimeAxisView* atv2 = dynamic_cast<AudioTimeAxisView*>(tvp2);
3319 from_playlist = (*i)->region.playlist();
3320 to_playlist = atv2->playlist();
3322 latest_regionview = 0;
3324 where = (jack_nframes_t) (unit_to_frame (ix1) * speed);
3325 Region* new_region = createRegion ((*i)->region);
3327 from_playlist->remove_region (&((*i)->region));
3329 sigc::connection c = atv2->view->AudioRegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3330 to_playlist->add_region (*new_region, where);
3333 if (latest_regionview) {
3334 selection->add (latest_regionview);
3340 /* motion within a single track */
3342 for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin(); i != selection->audio_regions.by_layer().end(); ++i) {
3346 if (rv->region.locked()) {
3350 if (regionview_x_movement) {
3351 double ownspeed = 1.0;
3352 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*> (&(rv->get_time_axis_view()));
3354 if (atv && atv->get_diskstream()) {
3355 ownspeed = atv->get_diskstream()->speed();
3358 /* base the new region position on the current position of the regionview.*/
3360 double ix1, ix2, iy1, iy2;
3362 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3363 rv->get_canvas_group()->i2w (ix1, iy1);
3364 where = (jack_nframes_t) (unit_to_frame (ix1) * ownspeed);
3368 where = rv->region.position();
3371 rv->get_time_axis_view().reveal_dependent_views (*rv);
3373 /* no need to add an undo here, we did that when we added this playlist to motion_frozen playlists */
3375 rv->region.set_position (where, (void *) this);
3380 for (set<Playlist*>::iterator p = motion_frozen_playlists.begin(); p != motion_frozen_playlists.end(); ++p) {
3382 session->add_redo_no_execute ((*p)->get_memento());
3385 motion_frozen_playlists.clear ();
3388 commit_reversible_command ();
3393 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
3395 /* Either add to or set the set the region selection, unless
3396 this is an alignment click (control used)
3399 if (Keyboard::modifier_state_contains (event->state, Keyboard::Control)) {
3400 TimeAxisView* tv = &rv.get_time_axis_view();
3401 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*>(tv);
3403 if (atv && atv->is_audio_track()) {
3404 speed = atv->get_diskstream()->speed();
3407 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Alt))) {
3409 align_region (rv.region, SyncPoint, (jack_nframes_t) (edit_cursor->current_frame * speed));
3411 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Shift))) {
3413 align_region (rv.region, End, (jack_nframes_t) (edit_cursor->current_frame * speed));
3417 align_region (rv.region, Start, (jack_nframes_t) (edit_cursor->current_frame * speed));
3423 Editor::show_verbose_time_cursor (jack_nframes_t frame, double offset, double xpos, double ypos)
3434 switch (ARDOUR_UI::instance()->secondary_clock.mode ()) {
3435 case AudioClock::BBT:
3436 session->bbt_time (frame, bbt);
3437 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, bbt.bars, bbt.beats, bbt.ticks);
3440 case AudioClock::SMPTE:
3441 session->smpte_time (frame, smpte);
3442 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
3445 case AudioClock::MinSec:
3446 /* XXX fix this to compute min/sec properly */
3447 session->smpte_time (frame, smpte);
3448 secs = smpte.seconds + ((float) smpte.frames / session->smpte_frames_per_second);
3449 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", smpte.hours, smpte.minutes, secs);
3453 snprintf (buf, sizeof(buf), "%u", frame);
3457 if (xpos >= 0 && ypos >=0) {
3458 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
3461 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
3463 show_verbose_canvas_cursor ();
3467 Editor::show_verbose_duration_cursor (jack_nframes_t start, jack_nframes_t end, double offset, double xpos, double ypos)
3474 Meter meter_at_start(session->tempo_map().meter_at(start));
3480 switch (ARDOUR_UI::instance()->secondary_clock.mode ()) {
3481 case AudioClock::BBT:
3482 session->bbt_time (start, sbbt);
3483 session->bbt_time (end, ebbt);
3486 /* XXX this computation won't work well if the
3487 user makes a selection that spans any meter changes.
3490 ebbt.bars -= sbbt.bars;
3491 if (ebbt.beats >= sbbt.beats) {
3492 ebbt.beats -= sbbt.beats;
3495 ebbt.beats = int(meter_at_start.beats_per_bar()) + ebbt.beats - sbbt.beats;
3497 if (ebbt.ticks >= sbbt.ticks) {
3498 ebbt.ticks -= sbbt.ticks;
3501 ebbt.ticks = int(Meter::ticks_per_beat) + ebbt.ticks - sbbt.ticks;
3504 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, ebbt.bars, ebbt.beats, ebbt.ticks);
3507 case AudioClock::SMPTE:
3508 session->smpte_duration (end - start, smpte);
3509 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
3512 case AudioClock::MinSec:
3513 /* XXX fix this to compute min/sec properly */
3514 session->smpte_duration (end - start, smpte);
3515 secs = smpte.seconds + ((float) smpte.frames / session->smpte_frames_per_second);
3516 snprintf (buf, sizeof (buf), "%02ld:%02ld:%.4f", smpte.hours, smpte.minutes, secs);
3520 snprintf (buf, sizeof(buf), "%u", end - start);
3524 if (xpos >= 0 && ypos >=0) {
3525 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
3528 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
3530 show_verbose_canvas_cursor ();
3534 Editor::collect_new_region_view (AudioRegionView* rv)
3536 latest_regionview = rv;
3540 Editor::start_selection_grab (ArdourCanvas::Item* item, GdkEvent* event)
3542 if (clicked_regionview == 0) {
3546 /* lets try to create new Region for the selection */
3548 vector<AudioRegion*> new_regions;
3549 create_region_from_selection (new_regions);
3551 if (new_regions.empty()) {
3555 /* XXX fix me one day to use all new regions */
3557 Region* region = new_regions.front();
3559 /* add it to the current stream/playlist.
3561 tricky: the streamview for the track will add a new regionview. we will
3562 catch the signal it sends when it creates the regionview to
3563 set the regionview we want to then drag.
3566 latest_regionview = 0;
3567 sigc::connection c = clicked_audio_trackview->view->AudioRegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3569 /* A selection grab currently creates two undo/redo operations, one for
3570 creating the new region and another for moving it.
3573 begin_reversible_command (_("selection grab"));
3575 Playlist* playlist = clicked_trackview->playlist();
3577 session->add_undo (playlist->get_memento ());
3578 clicked_trackview->playlist()->add_region (*region, selection->time[clicked_selection].start);
3579 session->add_redo_no_execute (playlist->get_memento ());
3581 commit_reversible_command ();
3585 if (latest_regionview == 0) {
3586 /* something went wrong */
3590 /* we need to deselect all other regionviews, and select this one
3591 i'm ignoring undo stuff, because the region creation will take care of it */
3592 selection->set (latest_regionview);
3594 drag_info.item = latest_regionview->get_canvas_group();
3595 drag_info.data = latest_regionview;
3596 drag_info.motion_callback = &Editor::region_drag_motion_callback;
3597 drag_info.finished_callback = &Editor::region_drag_finished_callback;
3601 drag_info.last_trackview = clicked_trackview;
3602 drag_info.last_frame_position = latest_regionview->region.position();
3603 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
3605 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3609 Editor::cancel_selection ()
3611 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
3612 (*i)->hide_selection ();
3614 begin_reversible_command (_("cancel selection"));
3615 selection->clear ();
3616 clicked_selection = 0;
3617 commit_reversible_command ();
3621 Editor::start_selection_op (ArdourCanvas::Item* item, GdkEvent* event, SelectionOp op)
3623 jack_nframes_t start = 0;
3624 jack_nframes_t end = 0;
3630 drag_info.item = item;
3631 drag_info.motion_callback = &Editor::drag_selection;
3632 drag_info.finished_callback = &Editor::end_selection_op;
3637 case CreateSelection:
3639 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
3640 drag_info.copy = true;
3642 drag_info.copy = false;
3644 start_grab (event, selector_cursor);
3647 case SelectionStartTrim:
3648 clicked_trackview->order_selection_trims (item, true);
3649 start_grab (event, trimmer_cursor);
3650 start = selection->time[clicked_selection].start;
3651 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
3654 case SelectionEndTrim:
3655 clicked_trackview->order_selection_trims (item, false);
3656 start_grab (event, trimmer_cursor);
3657 end = selection->time[clicked_selection].end;
3658 drag_info.pointer_frame_offset = drag_info.grab_frame - end;
3662 start = selection->time[clicked_selection].start;
3664 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
3668 if (selection_op == SelectionMove) {
3669 show_verbose_time_cursor(start, 10);
3671 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
3676 Editor::drag_selection (ArdourCanvas::Item* item, GdkEvent* event)
3678 jack_nframes_t start = 0;
3679 jack_nframes_t end = 0;
3680 jack_nframes_t length;
3681 jack_nframes_t pending_position;
3683 if ((int32_t) drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
3684 pending_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
3687 pending_position = 0;
3690 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3691 snap_to (pending_position);
3694 /* only alter selection if the current frame is
3695 different from the last frame position (adjusted)
3698 if (pending_position == drag_info.last_pointer_frame) return;
3700 switch (selection_op) {
3701 case CreateSelection:
3703 if (drag_info.first_move) {
3704 snap_to (drag_info.grab_frame);
3707 if (pending_position < drag_info.grab_frame) {
3708 start = pending_position;
3709 end = drag_info.grab_frame;
3711 end = pending_position;
3712 start = drag_info.grab_frame;
3715 /* first drag: Either add to the selection
3716 or create a new selection->
3719 if (drag_info.first_move) {
3721 begin_reversible_command (_("range selection"));
3723 if (drag_info.copy) {
3724 /* adding to the selection */
3725 clicked_selection = selection->add (start, end);
3726 drag_info.copy = false;
3728 /* new selection-> */
3729 clicked_selection = selection->set (clicked_trackview, start, end);
3734 case SelectionStartTrim:
3736 if (drag_info.first_move) {
3737 begin_reversible_command (_("trim selection start"));
3740 start = selection->time[clicked_selection].start;
3741 end = selection->time[clicked_selection].end;
3743 if (pending_position > end) {
3746 start = pending_position;
3750 case SelectionEndTrim:
3752 if (drag_info.first_move) {
3753 begin_reversible_command (_("trim selection end"));
3756 start = selection->time[clicked_selection].start;
3757 end = selection->time[clicked_selection].end;
3759 if (pending_position < start) {
3762 end = pending_position;
3769 if (drag_info.first_move) {
3770 begin_reversible_command (_("move selection"));
3773 start = selection->time[clicked_selection].start;
3774 end = selection->time[clicked_selection].end;
3776 length = end - start;
3778 start = pending_position;
3781 end = start + length;
3786 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
3787 start_canvas_autoscroll (1);
3791 selection->replace (clicked_selection, start, end);
3794 drag_info.last_pointer_frame = pending_position;
3795 drag_info.first_move = false;
3797 if (selection_op == SelectionMove) {
3798 show_verbose_time_cursor(start, 10);
3800 show_verbose_time_cursor(pending_position, 10);
3805 Editor::end_selection_op (ArdourCanvas::Item* item, GdkEvent* event)
3807 if (!drag_info.first_move) {
3808 drag_selection (item, event);
3809 /* XXX this is not object-oriented programming at all. ick */
3810 if (selection->time.consolidate()) {
3811 selection->TimeChanged ();
3813 commit_reversible_command ();
3815 /* just a click, no pointer movement.*/
3817 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
3819 selection->clear_time();
3824 /* XXX what happens if its a music selection? */
3825 session->set_audio_range (selection->time);
3826 stop_canvas_autoscroll ();
3830 Editor::start_trim (ArdourCanvas::Item* item, GdkEvent* event)
3833 TimeAxisView* tvp = clicked_trackview;
3834 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
3836 if (tv && tv->is_audio_track()) {
3837 speed = tv->get_diskstream()->speed();
3840 jack_nframes_t region_start = (jack_nframes_t) (clicked_regionview->region.position() / speed);
3841 jack_nframes_t region_end = (jack_nframes_t) (clicked_regionview->region.last_frame() / speed);
3842 jack_nframes_t region_length = (jack_nframes_t) (clicked_regionview->region.length() / speed);
3844 motion_frozen_playlists.clear();
3846 //drag_info.item = clicked_regionview->get_name_highlight();
3847 drag_info.item = item;
3848 drag_info.motion_callback = &Editor::trim_motion_callback;
3849 drag_info.finished_callback = &Editor::trim_finished_callback;
3851 start_grab (event, trimmer_cursor);
3853 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
3854 trim_op = ContentsTrim;
3856 /* These will get overridden for a point trim.*/
3857 if (drag_info.current_pointer_frame < (region_start + region_length/2)) {
3858 /* closer to start */
3859 trim_op = StartTrim;
3860 } else if (drag_info.current_pointer_frame > (region_end - region_length/2)) {
3868 show_verbose_time_cursor(region_start, 10);
3871 show_verbose_time_cursor(region_end, 10);
3874 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
3880 Editor::trim_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
3882 AudioRegionView* rv = clicked_regionview;
3883 jack_nframes_t frame_delta = 0;
3884 bool left_direction;
3885 bool obey_snap = !Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier());
3887 /* snap modifier works differently here..
3888 its' current state has to be passed to the
3889 various trim functions in order to work properly
3893 TimeAxisView* tvp = clicked_trackview;
3894 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
3895 pair<set<Playlist*>::iterator,bool> insert_result;
3897 if (tv && tv->is_audio_track()) {
3898 speed = tv->get_diskstream()->speed();
3901 if (drag_info.last_pointer_frame > drag_info.current_pointer_frame) {
3902 left_direction = true;
3904 left_direction = false;
3908 snap_to (drag_info.current_pointer_frame);
3911 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
3915 if (drag_info.first_move) {
3921 trim_type = "Region start trim";
3924 trim_type = "Region end trim";
3927 trim_type = "Region content trim";
3931 begin_reversible_command (trim_type);
3933 for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin(); i != selection->audio_regions.by_layer().end(); ++i) {
3934 (*i)->region.freeze ();
3935 (*i)->temporarily_hide_envelope ();
3937 Playlist * pl = (*i)->region.playlist();
3938 insert_result = motion_frozen_playlists.insert (pl);
3939 if (insert_result.second) {
3940 session->add_undo (pl->get_memento());
3945 if (left_direction) {
3946 frame_delta = (drag_info.last_pointer_frame - drag_info.current_pointer_frame);
3948 frame_delta = (drag_info.current_pointer_frame - drag_info.last_pointer_frame);
3953 if ((left_direction == false) && (drag_info.current_pointer_frame <= rv->region.first_frame()/speed)) {
3956 for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin(); i != selection->audio_regions.by_layer().end(); ++i) {
3957 single_start_trim (**i, frame_delta, left_direction, obey_snap);
3963 if ((left_direction == true) && (drag_info.current_pointer_frame > (jack_nframes_t) (rv->region.last_frame()/speed))) {
3966 for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin(); i != selection->audio_regions.by_layer().end(); ++i) {
3967 single_end_trim (**i, frame_delta, left_direction, obey_snap);
3974 bool swap_direction = false;
3976 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
3977 swap_direction = true;
3980 for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin();
3981 i != selection->audio_regions.by_layer().end(); ++i)
3983 single_contents_trim (**i, frame_delta, left_direction, swap_direction, obey_snap);
3991 show_verbose_time_cursor((jack_nframes_t) (rv->region.position()/speed), 10);
3994 show_verbose_time_cursor((jack_nframes_t) (rv->region.last_frame()/speed), 10);
3997 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4001 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4002 drag_info.first_move = false;
4006 Editor::single_contents_trim (AudioRegionView& rv, jack_nframes_t frame_delta, bool left_direction, bool swap_direction, bool obey_snap)
4008 Region& region (rv.region);
4010 if (region.locked()) {
4014 jack_nframes_t new_bound;
4017 TimeAxisView* tvp = clicked_trackview;
4018 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
4020 if (tv && tv->is_audio_track()) {
4021 speed = tv->get_diskstream()->speed();
4024 if (left_direction) {
4025 if (swap_direction) {
4026 new_bound = (jack_nframes_t) (region.position()/speed) + frame_delta;
4028 new_bound = (jack_nframes_t) (region.position()/speed) - frame_delta;
4031 if (swap_direction) {
4032 new_bound = (jack_nframes_t) (region.position()/speed) - frame_delta;
4034 new_bound = (jack_nframes_t) (region.position()/speed) + frame_delta;
4039 snap_to (new_bound);
4041 region.trim_start ((jack_nframes_t) (new_bound * speed), this);
4042 rv.region_changed (StartChanged);
4046 Editor::single_start_trim (AudioRegionView& rv, jack_nframes_t frame_delta, bool left_direction, bool obey_snap)
4048 Region& region (rv.region);
4050 if (region.locked()) {
4054 jack_nframes_t new_bound;
4057 TimeAxisView* tvp = clicked_trackview;
4058 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
4060 if (tv && tv->is_audio_track()) {
4061 speed = tv->get_diskstream()->speed();
4064 if (left_direction) {
4065 new_bound = (jack_nframes_t) (region.position()/speed) - frame_delta;
4067 new_bound = (jack_nframes_t) (region.position()/speed) + frame_delta;
4071 snap_to (new_bound, (left_direction ? 0 : 1));
4074 region.trim_front ((jack_nframes_t) (new_bound * speed), this);
4076 rv.region_changed (Change (LengthChanged|PositionChanged|StartChanged));
4080 Editor::single_end_trim (AudioRegionView& rv, jack_nframes_t frame_delta, bool left_direction, bool obey_snap)
4082 Region& region (rv.region);
4084 if (region.locked()) {
4088 jack_nframes_t new_bound;
4091 TimeAxisView* tvp = clicked_trackview;
4092 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
4094 if (tv && tv->is_audio_track()) {
4095 speed = tv->get_diskstream()->speed();
4098 if (left_direction) {
4099 new_bound = (jack_nframes_t) ((region.last_frame() + 1)/speed) - frame_delta;
4101 new_bound = (jack_nframes_t) ((region.last_frame() + 1)/speed) + frame_delta;
4105 snap_to (new_bound);
4107 region.trim_end ((jack_nframes_t) (new_bound * speed), this);
4108 rv.region_changed (LengthChanged);
4112 Editor::trim_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
4114 if (!drag_info.first_move) {
4115 trim_motion_callback (item, event);
4117 if (!clicked_regionview->get_selected()) {
4118 thaw_region_after_trim (*clicked_regionview);
4121 for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin();
4122 i != selection->audio_regions.by_layer().end(); ++i)
4124 thaw_region_after_trim (**i);
4128 for (set<Playlist*>::iterator p = motion_frozen_playlists.begin(); p != motion_frozen_playlists.end(); ++p) {
4130 session->add_redo_no_execute ((*p)->get_memento());
4133 motion_frozen_playlists.clear ();
4135 commit_reversible_command();
4137 /* no mouse movement */
4143 Editor::point_trim (GdkEvent* event)
4145 AudioRegionView* rv = clicked_regionview;
4146 jack_nframes_t new_bound = drag_info.current_pointer_frame;
4148 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4149 snap_to (new_bound);
4152 /* Choose action dependant on which button was pressed */
4153 switch (event->button.button) {
4155 trim_op = StartTrim;
4156 begin_reversible_command (_("Start point trim"));
4158 if (rv->get_selected()) {
4160 for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin();
4161 i != selection->audio_regions.by_layer().end(); ++i)
4163 if (!(*i)->region.locked()) {
4164 session->add_undo ((*i)->region.playlist()->get_memento());
4165 (*i)->region.trim_front (new_bound, this);
4166 session->add_redo_no_execute ((*i)->region.playlist()->get_memento());
4172 if (!rv->region.locked()) {
4173 session->add_undo (rv->region.playlist()->get_memento());
4174 rv->region.trim_front (new_bound, this);
4175 session->add_redo_no_execute (rv->region.playlist()->get_memento());
4179 commit_reversible_command();
4184 begin_reversible_command (_("End point trim"));
4186 if (rv->get_selected()) {
4188 for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin(); i != selection->audio_regions.by_layer().end(); ++i)
4190 if (!(*i)->region.locked()) {
4191 session->add_undo ((*i)->region.playlist()->get_memento());
4192 (*i)->region.trim_end (new_bound, this);
4193 session->add_redo_no_execute ((*i)->region.playlist()->get_memento());
4199 if (!rv->region.locked()) {
4200 session->add_undo (rv->region.playlist()->get_memento());
4201 rv->region.trim_end (new_bound, this);
4202 session->add_redo_no_execute (rv->region.playlist()->get_memento());
4206 commit_reversible_command();
4215 Editor::thaw_region_after_trim (AudioRegionView& rv)
4217 Region& region (rv.region);
4219 if (region.locked()) {
4223 region.thaw (_("trimmed region"));
4224 session->add_redo_no_execute (region.playlist()->get_memento());
4226 rv.unhide_envelope ();
4230 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* event)
4235 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
4236 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
4240 Location* location = find_location_from_marker (marker, is_start);
4241 location->set_hidden (true, this);
4246 Editor::start_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event, RangeMarkerOp op)
4253 drag_info.item = item;
4254 drag_info.motion_callback = &Editor::drag_range_markerbar_op;
4255 drag_info.finished_callback = &Editor::end_range_markerbar_op;
4257 range_marker_op = op;
4259 if (!temp_location) {
4260 temp_location = new Location;
4264 case CreateRangeMarker:
4265 case CreateTransportMarker:
4267 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
4268 drag_info.copy = true;
4270 drag_info.copy = false;
4272 start_grab (event, selector_cursor);
4276 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4281 Editor::drag_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4283 jack_nframes_t start = 0;
4284 jack_nframes_t end = 0;
4285 ArdourCanvas::SimpleRect *crect = (range_marker_op == CreateRangeMarker) ? range_bar_drag_rect: transport_bar_drag_rect;
4287 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4288 snap_to (drag_info.current_pointer_frame);
4291 /* only alter selection if the current frame is
4292 different from the last frame position.
4295 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4297 switch (range_marker_op) {
4298 case CreateRangeMarker:
4299 case CreateTransportMarker:
4300 if (drag_info.first_move) {
4301 snap_to (drag_info.grab_frame);
4304 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4305 start = drag_info.current_pointer_frame;
4306 end = drag_info.grab_frame;
4308 end = drag_info.current_pointer_frame;
4309 start = drag_info.grab_frame;
4312 /* first drag: Either add to the selection
4313 or create a new selection->
4316 if (drag_info.first_move) {
4318 temp_location->set (start, end);
4322 update_marker_drag_item (temp_location);
4323 range_marker_drag_rect->show();
4324 range_marker_drag_rect->raise_to_top();
4330 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
4331 start_canvas_autoscroll (1);
4335 temp_location->set (start, end);
4337 double x1 = frame_to_pixel (start);
4338 double x2 = frame_to_pixel (end);
4339 crect->property_x1() = x1;
4340 crect->property_x2() = x2;
4342 update_marker_drag_item (temp_location);
4345 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4346 drag_info.first_move = false;
4348 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4353 Editor::end_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4355 Location * newloc = 0;
4357 if (!drag_info.first_move) {
4358 drag_range_markerbar_op (item, event);
4360 switch (range_marker_op) {
4361 case CreateRangeMarker:
4362 begin_reversible_command (_("new range marker"));
4363 session->add_undo (session->locations()->get_memento());
4364 newloc = new Location(temp_location->start(), temp_location->end(), "unnamed", Location::IsRangeMarker);
4365 session->locations()->add (newloc, true);
4366 session->add_redo_no_execute (session->locations()->get_memento());
4367 commit_reversible_command ();
4369 range_bar_drag_rect->hide();
4370 range_marker_drag_rect->hide();
4373 case CreateTransportMarker:
4374 // popup menu to pick loop or punch
4375 new_transport_marker_context_menu (&event->button, item);
4380 /* just a click, no pointer movement.*/
4382 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
4389 stop_canvas_autoscroll ();
4395 Editor::start_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4397 drag_info.item = item;
4398 drag_info.motion_callback = &Editor::drag_mouse_zoom;
4399 drag_info.finished_callback = &Editor::end_mouse_zoom;
4401 start_grab (event, zoom_cursor);
4403 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4407 Editor::drag_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4409 jack_nframes_t start;
4412 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4413 snap_to (drag_info.current_pointer_frame);
4415 if (drag_info.first_move) {
4416 snap_to (drag_info.grab_frame);
4420 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4422 /* base start and end on initial click position */
4423 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4424 start = drag_info.current_pointer_frame;
4425 end = drag_info.grab_frame;
4427 end = drag_info.current_pointer_frame;
4428 start = drag_info.grab_frame;
4433 if (drag_info.first_move) {
4435 zoom_rect->raise_to_top();
4438 reposition_zoom_rect(start, end);
4440 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4441 drag_info.first_move = false;
4443 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4448 Editor::end_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4450 if (!drag_info.first_move) {
4451 drag_mouse_zoom (item, event);
4453 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
4454 temporal_zoom_by_frame (drag_info.grab_frame, drag_info.last_pointer_frame, "mouse zoom");
4456 temporal_zoom_by_frame (drag_info.last_pointer_frame, drag_info.grab_frame, "mouse zoom");
4459 temporal_zoom_to_frame (false, drag_info.grab_frame);
4461 temporal_zoom_step (false);
4462 center_screen (drag_info.grab_frame);
4470 Editor::reposition_zoom_rect (jack_nframes_t start, jack_nframes_t end)
4472 double x1 = frame_to_pixel (start);
4473 double x2 = frame_to_pixel (end);
4474 double y2 = canvas_height - 2;
4476 zoom_rect->property_x1() = x1;
4477 zoom_rect->property_y1() = 1.0;
4478 zoom_rect->property_x2() = x2;
4479 zoom_rect->property_y2() = y2;
4483 Editor::start_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4485 drag_info.item = item;
4486 drag_info.motion_callback = &Editor::drag_rubberband_select;
4487 drag_info.finished_callback = &Editor::end_rubberband_select;
4489 start_grab (event, cross_hair_cursor);
4491 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4495 Editor::drag_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4497 jack_nframes_t start;
4502 /* use a bigger drag threshold than the default */
4504 if (abs ((int) (drag_info.current_pointer_frame - drag_info.grab_frame)) < 8) {
4508 // if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4509 // snap_to (drag_info.current_pointer_frame);
4511 // if (drag_info.first_move) {
4512 // snap_to (drag_info.grab_frame);
4517 /* base start and end on initial click position */
4518 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4519 start = drag_info.current_pointer_frame;
4520 end = drag_info.grab_frame;
4522 end = drag_info.current_pointer_frame;
4523 start = drag_info.grab_frame;
4526 if (drag_info.current_pointer_y < drag_info.grab_y) {
4527 y1 = drag_info.current_pointer_y;
4528 y2 = drag_info.grab_y;
4531 y2 = drag_info.current_pointer_y;
4532 y1 = drag_info.grab_y;
4536 if (start != end || y1 != y2) {
4538 double x1 = frame_to_pixel (start);
4539 double x2 = frame_to_pixel (end);
4541 rubberband_rect->property_x1() = x1;
4542 rubberband_rect->property_y1() = y1;
4543 rubberband_rect->property_x2() = x2;
4544 rubberband_rect->property_y2() = y2;
4546 rubberband_rect->show();
4547 rubberband_rect->raise_to_top();
4549 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4550 drag_info.first_move = false;
4552 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4557 Editor::end_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4559 if (!drag_info.first_move) {
4561 drag_rubberband_select (item, event);
4564 if (drag_info.current_pointer_y < drag_info.grab_y) {
4565 y1 = drag_info.current_pointer_y;
4566 y2 = drag_info.grab_y;
4569 y2 = drag_info.current_pointer_y;
4570 y1 = drag_info.grab_y;
4574 Selection::Operation op = Keyboard::selection_type (event->button.state);
4577 begin_reversible_command (_("select regions"));
4579 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
4580 commit = select_all_within (drag_info.grab_frame, drag_info.last_pointer_frame, y1, y2, op);
4582 commit = select_all_within (drag_info.last_pointer_frame, drag_info.grab_frame, y1, y2, op);
4586 commit_reversible_command ();
4590 selection->clear_audio_regions();
4591 selection->clear_points ();
4592 selection->clear_lines ();
4595 rubberband_rect->hide();
4600 Editor::mouse_rename_region (ArdourCanvas::Item* item, GdkEvent* event)
4602 using namespace Gtkmm2ext;
4604 ArdourPrompter prompter (false);
4606 prompter.set_prompt (_("Name for region:"));
4607 prompter.set_initial_text (clicked_regionview->region.name());
4608 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
4609 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
4610 prompter.show_all ();
4611 switch (prompter.run ()) {
4612 case Gtk::RESPONSE_ACCEPT:
4614 prompter.get_result(str);
4616 clicked_regionview->region.set_name (str);
4624 Editor::start_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
4626 drag_info.item = item;
4627 drag_info.motion_callback = &Editor::time_fx_motion;
4628 drag_info.finished_callback = &Editor::end_time_fx;
4632 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4636 Editor::time_fx_motion (ArdourCanvas::Item *item, GdkEvent* event)
4638 AudioRegionView* rv = clicked_regionview;
4640 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4641 snap_to (drag_info.current_pointer_frame);
4644 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
4648 if (drag_info.current_pointer_frame > rv->region.position()) {
4649 rv->get_time_axis_view().show_timestretch (rv->region.position(), drag_info.current_pointer_frame);
4652 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4653 drag_info.first_move = false;
4655 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4659 Editor::end_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
4661 clicked_regionview->get_time_axis_view().hide_timestretch ();
4663 if (drag_info.first_move) {
4667 jack_nframes_t newlen = drag_info.last_pointer_frame - clicked_regionview->region.position();
4668 float percentage = (float) ((double) newlen - (double) clicked_regionview->region.length()) / ((double) newlen) * 100.0f;
4670 begin_reversible_command (_("timestretch"));
4672 if (run_timestretch (selection->audio_regions, percentage) == 0) {
4673 session->commit_reversible_command ();
4678 Editor::mouse_brush_insert_region (AudioRegionView* rv, jack_nframes_t pos)
4680 /* no brushing without a useful snap setting */
4682 switch (snap_mode) {
4684 return; /* can't work because it allows region to be placed anywhere */
4689 switch (snap_type) {
4692 case SnapToEditCursor:
4699 /* don't brush a copy over the original */
4701 if (pos == rv->region.position()) {
4705 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*>(&rv->get_time_axis_view());
4707 if (atv == 0 || !atv->is_audio_track()) {
4711 Playlist* playlist = atv->playlist();
4712 double speed = atv->get_diskstream()->speed();
4714 session->add_undo (playlist->get_memento());
4715 playlist->add_region (*(new AudioRegion (rv->region)), (jack_nframes_t) (pos * speed));
4716 session->add_redo_no_execute (playlist->get_memento());
4718 // playlist is frozen, so we have to update manually
4720 playlist->StateChanged (Change (~0)); /* EMIT SIGNAL */
4724 Editor::track_height_step_timeout ()
4727 struct timeval delta;
4729 gettimeofday (&now, 0);
4730 timersub (&now, &last_track_height_step_timestamp, &delta);
4732 if (delta.tv_sec * 1000000 + delta.tv_usec > 250000) { /* milliseconds */
4733 current_stepping_trackview = 0;