2 Copyright (C) 2000-2001 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
28 #include <pbd/error.h>
29 #include <gtkmm2ext/utils.h>
31 #include "ardour_ui.h"
33 #include "time_axis_view.h"
34 #include "audio_time_axis.h"
35 #include "regionview.h"
37 #include "streamview.h"
38 #include "region_gain_line.h"
39 #include "automation_time_axis.h"
42 #include "selection.h"
45 #include "rgb_macros.h"
47 #include <ardour/types.h>
48 #include <ardour/route.h>
49 #include <ardour/audio_track.h>
50 #include <ardour/diskstream.h>
51 #include <ardour/playlist.h>
52 #include <ardour/audioplaylist.h>
53 #include <ardour/audioregion.h>
54 #include <ardour/dB.h>
55 #include <ardour/utils.h>
56 #include <ardour/region_factory.h>
63 using namespace ARDOUR;
66 using namespace Editing;
69 Editor::event_frame (GdkEvent* event, double* pcx, double* pcy)
83 switch (event->type) {
84 case GDK_BUTTON_RELEASE:
85 case GDK_BUTTON_PRESS:
86 case GDK_2BUTTON_PRESS:
87 case GDK_3BUTTON_PRESS:
88 track_canvas.w2c(event->button.x, event->button.y, *pcx, *pcy);
90 case GDK_MOTION_NOTIFY:
91 track_canvas.w2c(event->motion.x, event->motion.y, *pcx, *pcy);
93 case GDK_ENTER_NOTIFY:
94 case GDK_LEAVE_NOTIFY:
95 track_canvas.w2c(event->crossing.x, event->crossing.y, *pcx, *pcy);
100 // track_canvas.w2c(event->key.x, event->key.y, *pcx, *pcy);
103 warning << string_compose (_("Editor::event_frame() used on unhandled event type %1"), event->type) << endmsg;
107 /* note that pixel_to_frame() never returns less than zero, so even if the pixel
108 position is negative (as can be the case with motion events in particular),
109 the frame location is always positive.
112 return pixel_to_frame (*pcx);
116 Editor::mouse_mode_toggled (MouseMode m)
118 if (ignore_mouse_mode_toggle) {
124 if (mouse_select_button.get_active()) {
130 if (mouse_move_button.get_active()) {
136 if (mouse_gain_button.get_active()) {
142 if (mouse_zoom_button.get_active()) {
148 if (mouse_timefx_button.get_active()) {
154 if (mouse_audition_button.get_active()) {
165 Editor::set_mouse_mode (MouseMode m, bool force)
167 if (drag_info.item) {
171 if (m == mouse_mode && !force) {
179 if (mouse_mode != MouseRange) {
181 /* in all modes except range, hide the range selection,
182 show the object (region) selection.
185 for (AudioRegionSelection::iterator i = selection->audio_regions.begin(); i != selection->audio_regions.end(); ++i) {
186 (*i)->set_should_show_selection (true);
188 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
189 (*i)->hide_selection ();
195 in range mode,show the range selection.
198 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
199 if ((*i)->selected()) {
200 (*i)->show_selection (selection->time);
205 /* XXX the hack of unsetting all other buttongs should go
206 away once GTK2 allows us to use regular radio buttons drawn like
207 normal buttons, rather than my silly GroupedButton hack.
210 ignore_mouse_mode_toggle = true;
212 switch (mouse_mode) {
214 mouse_select_button.set_active (true);
215 current_canvas_cursor = selector_cursor;
219 mouse_move_button.set_active (true);
220 current_canvas_cursor = grabber_cursor;
224 mouse_gain_button.set_active (true);
225 current_canvas_cursor = cross_hair_cursor;
229 mouse_zoom_button.set_active (true);
230 current_canvas_cursor = zoom_cursor;
234 mouse_timefx_button.set_active (true);
235 current_canvas_cursor = time_fx_cursor; // just use playhead
239 mouse_audition_button.set_active (true);
240 current_canvas_cursor = speaker_cursor;
244 ignore_mouse_mode_toggle = false;
247 track_canvas.get_window()->set_cursor(*current_canvas_cursor);
252 Editor::step_mouse_mode (bool next)
254 switch (current_mouse_mode()) {
256 if (next) set_mouse_mode (MouseRange);
257 else set_mouse_mode (MouseTimeFX);
261 if (next) set_mouse_mode (MouseZoom);
262 else set_mouse_mode (MouseObject);
266 if (next) set_mouse_mode (MouseGain);
267 else set_mouse_mode (MouseRange);
271 if (next) set_mouse_mode (MouseTimeFX);
272 else set_mouse_mode (MouseZoom);
276 if (next) set_mouse_mode (MouseAudition);
277 else set_mouse_mode (MouseGain);
281 if (next) set_mouse_mode (MouseObject);
282 else set_mouse_mode (MouseTimeFX);
288 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
290 jack_nframes_t where = event_frame (event, 0, 0);
292 track_canvas.grab_focus();
294 if (session && session->actively_recording()) {
298 /* in object/audition/timefx mode, any button press sets
299 the selection if the object can be selected. this is a
300 bit of hack, because we want to avoid this if the
301 mouse operation is a region alignment.
304 if (((mouse_mode == MouseObject) ||
305 (mouse_mode == MouseAudition && item_type == RegionItem) ||
306 (mouse_mode == MouseTimeFX && item_type == RegionItem)) &&
307 event->type == GDK_BUTTON_PRESS &&
308 event->button.button <= 3) {
313 /* not dbl-click or triple-click */
317 set_selected_regionview_from_click (Keyboard::selection_type (event->button.state), true);
320 case AudioRegionViewNameHighlight:
321 case AudioRegionViewName:
322 if ((rv = static_cast<AudioRegionView *> (item->get_data ("regionview"))) != 0) {
323 set_selected_regionview_from_click (Keyboard::selection_type (event->button.state), true);
327 case GainAutomationControlPointItem:
328 case PanAutomationControlPointItem:
329 case RedirectAutomationControlPointItem:
330 if ((cp = static_cast<ControlPoint *> (item->get_data ("control_point"))) != 0) {
331 set_selected_control_point_from_click (Keyboard::selection_type (event->button.state), true);
338 case AutomationTrackItem:
346 #define SELECT_TRACK_FROM_CANVAS_IN_RANGE_MODE
347 #ifdef SELECT_TRACK_FROM_CANVAS_IN_RANGE_MODE
348 /* in range mode, button 1/2/3 press potentially selects a track */
350 if (mouse_mode == MouseRange &&
351 event->type == GDK_BUTTON_PRESS &&
352 event->button.button <= 3) {
359 case AutomationTrackItem:
360 set_selected_track_from_click (Keyboard::selection_type (event->button.state), true, true);
363 case AudioRegionViewNameHighlight:
364 case AudioRegionViewName:
365 rv = reinterpret_cast<AudioRegionView *> (item->get_data ("regionview"));
372 if (drag_info.item == 0 &&
373 (Keyboard::is_delete_event (&event->button) ||
374 Keyboard::is_context_menu_event (&event->button) ||
375 Keyboard::is_edit_event (&event->button))) {
377 /* handled by button release */
381 switch (event->button.button) {
384 if (event->type == GDK_BUTTON_PRESS) {
386 if (drag_info.item) {
387 drag_info.item->ungrab (event->button.time);
390 /* single mouse clicks on any of these item types operate
391 independent of mouse mode, mostly because they are
392 not on the main track canvas or because we want
398 case PlayheadCursorItem:
399 start_cursor_grab (item, event);
403 if (Keyboard::modifier_state_equals (event->button.state,
404 Keyboard::ModifierMask(Keyboard::Control|Keyboard::Shift))) {
405 hide_marker (item, event);
407 start_marker_grab (item, event);
411 case TempoMarkerItem:
412 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
413 start_tempo_marker_copy_grab (item, event);
415 start_tempo_marker_grab (item, event);
419 case MeterMarkerItem:
420 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
421 start_meter_marker_copy_grab (item, event);
423 start_meter_marker_grab (item, event);
433 case RangeMarkerBarItem:
434 start_range_markerbar_op (item, event, CreateRangeMarker);
437 case TransportMarkerBarItem:
438 start_range_markerbar_op (item, event, CreateTransportMarker);
447 switch (mouse_mode) {
450 case StartSelectionTrimItem:
451 start_selection_op (item, event, SelectionStartTrim);
454 case EndSelectionTrimItem:
455 start_selection_op (item, event, SelectionEndTrim);
459 if (Keyboard::modifier_state_contains
460 (event->button.state, Keyboard::ModifierMask(Keyboard::Alt))) {
461 // contains and not equals because I can't use alt as a modifier alone.
462 start_selection_grab (item, event);
463 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
464 /* grab selection for moving */
465 start_selection_op (item, event, SelectionMove);
468 /* this was debated, but decided the more common action was to
469 make a new selection */
470 start_selection_op (item, event, CreateSelection);
475 start_selection_op (item, event, CreateSelection);
481 if (Keyboard::modifier_state_contains (event->button.state,
482 Keyboard::ModifierMask(Keyboard::Control|Keyboard::Alt))
483 && event->type == GDK_BUTTON_PRESS) {
485 start_rubberband_select (item, event);
487 } else if (event->type == GDK_BUTTON_PRESS) {
490 case FadeInHandleItem:
491 start_fade_in_grab (item, event);
494 case FadeOutHandleItem:
495 start_fade_out_grab (item, event);
499 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
500 start_region_copy_grab (item, event);
501 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
502 start_region_brush_grab (item, event);
504 start_region_grab (item, event);
508 case AudioRegionViewNameHighlight:
509 start_trim (item, event);
513 case AudioRegionViewName:
514 /* rename happens on edit clicks */
515 start_trim (clicked_regionview->get_name_highlight(), event);
519 case GainAutomationControlPointItem:
520 case PanAutomationControlPointItem:
521 case RedirectAutomationControlPointItem:
522 start_control_point_grab (item, event);
526 case GainAutomationLineItem:
527 case PanAutomationLineItem:
528 case RedirectAutomationLineItem:
529 start_line_grab_from_line (item, event);
534 case AutomationTrackItem:
535 start_rubberband_select (item, event);
538 /* <CMT Additions> */
539 case ImageFrameHandleStartItem:
540 imageframe_start_handle_op(item, event) ;
543 case ImageFrameHandleEndItem:
544 imageframe_end_handle_op(item, event) ;
547 case MarkerViewHandleStartItem:
548 markerview_item_start_handle_op(item, event) ;
551 case MarkerViewHandleEndItem:
552 markerview_item_end_handle_op(item, event) ;
555 /* </CMT Additions> */
557 /* <CMT Additions> */
559 start_markerview_grab(item, event) ;
562 start_imageframe_grab(item, event) ;
564 /* </CMT Additions> */
576 // start_line_grab_from_regionview (item, event);
579 case GainControlPointItem:
580 start_control_point_grab (item, event);
584 start_line_grab_from_line (item, event);
587 case GainAutomationControlPointItem:
588 case PanAutomationControlPointItem:
589 case RedirectAutomationControlPointItem:
590 start_control_point_grab (item, event);
601 case GainAutomationControlPointItem:
602 case PanAutomationControlPointItem:
603 case RedirectAutomationControlPointItem:
604 start_control_point_grab (item, event);
607 case GainAutomationLineItem:
608 case PanAutomationLineItem:
609 case RedirectAutomationLineItem:
610 start_line_grab_from_line (item, event);
614 // XXX need automation mode to identify which
616 // start_line_grab_from_regionview (item, event);
626 if (event->type == GDK_BUTTON_PRESS) {
627 start_mouse_zoom (item, event);
634 if (item_type == RegionItem) {
635 start_time_fx (item, event);
640 /* handled in release */
649 switch (mouse_mode) {
651 if (event->type == GDK_BUTTON_PRESS) {
654 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
655 start_region_copy_grab (item, event);
657 start_region_grab (item, event);
661 case GainAutomationControlPointItem:
662 case PanAutomationControlPointItem:
663 case RedirectAutomationControlPointItem:
664 start_control_point_grab (item, event);
675 case AudioRegionViewNameHighlight:
676 start_trim (item, event);
680 case AudioRegionViewName:
681 start_trim (clicked_regionview->get_name_highlight(), event);
692 if (event->type == GDK_BUTTON_PRESS) {
693 /* relax till release */
700 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
701 temporal_zoom_session();
703 temporal_zoom_to_frame (true, event_frame(event));
718 switch (mouse_mode) {
720 //temporal_zoom_to_frame (true, where);
721 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
722 temporal_zoom_to_frame (true, where);
725 temporal_zoom_step (true);
730 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::Alt))) {
731 scroll_backward (0.6f);
734 else if (Keyboard::no_modifier_keys_pressed (&event->button)) {
735 scroll_tracks_up_line ();
737 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
738 if (clicked_trackview) {
739 if (!current_stepping_trackview) {
740 step_timeout = Glib::signal_timeout().connect (mem_fun(*this, &Editor::track_height_step_timeout), 500);
741 current_stepping_trackview = clicked_trackview;
743 gettimeofday (&last_track_height_step_timestamp, 0);
744 current_stepping_trackview->step_height (true);
747 else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
748 temporal_zoom_to_frame (true, where);
755 switch (mouse_mode) {
757 // temporal_zoom_to_frame (false, where);
758 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
759 temporal_zoom_to_frame (false, where);
762 temporal_zoom_step (false);
767 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::Alt))) {
768 scroll_forward (0.6f);
771 else if (Keyboard::no_modifier_keys_pressed (&event->button)) {
772 scroll_tracks_down_line ();
774 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
775 if (clicked_trackview) {
776 if (!current_stepping_trackview) {
777 step_timeout = Glib::signal_timeout().connect (mem_fun(*this, &Editor::track_height_step_timeout), 500);
778 current_stepping_trackview = clicked_trackview;
780 gettimeofday (&last_track_height_step_timestamp, 0);
781 current_stepping_trackview->step_height (false);
783 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
784 temporal_zoom_to_frame (false, where);
799 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
801 jack_nframes_t where = event_frame (event, 0, 0);
803 /* no action if we're recording */
805 if (session && session->actively_recording()) {
809 /* first, see if we're finishing a drag ... */
811 if (drag_info.item) {
812 if (end_grab (item, event)) {
813 /* grab dragged, so do nothing else */
818 /* edit events get handled here */
820 if (drag_info.item == 0 && Keyboard::is_edit_event (&event->button)) {
826 case TempoMarkerItem:
827 edit_tempo_marker (item);
830 case MeterMarkerItem:
831 edit_meter_marker (item);
834 case AudioRegionViewName:
835 if (clicked_regionview->name_active()) {
836 return mouse_rename_region (item, event);
846 /* context menu events get handled here */
848 if (Keyboard::is_context_menu_event (&event->button)) {
850 if (drag_info.item == 0) {
852 /* no matter which button pops up the context menu, tell the menu
853 widget to use button 1 to drive menu selection.
858 case FadeInHandleItem:
860 case FadeOutHandleItem:
861 popup_fade_context_menu (1, event->button.time, item, item_type);
865 popup_track_context_menu (1, event->button.time, item_type, false, where);
869 case AudioRegionViewNameHighlight:
870 case AudioRegionViewName:
871 popup_track_context_menu (1, event->button.time, item_type, false, where);
875 popup_track_context_menu (1, event->button.time, item_type, true, where);
878 case AutomationTrackItem:
879 popup_track_context_menu (1, event->button.time, item_type, false, where);
883 case RangeMarkerBarItem:
884 case TransportMarkerBarItem:
887 popup_ruler_menu (pixel_to_frame(event->button.x), item_type);
891 marker_context_menu (&event->button, item);
894 case TempoMarkerItem:
895 tm_marker_context_menu (&event->button, item);
898 case MeterMarkerItem:
899 tm_marker_context_menu (&event->button, item);
902 case CrossfadeViewItem:
903 popup_track_context_menu (1, event->button.time, item_type, false, where);
906 /* <CMT Additions> */
908 popup_imageframe_edit_menu(1, event->button.time, item, true) ;
910 case ImageFrameTimeAxisItem:
911 popup_imageframe_edit_menu(1, event->button.time, item, false) ;
914 popup_marker_time_axis_edit_menu(1, event->button.time, item, true) ;
916 case MarkerTimeAxisItem:
917 popup_marker_time_axis_edit_menu(1, event->button.time, item, false) ;
919 /* <CMT Additions> */
930 /* delete events get handled here */
932 if (drag_info.item == 0 && Keyboard::is_delete_event (&event->button)) {
935 case TempoMarkerItem:
936 remove_tempo_marker (item);
939 case MeterMarkerItem:
940 remove_meter_marker (item);
945 remove_marker (*item, event);
950 if (mouse_mode == MouseObject) {
951 remove_clicked_region ();
955 case GainControlPointItem:
956 if (mouse_mode == MouseGain) {
957 remove_gain_control_point (item, event);
961 case GainAutomationControlPointItem:
962 case PanAutomationControlPointItem:
963 case RedirectAutomationControlPointItem:
964 remove_control_point (item, event);
973 switch (event->button.button) {
977 /* see comments in button_press_handler */
979 case PlayheadCursorItem:
982 case GainAutomationLineItem:
983 case PanAutomationLineItem:
984 case RedirectAutomationLineItem:
985 case StartSelectionTrimItem:
986 case EndSelectionTrimItem:
990 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
991 snap_to (where, 0, true);
993 mouse_add_new_marker (where);
997 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1000 mouse_add_new_tempo_event (where);
1004 mouse_add_new_meter_event (pixel_to_frame (event->button.x));
1012 switch (mouse_mode) {
1014 switch (item_type) {
1015 case AutomationTrackItem:
1016 dynamic_cast<AutomationTimeAxisView*>(clicked_trackview)->add_automation_event
1030 switch (item_type) {
1032 clicked_regionview->add_gain_point_event (item, event);
1036 case AutomationTrackItem:
1037 dynamic_cast<AutomationTimeAxisView*>(clicked_trackview)->
1038 add_automation_event (item, event, where, event->button.y);
1047 switch (item_type) {
1049 audition_selected_region ();
1066 switch (mouse_mode) {
1069 switch (item_type) {
1071 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
1073 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::Shift|Keyboard::Alt))) {
1076 // Button2 click is unused
1089 // x_style_paste (where, 1.0);
1109 Editor::maybe_autoscroll (GdkEvent* event)
1111 jack_nframes_t one_page = (jack_nframes_t) rint (canvas_width * frames_per_unit);
1112 jack_nframes_t rightmost_frame = leftmost_frame + one_page;
1114 jack_nframes_t frame = drag_info.current_pointer_frame;
1116 if (autoscroll_timeout_tag < 0) {
1117 if (frame > rightmost_frame) {
1118 if (rightmost_frame < max_frames) {
1119 start_canvas_autoscroll (1);
1121 } else if (frame < leftmost_frame) {
1122 if (leftmost_frame > 0) {
1123 start_canvas_autoscroll (-1);
1127 if (frame >= leftmost_frame && frame < rightmost_frame) {
1128 stop_canvas_autoscroll ();
1134 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1140 switch (item_type) {
1141 case GainControlPointItem:
1142 if (mouse_mode == MouseGain) {
1143 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1144 cp->set_visible (true);
1148 at_y = cp->get_y ();
1149 cp->item->i2w (at_x, at_y);
1153 fraction = 1.0 - (cp->get_y() / cp->line.height());
1155 set_verbose_canvas_cursor (cp->line.get_verbose_cursor_string (fraction), at_x, at_y);
1156 show_verbose_canvas_cursor ();
1158 if (is_drawable()) {
1159 track_canvas.get_window()->set_cursor (*fader_cursor);
1164 case GainAutomationControlPointItem:
1165 case PanAutomationControlPointItem:
1166 case RedirectAutomationControlPointItem:
1167 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1168 cp->set_visible (true);
1172 at_y = cp->get_y ();
1173 cp->item->i2w (at_x, at_y);
1177 fraction = 1.0 - (cp->get_y() / cp->line.height());
1179 set_verbose_canvas_cursor (cp->line.get_verbose_cursor_string (fraction), at_x, at_y);
1180 show_verbose_canvas_cursor ();
1182 if (is_drawable()) {
1183 track_canvas.get_window()->set_cursor (*fader_cursor);
1188 if (mouse_mode == MouseGain) {
1189 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1191 line->property_fill_color_rgba() = color_map[cEnteredGainLine];
1192 if (is_drawable()) {
1193 track_canvas.get_window()->set_cursor (*fader_cursor);
1198 case GainAutomationLineItem:
1199 case RedirectAutomationLineItem:
1200 case PanAutomationLineItem:
1202 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1204 line->property_fill_color_rgba() = color_map[cEnteredAutomationLine];
1206 if (is_drawable()) {
1207 track_canvas.get_window()->set_cursor (*fader_cursor);
1211 case AudioRegionViewNameHighlight:
1212 if (is_drawable() && mouse_mode == MouseObject) {
1213 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1217 case StartSelectionTrimItem:
1218 case EndSelectionTrimItem:
1219 /* <CMT Additions> */
1220 case ImageFrameHandleStartItem:
1221 case ImageFrameHandleEndItem:
1222 case MarkerViewHandleStartItem:
1223 case MarkerViewHandleEndItem:
1224 /* </CMT Additions> */
1226 if (is_drawable()) {
1227 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1231 case EditCursorItem:
1232 case PlayheadCursorItem:
1233 if (is_drawable()) {
1234 track_canvas.get_window()->set_cursor (*grabber_cursor);
1238 case AudioRegionViewName:
1240 /* when the name is not an active item, the entire name highlight is for trimming */
1242 if (!reinterpret_cast<AudioRegionView *> (item->get_data ("regionview"))->name_active()) {
1243 if (mouse_mode == MouseObject && is_drawable()) {
1244 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1250 case AutomationTrackItem:
1251 if (is_drawable()) {
1252 Gdk::Cursor *cursor;
1253 switch (mouse_mode) {
1255 cursor = selector_cursor;
1258 cursor = zoom_cursor;
1261 cursor = cross_hair_cursor;
1265 track_canvas.get_window()->set_cursor (*cursor);
1267 AutomationTimeAxisView* atv;
1268 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1269 clear_entered_track = false;
1270 set_entered_track (atv);
1276 case RangeMarkerBarItem:
1277 case TransportMarkerBarItem:
1280 if (is_drawable()) {
1281 time_canvas.get_window()->set_cursor (*timebar_cursor);
1286 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1289 marker->set_color_rgba (color_map[cEnteredMarker]);
1291 case MeterMarkerItem:
1292 case TempoMarkerItem:
1293 if (is_drawable()) {
1294 time_canvas.get_window()->set_cursor (*timebar_cursor);
1297 case FadeInHandleItem:
1298 case FadeOutHandleItem:
1299 if (mouse_mode == MouseObject) {
1300 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1302 rect->property_fill_color_rgba() = 0;
1303 rect->property_outline_pixels() = 1;
1312 /* second pass to handle entered track status in a comprehensible way.
1315 switch (item_type) {
1317 case GainAutomationLineItem:
1318 case RedirectAutomationLineItem:
1319 case PanAutomationLineItem:
1320 case GainControlPointItem:
1321 case GainAutomationControlPointItem:
1322 case PanAutomationControlPointItem:
1323 case RedirectAutomationControlPointItem:
1324 /* these do not affect the current entered track state */
1325 clear_entered_track = false;
1328 case AutomationTrackItem:
1329 /* handled above already */
1333 set_entered_track (0);
1341 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1347 AudioRegionView* rv;
1350 switch (item_type) {
1351 case GainControlPointItem:
1352 case GainAutomationControlPointItem:
1353 case PanAutomationControlPointItem:
1354 case RedirectAutomationControlPointItem:
1355 cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
1356 if (cp->line.npoints() > 1) {
1357 if (!cp->selected) {
1358 cp->set_visible (false);
1362 if (is_drawable()) {
1363 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1366 hide_verbose_canvas_cursor ();
1369 case AudioRegionViewNameHighlight:
1370 case StartSelectionTrimItem:
1371 case EndSelectionTrimItem:
1372 case EditCursorItem:
1373 case PlayheadCursorItem:
1374 /* <CMT Additions> */
1375 case ImageFrameHandleStartItem:
1376 case ImageFrameHandleEndItem:
1377 case MarkerViewHandleStartItem:
1378 case MarkerViewHandleEndItem:
1379 /* </CMT Additions> */
1380 if (is_drawable()) {
1381 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1386 case GainAutomationLineItem:
1387 case RedirectAutomationLineItem:
1388 case PanAutomationLineItem:
1389 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1391 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1393 line->property_fill_color_rgba() = al->get_line_color();
1395 if (is_drawable()) {
1396 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1400 case AudioRegionViewName:
1401 /* see enter_handler() for notes */
1402 if (!reinterpret_cast<AudioRegionView *> (item->get_data ("regionview"))->name_active()) {
1403 if (is_drawable() && mouse_mode == MouseObject) {
1404 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1409 case RangeMarkerBarItem:
1410 case TransportMarkerBarItem:
1414 if (is_drawable()) {
1415 time_canvas.get_window()->set_cursor (*timebar_cursor);
1420 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1423 loc = find_location_from_marker (marker, is_start);
1424 if (loc) location_flags_changed (loc, this);
1426 case MeterMarkerItem:
1427 case TempoMarkerItem:
1429 if (is_drawable()) {
1430 time_canvas.get_window()->set_cursor (*timebar_cursor);
1435 case FadeInHandleItem:
1436 case FadeOutHandleItem:
1437 rv = static_cast<AudioRegionView*>(item->get_data ("regionview"));
1439 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1441 rect->property_fill_color_rgba() = rv->get_fill_color();
1442 rect->property_outline_pixels() = 0;
1447 case AutomationTrackItem:
1448 if (is_drawable()) {
1449 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1450 clear_entered_track = true;
1451 Glib::signal_idle().connect (mem_fun(*this, &Editor::left_automation_track));
1463 Editor::left_automation_track ()
1465 if (clear_entered_track) {
1466 set_entered_track (0);
1467 clear_entered_track = false;
1473 Editor::motion_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1477 /* We call this so that MOTION_NOTIFY events continue to be
1478 delivered to the canvas. We need to do this because we set
1479 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
1480 the density of the events, at the expense of a round-trip
1481 to the server. Given that this will mostly occur on cases
1482 where DISPLAY = :0.0, and given the cost of what the motion
1483 event might do, its a good tradeoff.
1486 track_canvas.get_pointer (x, y);
1488 if (current_stepping_trackview) {
1489 /* don't keep the persistent stepped trackview if the mouse moves */
1490 current_stepping_trackview = 0;
1491 step_timeout.disconnect ();
1494 if (session && session->actively_recording()) {
1495 /* Sorry. no dragging stuff around while we record */
1499 drag_info.current_pointer_frame = event_frame (event, &drag_info.current_pointer_x,
1500 &drag_info.current_pointer_y);
1502 if (drag_info.item) {
1503 /* item != 0 is the best test i can think of for
1506 if (!drag_info.move_threshold_passsed) {
1507 drag_info.move_threshold_passsed = (abs ((int) (drag_info.current_pointer_x - drag_info.grab_x)) > 4);
1509 // and change the initial grab loc/frame if this drag info wants us to
1510 if (drag_info.want_move_threshold && drag_info.move_threshold_passsed) {
1511 drag_info.grab_frame = drag_info.current_pointer_frame;
1512 drag_info.grab_x = drag_info.current_pointer_x;
1513 drag_info.grab_y = drag_info.current_pointer_y;
1514 drag_info.last_pointer_frame = drag_info.grab_frame;
1515 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
1520 switch (item_type) {
1521 case PlayheadCursorItem:
1522 case EditCursorItem:
1524 case GainControlPointItem:
1525 case RedirectAutomationControlPointItem:
1526 case GainAutomationControlPointItem:
1527 case PanAutomationControlPointItem:
1528 case TempoMarkerItem:
1529 case MeterMarkerItem:
1530 case AudioRegionViewNameHighlight:
1531 case StartSelectionTrimItem:
1532 case EndSelectionTrimItem:
1535 case RedirectAutomationLineItem:
1536 case GainAutomationLineItem:
1537 case PanAutomationLineItem:
1538 case FadeInHandleItem:
1539 case FadeOutHandleItem:
1540 /* <CMT Additions> */
1541 case ImageFrameHandleStartItem:
1542 case ImageFrameHandleEndItem:
1543 case MarkerViewHandleStartItem:
1544 case MarkerViewHandleEndItem:
1545 /* </CMT Additions> */
1546 if (drag_info.item && (event->motion.state & Gdk::BUTTON1_MASK ||
1547 (event->motion.state & Gdk::BUTTON2_MASK))) {
1548 maybe_autoscroll (event);
1549 (this->*(drag_info.motion_callback)) (item, event);
1558 switch (mouse_mode) {
1563 if (drag_info.item && (event->motion.state & GDK_BUTTON1_MASK ||
1564 (event->motion.state & GDK_BUTTON2_MASK))) {
1565 maybe_autoscroll (event);
1566 (this->*(drag_info.motion_callback)) (item, event);
1577 track_canvas_motion (event);
1585 Editor::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
1587 if (drag_info.item == 0) {
1588 fatal << _("programming error: start_grab called without drag item") << endmsg;
1594 cursor = grabber_cursor;
1597 // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
1599 if (event->button.button == 2) {
1600 drag_info.x_constrained = true;
1602 drag_info.x_constrained = false;
1605 drag_info.grab_frame = event_frame(event, &drag_info.grab_x, &drag_info.grab_y);
1606 drag_info.last_pointer_frame = drag_info.grab_frame;
1607 drag_info.current_pointer_frame = drag_info.grab_frame;
1608 drag_info.current_pointer_x = drag_info.grab_x;
1609 drag_info.current_pointer_y = drag_info.grab_y;
1610 drag_info.cumulative_x_drag = 0;
1611 drag_info.cumulative_y_drag = 0;
1612 drag_info.first_move = true;
1613 drag_info.move_threshold_passsed = false;
1614 drag_info.want_move_threshold = false;
1615 drag_info.pointer_frame_offset = 0;
1616 drag_info.brushing = false;
1617 drag_info.copied_location = 0;
1619 drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK,
1621 event->button.time);
1623 if (session && session->transport_rolling()) {
1624 drag_info.was_rolling = true;
1626 drag_info.was_rolling = false;
1629 switch (snap_type) {
1630 case SnapToRegionStart:
1631 case SnapToRegionEnd:
1632 case SnapToRegionSync:
1633 case SnapToRegionBoundary:
1634 build_region_boundary_cache ();
1642 Editor::end_grab (ArdourCanvas::Item* item, GdkEvent* event)
1644 bool did_drag = false;
1646 stop_canvas_autoscroll ();
1648 if (drag_info.item == 0) {
1652 drag_info.item->ungrab (event->button.time);
1654 if (drag_info.finished_callback) {
1655 (this->*(drag_info.finished_callback)) (item, event);
1658 did_drag = !drag_info.first_move;
1660 hide_verbose_canvas_cursor();
1663 drag_info.copy = false;
1664 drag_info.motion_callback = 0;
1665 drag_info.finished_callback = 0;
1666 drag_info.last_trackview = 0;
1667 drag_info.last_frame_position = 0;
1668 drag_info.grab_frame = 0;
1669 drag_info.last_pointer_frame = 0;
1670 drag_info.current_pointer_frame = 0;
1671 drag_info.brushing = false;
1673 if (drag_info.copied_location) {
1674 delete drag_info.copied_location;
1675 drag_info.copied_location = 0;
1682 Editor::set_edit_cursor (GdkEvent* event)
1684 jack_nframes_t pointer_frame = event_frame (event);
1686 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1687 if (snap_type != SnapToEditCursor) {
1688 snap_to (pointer_frame);
1692 edit_cursor->set_position (pointer_frame);
1693 edit_cursor_clock.set (pointer_frame);
1697 Editor::set_playhead_cursor (GdkEvent* event)
1699 jack_nframes_t pointer_frame = event_frame (event);
1701 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1702 snap_to (pointer_frame);
1706 session->request_locate (pointer_frame, session->transport_rolling());
1711 Editor::start_fade_in_grab (ArdourCanvas::Item* item, GdkEvent* event)
1713 drag_info.item = item;
1714 drag_info.motion_callback = &Editor::fade_in_drag_motion_callback;
1715 drag_info.finished_callback = &Editor::fade_in_drag_finished_callback;
1719 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
1720 fatal << _("programming error: fade in canvas item has no regionview data pointer!") << endmsg;
1724 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1726 drag_info.pointer_frame_offset = drag_info.grab_frame - ((jack_nframes_t) arv->region.fade_in().back()->when + arv->region.position());
1730 Editor::fade_in_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1732 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1734 jack_nframes_t fade_length;
1736 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1737 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1743 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1747 if (pos < (arv->region.position() + 64)) {
1748 fade_length = 64; // this should be a minimum defined somewhere
1749 } else if (pos > arv->region.last_frame()) {
1750 fade_length = arv->region.length();
1752 fade_length = pos - arv->region.position();
1755 arv->reset_fade_in_shape_width (fade_length);
1757 show_verbose_duration_cursor (arv->region.position(), arv->region.position() + fade_length, 10);
1759 drag_info.first_move = false;
1763 Editor::fade_in_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1765 if (drag_info.first_move) return;
1767 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1769 jack_nframes_t fade_length;
1771 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1772 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1778 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1782 if (pos < (arv->region.position() + 64)) {
1783 fade_length = 64; // this should be a minimum defined somewhere
1785 else if (pos > arv->region.last_frame()) {
1786 fade_length = arv->region.length();
1789 fade_length = pos - arv->region.position();
1792 begin_reversible_command (_("change fade in length"));
1793 session->add_undo (arv->region.get_memento());
1794 arv->region.set_fade_in_length (fade_length);
1795 session->add_redo_no_execute (arv->region.get_memento());
1796 commit_reversible_command ();
1797 fade_in_drag_motion_callback (item, event);
1801 Editor::start_fade_out_grab (ArdourCanvas::Item* item, GdkEvent* event)
1803 drag_info.item = item;
1804 drag_info.motion_callback = &Editor::fade_out_drag_motion_callback;
1805 drag_info.finished_callback = &Editor::fade_out_drag_finished_callback;
1809 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
1810 fatal << _("programming error: fade out canvas item has no regionview data pointer!") << endmsg;
1814 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1816 drag_info.pointer_frame_offset = drag_info.grab_frame - (arv->region.length() - (jack_nframes_t) arv->region.fade_out().back()->when + arv->region.position());
1820 Editor::fade_out_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1822 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1824 jack_nframes_t fade_length;
1826 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1827 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1833 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1837 if (pos > (arv->region.last_frame() - 64)) {
1838 fade_length = 64; // this should really be a minimum fade defined somewhere
1840 else if (pos < arv->region.position()) {
1841 fade_length = arv->region.length();
1844 fade_length = arv->region.last_frame() - pos;
1847 arv->reset_fade_out_shape_width (fade_length);
1849 show_verbose_duration_cursor (arv->region.last_frame() - fade_length, arv->region.last_frame(), 10);
1851 drag_info.first_move = false;
1855 Editor::fade_out_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1857 if (drag_info.first_move) return;
1859 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1861 jack_nframes_t fade_length;
1863 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1864 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1870 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1874 if (pos > (arv->region.last_frame() - 64)) {
1875 fade_length = 64; // this should really be a minimum fade defined somewhere
1877 else if (pos < arv->region.position()) {
1878 fade_length = arv->region.length();
1881 fade_length = arv->region.last_frame() - pos;
1884 begin_reversible_command (_("change fade out length"));
1885 session->add_undo (arv->region.get_memento());
1886 arv->region.set_fade_out_length (fade_length);
1887 session->add_redo_no_execute (arv->region.get_memento());
1888 commit_reversible_command ();
1890 fade_out_drag_motion_callback (item, event);
1894 Editor::start_cursor_grab (ArdourCanvas::Item* item, GdkEvent* event)
1896 drag_info.item = item;
1897 drag_info.motion_callback = &Editor::cursor_drag_motion_callback;
1898 drag_info.finished_callback = &Editor::cursor_drag_finished_callback;
1902 if ((drag_info.data = (item->get_data ("cursor"))) == 0) {
1903 fatal << _("programming error: cursor canvas item has no cursor data pointer!") << endmsg;
1907 Cursor* cursor = (Cursor *) drag_info.data;
1909 if (session && cursor == playhead_cursor) {
1910 if (drag_info.was_rolling) {
1911 session->request_stop ();
1915 drag_info.pointer_frame_offset = drag_info.grab_frame - cursor->current_frame;
1917 show_verbose_time_cursor (cursor->current_frame, 10);
1921 Editor::cursor_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1923 Cursor* cursor = (Cursor *) drag_info.data;
1924 jack_nframes_t adjusted_frame;
1926 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1927 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1933 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1934 if (cursor != edit_cursor || snap_type != SnapToEditCursor) {
1935 snap_to (adjusted_frame);
1939 if (adjusted_frame == drag_info.last_pointer_frame) return;
1941 cursor->set_position (adjusted_frame);
1943 if (cursor == edit_cursor) {
1944 edit_cursor_clock.set (cursor->current_frame);
1947 show_verbose_time_cursor (cursor->current_frame, 10);
1949 drag_info.last_pointer_frame = adjusted_frame;
1950 drag_info.first_move = false;
1954 Editor::cursor_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1956 if (drag_info.first_move) return;
1958 cursor_drag_motion_callback (item, event);
1960 if (item == &playhead_cursor->canvas_item) {
1962 session->request_locate (playhead_cursor->current_frame, drag_info.was_rolling);
1964 } else if (item == &edit_cursor->canvas_item) {
1965 edit_cursor->set_position (edit_cursor->current_frame);
1966 edit_cursor_clock.set (edit_cursor->current_frame);
1971 Editor::update_marker_drag_item (Location *location)
1973 double x1 = frame_to_pixel (location->start());
1974 double x2 = frame_to_pixel (location->end());
1976 if (location->is_mark()) {
1977 marker_drag_line_points.front().set_x(x1);
1978 marker_drag_line_points.back().set_x(x1);
1979 marker_drag_line->property_points() = marker_drag_line_points;
1982 range_marker_drag_rect->property_x1() = x1;
1983 range_marker_drag_rect->property_x2() = x2;
1988 Editor::start_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
1992 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1993 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
1999 Location *location = find_location_from_marker (marker, is_start);
2001 drag_info.item = item;
2002 drag_info.data = marker;
2003 drag_info.motion_callback = &Editor::marker_drag_motion_callback;
2004 drag_info.finished_callback = &Editor::marker_drag_finished_callback;
2008 drag_info.copied_location = new Location (*location);
2009 drag_info.pointer_frame_offset = drag_info.grab_frame - (is_start ? location->start() : location->end());
2011 update_marker_drag_item (location);
2013 if (location->is_mark()) {
2014 marker_drag_line->show();
2015 marker_drag_line->raise_to_top();
2018 range_marker_drag_rect->show();
2019 range_marker_drag_rect->raise_to_top();
2022 if (is_start) show_verbose_time_cursor (location->start(), 10);
2023 else show_verbose_time_cursor (location->end(), 10);
2027 Editor::marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2029 jack_nframes_t f_delta;
2030 Marker* marker = (Marker *) drag_info.data;
2031 Location *real_location;
2032 Location *copy_location;
2034 bool move_both = false;
2036 jack_nframes_t newframe;
2037 if (drag_info.pointer_frame_offset <= (long) drag_info.current_pointer_frame) {
2038 newframe = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2044 jack_nframes_t next = newframe;
2046 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2047 snap_to (newframe, 0, true);
2050 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
2052 /* call this to find out if its the start or end */
2054 real_location = find_location_from_marker (marker, is_start);
2056 /* use the copy that we're "dragging" around */
2058 copy_location = drag_info.copied_location;
2060 f_delta = copy_location->end() - copy_location->start();
2062 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
2066 if (is_start) { // start marker
2069 copy_location->set_start (newframe);
2070 copy_location->set_end (newframe + f_delta);
2071 } else if (newframe < copy_location->end()) {
2072 copy_location->set_start (newframe);
2074 snap_to (next, 1, true);
2075 copy_location->set_end (next);
2076 copy_location->set_start (newframe);
2079 } else { // end marker
2082 copy_location->set_end (newframe);
2083 copy_location->set_start (newframe - f_delta);
2084 } else if (newframe > copy_location->start()) {
2085 copy_location->set_end (newframe);
2087 } else if (newframe > 0) {
2088 snap_to (next, -1, true);
2089 copy_location->set_start (next);
2090 copy_location->set_end (newframe);
2094 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
2095 drag_info.first_move = false;
2097 update_marker_drag_item (copy_location);
2099 LocationMarkers* lm = find_location_markers (real_location);
2100 lm->set_position (copy_location->start(), copy_location->end());
2102 show_verbose_time_cursor (newframe, 10);
2106 Editor::marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2108 if (drag_info.first_move) {
2109 marker_drag_motion_callback (item, event);
2113 Marker* marker = (Marker *) drag_info.data;
2118 begin_reversible_command ( _("move marker") );
2119 session->add_undo( session->locations()->get_memento() );
2121 Location * location = find_location_from_marker (marker, is_start);
2124 location->set (drag_info.copied_location->start(), drag_info.copied_location->end());
2127 session->add_redo_no_execute( session->locations()->get_memento() );
2128 commit_reversible_command ();
2130 marker_drag_line->hide();
2131 range_marker_drag_rect->hide();
2135 Editor::start_meter_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2138 MeterMarker* meter_marker;
2140 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2141 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2145 meter_marker = dynamic_cast<MeterMarker*> (marker);
2147 MetricSection& section (meter_marker->meter());
2149 if (!section.movable()) {
2153 drag_info.item = item;
2154 drag_info.data = marker;
2155 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2156 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2160 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2162 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2166 Editor::start_meter_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2169 MeterMarker* meter_marker;
2171 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2172 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2176 meter_marker = dynamic_cast<MeterMarker*> (marker);
2178 // create a dummy marker for visual representation of moving the copy.
2179 // The actual copying is not done before we reach the finish callback.
2181 snprintf (name, sizeof(name), "%g/%g", meter_marker->meter().beats_per_bar(), meter_marker->meter().note_divisor ());
2182 MeterMarker* new_marker = new MeterMarker(*this, *meter_group, color_map[cMeterMarker], name,
2183 *new MeterSection(meter_marker->meter()));
2185 drag_info.item = &new_marker->the_item();
2186 drag_info.copy = true;
2187 drag_info.data = new_marker;
2188 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2189 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2193 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2195 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2199 Editor::meter_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2201 MeterMarker* marker = (MeterMarker *) drag_info.data;
2202 jack_nframes_t adjusted_frame;
2204 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2205 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2211 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2212 snap_to (adjusted_frame);
2215 if (adjusted_frame == drag_info.last_pointer_frame) return;
2217 marker->set_position (adjusted_frame);
2220 drag_info.last_pointer_frame = adjusted_frame;
2221 drag_info.first_move = false;
2223 show_verbose_time_cursor (adjusted_frame, 10);
2227 Editor::meter_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2229 if (drag_info.first_move) return;
2231 meter_marker_drag_motion_callback (drag_info.item, event);
2233 MeterMarker* marker = (MeterMarker *) drag_info.data;
2236 TempoMap& map (session->tempo_map());
2237 map.bbt_time (drag_info.last_pointer_frame, when);
2239 if (drag_info.copy == true) {
2240 begin_reversible_command (_("copy meter mark"));
2241 session->add_undo (map.get_memento());
2242 map.add_meter (marker->meter(), when);
2243 session->add_redo_no_execute (map.get_memento());
2244 commit_reversible_command ();
2246 // delete the dummy marker we used for visual representation of copying.
2247 // a new visual marker will show up automatically.
2250 begin_reversible_command (_("move meter mark"));
2251 session->add_undo (map.get_memento());
2252 map.move_meter (marker->meter(), when);
2253 session->add_redo_no_execute (map.get_memento());
2254 commit_reversible_command ();
2259 Editor::start_tempo_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2262 TempoMarker* tempo_marker;
2264 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2265 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2269 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2270 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2274 MetricSection& section (tempo_marker->tempo());
2276 if (!section.movable()) {
2280 drag_info.item = item;
2281 drag_info.data = marker;
2282 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2283 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2287 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2288 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2292 Editor::start_tempo_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2295 TempoMarker* tempo_marker;
2297 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2298 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2302 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2303 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2307 // create a dummy marker for visual representation of moving the copy.
2308 // The actual copying is not done before we reach the finish callback.
2310 snprintf (name, sizeof (name), "%.2f", tempo_marker->tempo().beats_per_minute());
2311 TempoMarker* new_marker = new TempoMarker(*this, *tempo_group, color_map[cTempoMarker], name,
2312 *new TempoSection(tempo_marker->tempo()));
2314 drag_info.item = &new_marker->the_item();
2315 drag_info.copy = true;
2316 drag_info.data = new_marker;
2317 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2318 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2322 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2324 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2328 Editor::tempo_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2330 TempoMarker* marker = (TempoMarker *) drag_info.data;
2331 jack_nframes_t adjusted_frame;
2333 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2334 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2340 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2341 snap_to (adjusted_frame);
2344 if (adjusted_frame == drag_info.last_pointer_frame) return;
2346 /* OK, we've moved far enough to make it worth actually move the thing. */
2348 marker->set_position (adjusted_frame);
2350 show_verbose_time_cursor (adjusted_frame, 10);
2352 drag_info.last_pointer_frame = adjusted_frame;
2353 drag_info.first_move = false;
2357 Editor::tempo_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2359 if (drag_info.first_move) return;
2361 tempo_marker_drag_motion_callback (drag_info.item, event);
2363 TempoMarker* marker = (TempoMarker *) drag_info.data;
2366 TempoMap& map (session->tempo_map());
2367 map.bbt_time (drag_info.last_pointer_frame, when);
2369 if (drag_info.copy == true) {
2370 begin_reversible_command (_("copy tempo mark"));
2371 session->add_undo (map.get_memento());
2372 map.add_tempo (marker->tempo(), when);
2373 session->add_redo_no_execute (map.get_memento());
2374 commit_reversible_command ();
2376 // delete the dummy marker we used for visual representation of copying.
2377 // a new visual marker will show up automatically.
2380 begin_reversible_command (_("move tempo mark"));
2381 session->add_undo (map.get_memento());
2382 map.move_tempo (marker->tempo(), when);
2383 session->add_redo_no_execute (map.get_memento());
2384 commit_reversible_command ();
2389 Editor::remove_gain_control_point (ArdourCanvas::Item*item, GdkEvent* event)
2391 ControlPoint* control_point;
2393 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2394 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2398 // We shouldn't remove the first or last gain point
2399 if (control_point->line.is_last_point(*control_point) ||
2400 control_point->line.is_first_point(*control_point)) {
2404 control_point->line.remove_point (*control_point);
2408 Editor::remove_control_point (ArdourCanvas::Item*item, GdkEvent* event)
2410 ControlPoint* control_point;
2412 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2413 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2417 control_point->line.remove_point (*control_point);
2421 Editor::start_control_point_grab (ArdourCanvas::Item* item, GdkEvent* event)
2423 ControlPoint* control_point;
2425 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2426 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2430 drag_info.item = item;
2431 drag_info.data = control_point;
2432 drag_info.motion_callback = &Editor::control_point_drag_motion_callback;
2433 drag_info.finished_callback = &Editor::control_point_drag_finished_callback;
2435 start_grab (event, fader_cursor);
2437 control_point->line.start_drag (control_point, 0);
2439 float fraction = 1.0 - (control_point->get_y() / control_point->line.height());
2440 set_verbose_canvas_cursor (control_point->line.get_verbose_cursor_string (fraction),
2441 drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
2443 show_verbose_canvas_cursor ();
2447 Editor::control_point_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2449 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2451 double cx = drag_info.current_pointer_x;
2452 double cy = drag_info.current_pointer_y;
2454 drag_info.cumulative_x_drag = cx - drag_info.grab_x ;
2455 drag_info.cumulative_y_drag = cy - drag_info.grab_y ;
2457 bool x_constrained = false;
2459 if (drag_info.x_constrained) {
2460 if (fabs(drag_info.cumulative_x_drag) < fabs(drag_info.cumulative_y_drag)) {
2461 cx = drag_info.grab_x;
2462 x_constrained = true;
2465 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 = (jack_nframes_t) floor (cx * frames_per_unit);
2479 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier()) && !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 selection->clear ();
3615 clicked_selection = 0;
3619 Editor::start_selection_op (ArdourCanvas::Item* item, GdkEvent* event, SelectionOp op)
3621 jack_nframes_t start = 0;
3622 jack_nframes_t end = 0;
3628 drag_info.item = item;
3629 drag_info.motion_callback = &Editor::drag_selection;
3630 drag_info.finished_callback = &Editor::end_selection_op;
3635 case CreateSelection:
3637 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
3638 drag_info.copy = true;
3640 drag_info.copy = false;
3642 start_grab (event, selector_cursor);
3645 case SelectionStartTrim:
3646 clicked_trackview->order_selection_trims (item, true);
3647 start_grab (event, trimmer_cursor);
3648 start = selection->time[clicked_selection].start;
3649 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
3652 case SelectionEndTrim:
3653 clicked_trackview->order_selection_trims (item, false);
3654 start_grab (event, trimmer_cursor);
3655 end = selection->time[clicked_selection].end;
3656 drag_info.pointer_frame_offset = drag_info.grab_frame - end;
3660 start = selection->time[clicked_selection].start;
3662 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
3666 if (selection_op == SelectionMove) {
3667 show_verbose_time_cursor(start, 10);
3669 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
3674 Editor::drag_selection (ArdourCanvas::Item* item, GdkEvent* event)
3676 jack_nframes_t start = 0;
3677 jack_nframes_t end = 0;
3678 jack_nframes_t length;
3679 jack_nframes_t pending_position;
3681 if ((int32_t) drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
3682 pending_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
3685 pending_position = 0;
3688 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3689 snap_to (pending_position);
3692 /* only alter selection if the current frame is
3693 different from the last frame position (adjusted)
3696 if (pending_position == drag_info.last_pointer_frame) return;
3698 switch (selection_op) {
3699 case CreateSelection:
3701 if (drag_info.first_move) {
3702 snap_to (drag_info.grab_frame);
3705 if (pending_position < drag_info.grab_frame) {
3706 start = pending_position;
3707 end = drag_info.grab_frame;
3709 end = pending_position;
3710 start = drag_info.grab_frame;
3713 /* first drag: Either add to the selection
3714 or create a new selection->
3717 if (drag_info.first_move) {
3719 begin_reversible_command (_("range selection"));
3721 if (drag_info.copy) {
3722 /* adding to the selection */
3723 clicked_selection = selection->add (start, end);
3724 drag_info.copy = false;
3726 /* new selection-> */
3727 clicked_selection = selection->set (clicked_trackview, start, end);
3732 case SelectionStartTrim:
3734 if (drag_info.first_move) {
3735 begin_reversible_command (_("trim selection start"));
3738 start = selection->time[clicked_selection].start;
3739 end = selection->time[clicked_selection].end;
3741 if (pending_position > end) {
3744 start = pending_position;
3748 case SelectionEndTrim:
3750 if (drag_info.first_move) {
3751 begin_reversible_command (_("trim selection end"));
3754 start = selection->time[clicked_selection].start;
3755 end = selection->time[clicked_selection].end;
3757 if (pending_position < start) {
3760 end = pending_position;
3767 if (drag_info.first_move) {
3768 begin_reversible_command (_("move selection"));
3771 start = selection->time[clicked_selection].start;
3772 end = selection->time[clicked_selection].end;
3774 length = end - start;
3776 start = pending_position;
3779 end = start + length;
3784 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
3785 start_canvas_autoscroll (1);
3789 selection->replace (clicked_selection, start, end);
3792 drag_info.last_pointer_frame = pending_position;
3793 drag_info.first_move = false;
3795 if (selection_op == SelectionMove) {
3796 show_verbose_time_cursor(start, 10);
3798 show_verbose_time_cursor(pending_position, 10);
3803 Editor::end_selection_op (ArdourCanvas::Item* item, GdkEvent* event)
3805 if (!drag_info.first_move) {
3806 drag_selection (item, event);
3807 /* XXX this is not object-oriented programming at all. ick */
3808 if (selection->time.consolidate()) {
3809 selection->TimeChanged ();
3811 commit_reversible_command ();
3813 /* just a click, no pointer movement.*/
3815 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
3817 selection->clear_time();
3822 /* XXX what happens if its a music selection? */
3823 session->set_audio_range (selection->time);
3824 stop_canvas_autoscroll ();
3828 Editor::start_trim (ArdourCanvas::Item* item, GdkEvent* event)
3831 TimeAxisView* tvp = clicked_trackview;
3832 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
3834 if (tv && tv->is_audio_track()) {
3835 speed = tv->get_diskstream()->speed();
3838 jack_nframes_t region_start = (jack_nframes_t) (clicked_regionview->region.position() / speed);
3839 jack_nframes_t region_end = (jack_nframes_t) (clicked_regionview->region.last_frame() / speed);
3840 jack_nframes_t region_length = (jack_nframes_t) (clicked_regionview->region.length() / speed);
3842 motion_frozen_playlists.clear();
3844 //drag_info.item = clicked_regionview->get_name_highlight();
3845 drag_info.item = item;
3846 drag_info.motion_callback = &Editor::trim_motion_callback;
3847 drag_info.finished_callback = &Editor::trim_finished_callback;
3849 start_grab (event, trimmer_cursor);
3851 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
3852 trim_op = ContentsTrim;
3854 /* These will get overridden for a point trim.*/
3855 if (drag_info.current_pointer_frame < (region_start + region_length/2)) {
3856 /* closer to start */
3857 trim_op = StartTrim;
3858 } else if (drag_info.current_pointer_frame > (region_end - region_length/2)) {
3866 show_verbose_time_cursor(region_start, 10);
3869 show_verbose_time_cursor(region_end, 10);
3872 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
3878 Editor::trim_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
3880 AudioRegionView* rv = clicked_regionview;
3881 jack_nframes_t frame_delta = 0;
3882 bool left_direction;
3883 bool obey_snap = !Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier());
3885 /* snap modifier works differently here..
3886 its' current state has to be passed to the
3887 various trim functions in order to work properly
3891 TimeAxisView* tvp = clicked_trackview;
3892 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
3893 pair<set<Playlist*>::iterator,bool> insert_result;
3895 if (tv && tv->is_audio_track()) {
3896 speed = tv->get_diskstream()->speed();
3899 if (drag_info.last_pointer_frame > drag_info.current_pointer_frame) {
3900 left_direction = true;
3902 left_direction = false;
3906 snap_to (drag_info.current_pointer_frame);
3909 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
3913 if (drag_info.first_move) {
3919 trim_type = "Region start trim";
3922 trim_type = "Region end trim";
3925 trim_type = "Region content trim";
3929 begin_reversible_command (trim_type);
3931 for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin(); i != selection->audio_regions.by_layer().end(); ++i) {
3932 (*i)->region.freeze ();
3933 (*i)->temporarily_hide_envelope ();
3935 Playlist * pl = (*i)->region.playlist();
3936 insert_result = motion_frozen_playlists.insert (pl);
3937 if (insert_result.second) {
3938 session->add_undo (pl->get_memento());
3943 if (left_direction) {
3944 frame_delta = (drag_info.last_pointer_frame - drag_info.current_pointer_frame);
3946 frame_delta = (drag_info.current_pointer_frame - drag_info.last_pointer_frame);
3951 if ((left_direction == false) && (drag_info.current_pointer_frame <= rv->region.first_frame()/speed)) {
3954 for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin(); i != selection->audio_regions.by_layer().end(); ++i) {
3955 single_start_trim (**i, frame_delta, left_direction, obey_snap);
3961 if ((left_direction == true) && (drag_info.current_pointer_frame > (jack_nframes_t) (rv->region.last_frame()/speed))) {
3964 for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin(); i != selection->audio_regions.by_layer().end(); ++i) {
3965 single_end_trim (**i, frame_delta, left_direction, obey_snap);
3972 bool swap_direction = false;
3974 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
3975 swap_direction = true;
3978 for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin();
3979 i != selection->audio_regions.by_layer().end(); ++i)
3981 single_contents_trim (**i, frame_delta, left_direction, swap_direction, obey_snap);
3989 show_verbose_time_cursor((jack_nframes_t) (rv->region.position()/speed), 10);
3992 show_verbose_time_cursor((jack_nframes_t) (rv->region.last_frame()/speed), 10);
3995 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
3999 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4000 drag_info.first_move = false;
4004 Editor::single_contents_trim (AudioRegionView& rv, jack_nframes_t frame_delta, bool left_direction, bool swap_direction, bool obey_snap)
4006 Region& region (rv.region);
4008 if (region.locked()) {
4012 jack_nframes_t new_bound;
4015 TimeAxisView* tvp = clicked_trackview;
4016 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
4018 if (tv && tv->is_audio_track()) {
4019 speed = tv->get_diskstream()->speed();
4022 if (left_direction) {
4023 if (swap_direction) {
4024 new_bound = (jack_nframes_t) (region.position()/speed) + frame_delta;
4026 new_bound = (jack_nframes_t) (region.position()/speed) - frame_delta;
4029 if (swap_direction) {
4030 new_bound = (jack_nframes_t) (region.position()/speed) - frame_delta;
4032 new_bound = (jack_nframes_t) (region.position()/speed) + frame_delta;
4037 snap_to (new_bound);
4039 region.trim_start ((jack_nframes_t) (new_bound * speed), this);
4040 rv.region_changed (StartChanged);
4044 Editor::single_start_trim (AudioRegionView& rv, jack_nframes_t frame_delta, bool left_direction, bool obey_snap)
4046 Region& region (rv.region);
4048 if (region.locked()) {
4052 jack_nframes_t new_bound;
4055 TimeAxisView* tvp = clicked_trackview;
4056 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
4058 if (tv && tv->is_audio_track()) {
4059 speed = tv->get_diskstream()->speed();
4062 if (left_direction) {
4063 new_bound = (jack_nframes_t) (region.position()/speed) - frame_delta;
4065 new_bound = (jack_nframes_t) (region.position()/speed) + frame_delta;
4069 snap_to (new_bound, (left_direction ? 0 : 1));
4072 region.trim_front ((jack_nframes_t) (new_bound * speed), this);
4074 rv.region_changed (Change (LengthChanged|PositionChanged|StartChanged));
4078 Editor::single_end_trim (AudioRegionView& rv, jack_nframes_t frame_delta, bool left_direction, bool obey_snap)
4080 Region& region (rv.region);
4082 if (region.locked()) {
4086 jack_nframes_t new_bound;
4089 TimeAxisView* tvp = clicked_trackview;
4090 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
4092 if (tv && tv->is_audio_track()) {
4093 speed = tv->get_diskstream()->speed();
4096 if (left_direction) {
4097 new_bound = (jack_nframes_t) ((region.last_frame() + 1)/speed) - frame_delta;
4099 new_bound = (jack_nframes_t) ((region.last_frame() + 1)/speed) + frame_delta;
4103 snap_to (new_bound);
4105 region.trim_end ((jack_nframes_t) (new_bound * speed), this);
4106 rv.region_changed (LengthChanged);
4110 Editor::trim_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
4112 if (!drag_info.first_move) {
4113 trim_motion_callback (item, event);
4115 if (!clicked_regionview->get_selected()) {
4116 thaw_region_after_trim (*clicked_regionview);
4119 for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin();
4120 i != selection->audio_regions.by_layer().end(); ++i)
4122 thaw_region_after_trim (**i);
4126 for (set<Playlist*>::iterator p = motion_frozen_playlists.begin(); p != motion_frozen_playlists.end(); ++p) {
4128 session->add_redo_no_execute ((*p)->get_memento());
4131 motion_frozen_playlists.clear ();
4133 commit_reversible_command();
4135 /* no mouse movement */
4141 Editor::point_trim (GdkEvent* event)
4143 AudioRegionView* rv = clicked_regionview;
4144 jack_nframes_t new_bound = drag_info.current_pointer_frame;
4146 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4147 snap_to (new_bound);
4150 /* Choose action dependant on which button was pressed */
4151 switch (event->button.button) {
4153 trim_op = StartTrim;
4154 begin_reversible_command (_("Start point trim"));
4156 if (rv->get_selected()) {
4158 for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin();
4159 i != selection->audio_regions.by_layer().end(); ++i)
4161 if (!(*i)->region.locked()) {
4162 session->add_undo ((*i)->region.playlist()->get_memento());
4163 (*i)->region.trim_front (new_bound, this);
4164 session->add_redo_no_execute ((*i)->region.playlist()->get_memento());
4170 if (!rv->region.locked()) {
4171 session->add_undo (rv->region.playlist()->get_memento());
4172 rv->region.trim_front (new_bound, this);
4173 session->add_redo_no_execute (rv->region.playlist()->get_memento());
4177 commit_reversible_command();
4182 begin_reversible_command (_("End point trim"));
4184 if (rv->get_selected()) {
4186 for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin(); i != selection->audio_regions.by_layer().end(); ++i)
4188 if (!(*i)->region.locked()) {
4189 session->add_undo ((*i)->region.playlist()->get_memento());
4190 (*i)->region.trim_end (new_bound, this);
4191 session->add_redo_no_execute ((*i)->region.playlist()->get_memento());
4197 if (!rv->region.locked()) {
4198 session->add_undo (rv->region.playlist()->get_memento());
4199 rv->region.trim_end (new_bound, this);
4200 session->add_redo_no_execute (rv->region.playlist()->get_memento());
4204 commit_reversible_command();
4213 Editor::thaw_region_after_trim (AudioRegionView& rv)
4215 Region& region (rv.region);
4217 if (region.locked()) {
4221 region.thaw (_("trimmed region"));
4222 session->add_redo_no_execute (region.playlist()->get_memento());
4224 rv.unhide_envelope ();
4228 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* event)
4233 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
4234 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
4238 Location* location = find_location_from_marker (marker, is_start);
4239 location->set_hidden (true, this);
4244 Editor::start_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event, RangeMarkerOp op)
4251 drag_info.item = item;
4252 drag_info.motion_callback = &Editor::drag_range_markerbar_op;
4253 drag_info.finished_callback = &Editor::end_range_markerbar_op;
4255 range_marker_op = op;
4257 if (!temp_location) {
4258 temp_location = new Location;
4262 case CreateRangeMarker:
4263 case CreateTransportMarker:
4265 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
4266 drag_info.copy = true;
4268 drag_info.copy = false;
4270 start_grab (event, selector_cursor);
4274 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4279 Editor::drag_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4281 jack_nframes_t start = 0;
4282 jack_nframes_t end = 0;
4283 ArdourCanvas::SimpleRect *crect = (range_marker_op == CreateRangeMarker) ? range_bar_drag_rect: transport_bar_drag_rect;
4285 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4286 snap_to (drag_info.current_pointer_frame);
4289 /* only alter selection if the current frame is
4290 different from the last frame position.
4293 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4295 switch (range_marker_op) {
4296 case CreateRangeMarker:
4297 case CreateTransportMarker:
4298 if (drag_info.first_move) {
4299 snap_to (drag_info.grab_frame);
4302 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4303 start = drag_info.current_pointer_frame;
4304 end = drag_info.grab_frame;
4306 end = drag_info.current_pointer_frame;
4307 start = drag_info.grab_frame;
4310 /* first drag: Either add to the selection
4311 or create a new selection->
4314 if (drag_info.first_move) {
4316 temp_location->set (start, end);
4320 update_marker_drag_item (temp_location);
4321 range_marker_drag_rect->show();
4322 range_marker_drag_rect->raise_to_top();
4328 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
4329 start_canvas_autoscroll (1);
4333 temp_location->set (start, end);
4335 double x1 = frame_to_pixel (start);
4336 double x2 = frame_to_pixel (end);
4337 crect->property_x1() = x1;
4338 crect->property_x2() = x2;
4340 update_marker_drag_item (temp_location);
4343 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4344 drag_info.first_move = false;
4346 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4351 Editor::end_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4353 Location * newloc = 0;
4355 if (!drag_info.first_move) {
4356 drag_range_markerbar_op (item, event);
4358 switch (range_marker_op) {
4359 case CreateRangeMarker:
4360 begin_reversible_command (_("new range marker"));
4361 session->add_undo (session->locations()->get_memento());
4362 newloc = new Location(temp_location->start(), temp_location->end(), "unnamed", Location::IsRangeMarker);
4363 session->locations()->add (newloc, true);
4364 session->add_redo_no_execute (session->locations()->get_memento());
4365 commit_reversible_command ();
4367 range_bar_drag_rect->hide();
4368 range_marker_drag_rect->hide();
4371 case CreateTransportMarker:
4372 // popup menu to pick loop or punch
4373 new_transport_marker_context_menu (&event->button, item);
4378 /* just a click, no pointer movement.*/
4380 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
4387 stop_canvas_autoscroll ();
4393 Editor::start_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4395 drag_info.item = item;
4396 drag_info.motion_callback = &Editor::drag_mouse_zoom;
4397 drag_info.finished_callback = &Editor::end_mouse_zoom;
4399 start_grab (event, zoom_cursor);
4401 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4405 Editor::drag_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4407 jack_nframes_t start;
4410 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4411 snap_to (drag_info.current_pointer_frame);
4413 if (drag_info.first_move) {
4414 snap_to (drag_info.grab_frame);
4418 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4420 /* base start and end on initial click position */
4421 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4422 start = drag_info.current_pointer_frame;
4423 end = drag_info.grab_frame;
4425 end = drag_info.current_pointer_frame;
4426 start = drag_info.grab_frame;
4431 if (drag_info.first_move) {
4433 zoom_rect->raise_to_top();
4436 reposition_zoom_rect(start, end);
4438 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4439 drag_info.first_move = false;
4441 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4446 Editor::end_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4448 if (!drag_info.first_move) {
4449 drag_mouse_zoom (item, event);
4451 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
4452 temporal_zoom_by_frame (drag_info.grab_frame, drag_info.last_pointer_frame, "mouse zoom");
4454 temporal_zoom_by_frame (drag_info.last_pointer_frame, drag_info.grab_frame, "mouse zoom");
4457 temporal_zoom_to_frame (false, drag_info.grab_frame);
4459 temporal_zoom_step (false);
4460 center_screen (drag_info.grab_frame);
4468 Editor::reposition_zoom_rect (jack_nframes_t start, jack_nframes_t end)
4470 double x1 = frame_to_pixel (start);
4471 double x2 = frame_to_pixel (end);
4472 double y2 = canvas_height - 2;
4474 zoom_rect->property_x1() = x1;
4475 zoom_rect->property_y1() = 1.0;
4476 zoom_rect->property_x2() = x2;
4477 zoom_rect->property_y2() = y2;
4481 Editor::start_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4483 drag_info.item = item;
4484 drag_info.motion_callback = &Editor::drag_rubberband_select;
4485 drag_info.finished_callback = &Editor::end_rubberband_select;
4487 start_grab (event, cross_hair_cursor);
4489 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4493 Editor::drag_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4495 jack_nframes_t start;
4500 /* use a bigger drag threshold than the default */
4502 if (abs ((int) (drag_info.current_pointer_frame - drag_info.grab_frame)) < 8) {
4506 // if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4507 // snap_to (drag_info.current_pointer_frame);
4509 // if (drag_info.first_move) {
4510 // snap_to (drag_info.grab_frame);
4515 /* base start and end on initial click position */
4516 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4517 start = drag_info.current_pointer_frame;
4518 end = drag_info.grab_frame;
4520 end = drag_info.current_pointer_frame;
4521 start = drag_info.grab_frame;
4524 if (drag_info.current_pointer_y < drag_info.grab_y) {
4525 y1 = drag_info.current_pointer_y;
4526 y2 = drag_info.grab_y;
4529 y2 = drag_info.current_pointer_y;
4530 y1 = drag_info.grab_y;
4534 if (start != end || y1 != y2) {
4536 double x1 = frame_to_pixel (start);
4537 double x2 = frame_to_pixel (end);
4539 rubberband_rect->property_x1() = x1;
4540 rubberband_rect->property_y1() = y1;
4541 rubberband_rect->property_x2() = x2;
4542 rubberband_rect->property_y2() = y2;
4544 rubberband_rect->show();
4545 rubberband_rect->raise_to_top();
4547 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4548 drag_info.first_move = false;
4550 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4555 Editor::end_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4557 if (!drag_info.first_move) {
4559 drag_rubberband_select (item, event);
4562 if (drag_info.current_pointer_y < drag_info.grab_y) {
4563 y1 = drag_info.current_pointer_y;
4564 y2 = drag_info.grab_y;
4567 y2 = drag_info.current_pointer_y;
4568 y1 = drag_info.grab_y;
4572 Selection::Operation op = Keyboard::selection_type (event->button.state);
4575 begin_reversible_command (_("select regions"));
4577 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
4578 commit = select_all_within (drag_info.grab_frame, drag_info.last_pointer_frame, y1, y2, op);
4580 commit = select_all_within (drag_info.last_pointer_frame, drag_info.grab_frame, y1, y2, op);
4584 commit_reversible_command ();
4588 selection->clear_audio_regions();
4589 selection->clear_points ();
4590 selection->clear_lines ();
4593 rubberband_rect->hide();
4598 Editor::mouse_rename_region (ArdourCanvas::Item* item, GdkEvent* event)
4600 using namespace Gtkmm2ext;
4602 ArdourPrompter prompter (false);
4604 prompter.set_prompt (_("Name for region:"));
4605 prompter.set_initial_text (clicked_regionview->region.name());
4606 prompter.show_all ();
4607 switch (prompter.run ()) {
4608 case Gtk::RESPONSE_ACCEPT:
4610 prompter.get_result(str);
4612 clicked_regionview->region.set_name (str);
4620 Editor::start_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
4622 drag_info.item = item;
4623 drag_info.motion_callback = &Editor::time_fx_motion;
4624 drag_info.finished_callback = &Editor::end_time_fx;
4628 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4632 Editor::time_fx_motion (ArdourCanvas::Item *item, GdkEvent* event)
4634 AudioRegionView* rv = clicked_regionview;
4636 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4637 snap_to (drag_info.current_pointer_frame);
4640 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
4644 if (drag_info.current_pointer_frame > rv->region.position()) {
4645 rv->get_time_axis_view().show_timestretch (rv->region.position(), drag_info.current_pointer_frame);
4648 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4649 drag_info.first_move = false;
4651 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4655 Editor::end_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
4657 clicked_regionview->get_time_axis_view().hide_timestretch ();
4659 if (drag_info.first_move) {
4663 jack_nframes_t newlen = drag_info.last_pointer_frame - clicked_regionview->region.position();
4664 float percentage = (float) ((double) newlen - (double) clicked_regionview->region.length()) / ((double) newlen) * 100.0f;
4666 begin_reversible_command (_("timestretch"));
4668 if (run_timestretch (selection->audio_regions, percentage) == 0) {
4669 session->commit_reversible_command ();
4674 Editor::mouse_brush_insert_region (AudioRegionView* rv, jack_nframes_t pos)
4676 /* no brushing without a useful snap setting */
4678 switch (snap_mode) {
4680 return; /* can't work because it allows region to be placed anywhere */
4685 switch (snap_type) {
4688 case SnapToEditCursor:
4695 /* don't brush a copy over the original */
4697 if (pos == rv->region.position()) {
4701 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*>(&rv->get_time_axis_view());
4703 if (atv == 0 || !atv->is_audio_track()) {
4707 Playlist* playlist = atv->playlist();
4708 double speed = atv->get_diskstream()->speed();
4710 session->add_undo (playlist->get_memento());
4711 playlist->add_region (*(new AudioRegion (rv->region)), (jack_nframes_t) (pos * speed));
4712 session->add_redo_no_execute (playlist->get_memento());
4714 // playlist is frozen, so we have to update manually
4716 playlist->StateChanged (Change (~0)); /* EMIT SIGNAL */
4720 Editor::track_height_step_timeout ()
4723 struct timeval delta;
4725 gettimeofday (&now, 0);
4726 timersub (&now, &last_track_height_step_timestamp, &delta);
4728 if (delta.tv_sec * 1000000 + delta.tv_usec > 250000) { /* milliseconds */
4729 current_stepping_trackview = 0;