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/audio_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;
67 using namespace Editing;
70 Editor::event_frame (GdkEvent* event, double* pcx, double* pcy)
84 switch (event->type) {
85 case GDK_BUTTON_RELEASE:
86 case GDK_BUTTON_PRESS:
87 case GDK_2BUTTON_PRESS:
88 case GDK_3BUTTON_PRESS:
89 track_canvas.w2c(event->button.x, event->button.y, *pcx, *pcy);
91 case GDK_MOTION_NOTIFY:
92 track_canvas.w2c(event->motion.x, event->motion.y, *pcx, *pcy);
94 case GDK_ENTER_NOTIFY:
95 case GDK_LEAVE_NOTIFY:
96 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 (!force && m == mouse_mode) {
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)->get_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_selection (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
294 /* in object/audition/timefx mode, any button press sets
295 the selection if the object can be selected. this is a
296 bit of hack, because we want to avoid this if the
297 mouse operation is a region alignment.
299 note: not dbl-click or triple-click
302 if (((mouse_mode != MouseObject) &&
303 (mouse_mode != MouseAudition || item_type != RegionItem) &&
304 (mouse_mode != MouseTimeFX || item_type != RegionItem)) ||
305 (event->type != GDK_BUTTON_PRESS && event->type != GDK_BUTTON_RELEASE || event->button.button > 3)) {
310 Selection::Operation op = Keyboard::selection_type (event->button.state);
311 bool press = (event->type == GDK_BUTTON_PRESS);
313 begin_reversible_command (_("select on click"));
317 c1 = set_selected_track_from_click (press, op, true, true);
318 c2 = set_selected_regionview_from_click (press, op, true);
322 case AudioRegionViewNameHighlight:
323 case AudioRegionViewName:
324 c1 = set_selected_track_from_click (press, op, true, true);
325 c2 = set_selected_regionview_from_click (press, op, true);
329 case GainAutomationControlPointItem:
330 case PanAutomationControlPointItem:
331 case RedirectAutomationControlPointItem:
332 c1 = set_selected_track_from_click (press, op, true, true);
333 c2 = set_selected_control_point_from_click (press, op, false);
338 commit = set_selected_track_from_click (press, op, true, true);
341 case AutomationTrackItem:
342 commit = set_selected_track_from_click (press, op, true, true);
349 #define SELECT_TRACK_FROM_CANVAS_IN_RANGE_MODE
350 #ifdef SELECT_TRACK_FROM_CANVAS_IN_RANGE_MODE
351 /* in range mode, button 1/2/3 press potentially selects a track */
353 if (mouse_mode == MouseRange &&
354 event->type == GDK_BUTTON_PRESS &&
355 event->button.button <= 3) {
360 case AutomationTrackItem:
361 commit = set_selected_track_from_click (press, op, true, true);
370 commit_reversible_command ();
375 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
377 jack_nframes_t where = event_frame (event, 0, 0);
379 track_canvas.grab_focus();
381 if (session && session->actively_recording()) {
385 button_selection (item, event, item_type);
387 if (drag_info.item == 0 &&
388 (Keyboard::is_delete_event (&event->button) ||
389 Keyboard::is_context_menu_event (&event->button) ||
390 Keyboard::is_edit_event (&event->button))) {
392 /* handled by button release */
396 switch (event->button.button) {
399 if (event->type == GDK_BUTTON_PRESS) {
401 if (drag_info.item) {
402 drag_info.item->ungrab (event->button.time);
405 /* single mouse clicks on any of these item types operate
406 independent of mouse mode, mostly because they are
407 not on the main track canvas or because we want
413 case PlayheadCursorItem:
414 start_cursor_grab (item, event);
418 if (Keyboard::modifier_state_equals (event->button.state,
419 Keyboard::ModifierMask(Keyboard::Control|Keyboard::Shift))) {
420 hide_marker (item, event);
422 start_marker_grab (item, event);
426 case TempoMarkerItem:
427 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
428 start_tempo_marker_copy_grab (item, event);
430 start_tempo_marker_grab (item, event);
434 case MeterMarkerItem:
435 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
436 start_meter_marker_copy_grab (item, event);
438 start_meter_marker_grab (item, event);
448 case RangeMarkerBarItem:
449 start_range_markerbar_op (item, event, CreateRangeMarker);
453 case TransportMarkerBarItem:
454 start_range_markerbar_op (item, event, CreateTransportMarker);
463 switch (mouse_mode) {
466 case StartSelectionTrimItem:
467 start_selection_op (item, event, SelectionStartTrim);
470 case EndSelectionTrimItem:
471 start_selection_op (item, event, SelectionEndTrim);
475 if (Keyboard::modifier_state_contains
476 (event->button.state, Keyboard::ModifierMask(Keyboard::Alt))) {
477 // contains and not equals because I can't use alt as a modifier alone.
478 start_selection_grab (item, event);
479 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
480 /* grab selection for moving */
481 start_selection_op (item, event, SelectionMove);
484 /* this was debated, but decided the more common action was to
485 make a new selection */
486 start_selection_op (item, event, CreateSelection);
491 start_selection_op (item, event, CreateSelection);
497 if (Keyboard::modifier_state_contains (event->button.state,
498 Keyboard::ModifierMask(Keyboard::Control|Keyboard::Alt))
499 && event->type == GDK_BUTTON_PRESS) {
501 start_rubberband_select (item, event);
503 } else if (event->type == GDK_BUTTON_PRESS) {
506 case FadeInHandleItem:
507 start_fade_in_grab (item, event);
510 case FadeOutHandleItem:
511 start_fade_out_grab (item, event);
515 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
516 start_region_copy_grab (item, event);
517 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
518 start_region_brush_grab (item, event);
520 start_region_grab (item, event);
524 case AudioRegionViewNameHighlight:
525 start_trim (item, event);
529 case AudioRegionViewName:
530 /* rename happens on edit clicks */
531 start_trim (clicked_regionview->get_name_highlight(), event);
535 case GainAutomationControlPointItem:
536 case PanAutomationControlPointItem:
537 case RedirectAutomationControlPointItem:
538 start_control_point_grab (item, event);
542 case GainAutomationLineItem:
543 case PanAutomationLineItem:
544 case RedirectAutomationLineItem:
545 start_line_grab_from_line (item, event);
550 case AutomationTrackItem:
551 start_rubberband_select (item, event);
554 /* <CMT Additions> */
555 case ImageFrameHandleStartItem:
556 imageframe_start_handle_op(item, event) ;
559 case ImageFrameHandleEndItem:
560 imageframe_end_handle_op(item, event) ;
563 case MarkerViewHandleStartItem:
564 markerview_item_start_handle_op(item, event) ;
567 case MarkerViewHandleEndItem:
568 markerview_item_end_handle_op(item, event) ;
571 /* </CMT Additions> */
573 /* <CMT Additions> */
575 start_markerview_grab(item, event) ;
578 start_imageframe_grab(item, event) ;
580 /* </CMT Additions> */
596 // start_line_grab_from_regionview (item, event);
599 case GainControlPointItem:
600 start_control_point_grab (item, event);
604 start_line_grab_from_line (item, event);
607 case GainAutomationControlPointItem:
608 case PanAutomationControlPointItem:
609 case RedirectAutomationControlPointItem:
610 start_control_point_grab (item, event);
621 case GainAutomationControlPointItem:
622 case PanAutomationControlPointItem:
623 case RedirectAutomationControlPointItem:
624 start_control_point_grab (item, event);
627 case GainAutomationLineItem:
628 case PanAutomationLineItem:
629 case RedirectAutomationLineItem:
630 start_line_grab_from_line (item, event);
634 // XXX need automation mode to identify which
636 // start_line_grab_from_regionview (item, event);
646 if (event->type == GDK_BUTTON_PRESS) {
647 start_mouse_zoom (item, event);
654 if (item_type == RegionItem) {
655 start_time_fx (item, event);
660 /* handled in release */
669 switch (mouse_mode) {
671 if (event->type == GDK_BUTTON_PRESS) {
674 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
675 start_region_copy_grab (item, event);
677 start_region_grab (item, event);
681 case GainAutomationControlPointItem:
682 case PanAutomationControlPointItem:
683 case RedirectAutomationControlPointItem:
684 start_control_point_grab (item, event);
695 case AudioRegionViewNameHighlight:
696 start_trim (item, event);
700 case AudioRegionViewName:
701 start_trim (clicked_regionview->get_name_highlight(), event);
712 if (event->type == GDK_BUTTON_PRESS) {
713 /* relax till release */
720 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
721 temporal_zoom_session();
723 temporal_zoom_to_frame (true, event_frame(event));
738 switch (mouse_mode) {
740 //temporal_zoom_to_frame (true, where);
741 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
742 temporal_zoom_to_frame (true, where);
745 temporal_zoom_step (true);
750 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::Alt))) {
751 scroll_backward (0.6f);
754 else if (Keyboard::no_modifier_keys_pressed (&event->button)) {
755 scroll_tracks_up_line ();
757 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
758 if (clicked_trackview) {
759 if (!current_stepping_trackview) {
760 step_timeout = Glib::signal_timeout().connect (mem_fun(*this, &Editor::track_height_step_timeout), 500);
761 current_stepping_trackview = clicked_trackview;
763 gettimeofday (&last_track_height_step_timestamp, 0);
764 current_stepping_trackview->step_height (true);
767 else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
768 temporal_zoom_to_frame (true, where);
775 switch (mouse_mode) {
777 // temporal_zoom_to_frame (false, where);
778 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
779 temporal_zoom_to_frame (false, where);
782 temporal_zoom_step (false);
787 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::Alt))) {
788 scroll_forward (0.6f);
791 else if (Keyboard::no_modifier_keys_pressed (&event->button)) {
792 scroll_tracks_down_line ();
794 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
795 if (clicked_trackview) {
796 if (!current_stepping_trackview) {
797 step_timeout = Glib::signal_timeout().connect (mem_fun(*this, &Editor::track_height_step_timeout), 500);
798 current_stepping_trackview = clicked_trackview;
800 gettimeofday (&last_track_height_step_timestamp, 0);
801 current_stepping_trackview->step_height (false);
803 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
804 temporal_zoom_to_frame (false, where);
819 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
821 jack_nframes_t where = event_frame (event, 0, 0);
823 /* no action if we're recording */
825 if (session && session->actively_recording()) {
829 /* first, see if we're finishing a drag ... */
831 if (drag_info.item) {
832 if (end_grab (item, event)) {
833 /* grab dragged, so do nothing else */
838 button_selection (item, event, item_type);
840 /* edit events get handled here */
842 if (drag_info.item == 0 && Keyboard::is_edit_event (&event->button)) {
848 case TempoMarkerItem:
849 edit_tempo_marker (item);
852 case MeterMarkerItem:
853 edit_meter_marker (item);
856 case AudioRegionViewName:
857 if (clicked_regionview->name_active()) {
858 return mouse_rename_region (item, event);
868 /* context menu events get handled here */
870 if (Keyboard::is_context_menu_event (&event->button)) {
872 if (drag_info.item == 0) {
874 /* no matter which button pops up the context menu, tell the menu
875 widget to use button 1 to drive menu selection.
880 case FadeInHandleItem:
882 case FadeOutHandleItem:
883 popup_fade_context_menu (1, event->button.time, item, item_type);
887 popup_track_context_menu (1, event->button.time, item_type, false, where);
891 case AudioRegionViewNameHighlight:
892 case AudioRegionViewName:
893 popup_track_context_menu (1, event->button.time, item_type, false, where);
897 popup_track_context_menu (1, event->button.time, item_type, true, where);
900 case AutomationTrackItem:
901 popup_track_context_menu (1, event->button.time, item_type, false, where);
905 case RangeMarkerBarItem:
906 case TransportMarkerBarItem:
909 popup_ruler_menu (pixel_to_frame(event->button.x), item_type);
913 marker_context_menu (&event->button, item);
916 case TempoMarkerItem:
917 tm_marker_context_menu (&event->button, item);
920 case MeterMarkerItem:
921 tm_marker_context_menu (&event->button, item);
924 case CrossfadeViewItem:
925 popup_track_context_menu (1, event->button.time, item_type, false, where);
928 /* <CMT Additions> */
930 popup_imageframe_edit_menu(1, event->button.time, item, true) ;
932 case ImageFrameTimeAxisItem:
933 popup_imageframe_edit_menu(1, event->button.time, item, false) ;
936 popup_marker_time_axis_edit_menu(1, event->button.time, item, true) ;
938 case MarkerTimeAxisItem:
939 popup_marker_time_axis_edit_menu(1, event->button.time, item, false) ;
941 /* <CMT Additions> */
952 /* delete events get handled here */
954 if (drag_info.item == 0 && Keyboard::is_delete_event (&event->button)) {
957 case TempoMarkerItem:
958 remove_tempo_marker (item);
961 case MeterMarkerItem:
962 remove_meter_marker (item);
966 remove_marker (*item, event);
970 if (mouse_mode == MouseObject) {
971 remove_clicked_region ();
975 case GainControlPointItem:
976 if (mouse_mode == MouseGain) {
977 remove_gain_control_point (item, event);
981 case GainAutomationControlPointItem:
982 case PanAutomationControlPointItem:
983 case RedirectAutomationControlPointItem:
984 remove_control_point (item, event);
993 switch (event->button.button) {
997 /* see comments in button_press_handler */
999 case PlayheadCursorItem:
1002 case GainAutomationLineItem:
1003 case PanAutomationLineItem:
1004 case RedirectAutomationLineItem:
1005 case StartSelectionTrimItem:
1006 case EndSelectionTrimItem:
1010 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1011 snap_to (where, 0, true);
1013 mouse_add_new_marker (where);
1017 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1020 mouse_add_new_tempo_event (where);
1024 mouse_add_new_meter_event (pixel_to_frame (event->button.x));
1032 switch (mouse_mode) {
1034 switch (item_type) {
1035 case AutomationTrackItem:
1036 dynamic_cast<AutomationTimeAxisView*>(clicked_trackview)->add_automation_event
1050 switch (item_type) {
1052 clicked_regionview->add_gain_point_event (item, event);
1056 case AutomationTrackItem:
1057 dynamic_cast<AutomationTimeAxisView*>(clicked_trackview)->
1058 add_automation_event (item, event, where, event->button.y);
1067 switch (item_type) {
1069 audition_selected_region ();
1086 switch (mouse_mode) {
1089 switch (item_type) {
1091 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
1093 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::Shift|Keyboard::Alt))) {
1096 // Button2 click is unused
1109 // x_style_paste (where, 1.0);
1129 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1135 switch (item_type) {
1136 case GainControlPointItem:
1137 if (mouse_mode == MouseGain) {
1138 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1139 cp->set_visible (true);
1143 at_y = cp->get_y ();
1144 cp->item->i2w (at_x, at_y);
1148 fraction = 1.0 - (cp->get_y() / cp->line.height());
1150 set_verbose_canvas_cursor (cp->line.get_verbose_cursor_string (fraction), at_x, at_y);
1151 show_verbose_canvas_cursor ();
1153 if (is_drawable()) {
1154 track_canvas.get_window()->set_cursor (*fader_cursor);
1159 case GainAutomationControlPointItem:
1160 case PanAutomationControlPointItem:
1161 case RedirectAutomationControlPointItem:
1162 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1163 cp->set_visible (true);
1167 at_y = cp->get_y ();
1168 cp->item->i2w (at_x, at_y);
1172 fraction = 1.0 - (cp->get_y() / cp->line.height());
1174 set_verbose_canvas_cursor (cp->line.get_verbose_cursor_string (fraction), at_x, at_y);
1175 show_verbose_canvas_cursor ();
1177 if (is_drawable()) {
1178 track_canvas.get_window()->set_cursor (*fader_cursor);
1183 if (mouse_mode == MouseGain) {
1184 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1186 line->property_fill_color_rgba() = color_map[cEnteredGainLine];
1187 if (is_drawable()) {
1188 track_canvas.get_window()->set_cursor (*fader_cursor);
1193 case GainAutomationLineItem:
1194 case RedirectAutomationLineItem:
1195 case PanAutomationLineItem:
1197 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1199 line->property_fill_color_rgba() = color_map[cEnteredAutomationLine];
1201 if (is_drawable()) {
1202 track_canvas.get_window()->set_cursor (*fader_cursor);
1206 case AudioRegionViewNameHighlight:
1207 if (is_drawable() && mouse_mode == MouseObject) {
1208 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1212 case StartSelectionTrimItem:
1213 case EndSelectionTrimItem:
1214 /* <CMT Additions> */
1215 case ImageFrameHandleStartItem:
1216 case ImageFrameHandleEndItem:
1217 case MarkerViewHandleStartItem:
1218 case MarkerViewHandleEndItem:
1219 /* </CMT Additions> */
1221 if (is_drawable()) {
1222 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1226 case EditCursorItem:
1227 case PlayheadCursorItem:
1228 if (is_drawable()) {
1229 track_canvas.get_window()->set_cursor (*grabber_cursor);
1233 case AudioRegionViewName:
1235 /* when the name is not an active item, the entire name highlight is for trimming */
1237 if (!reinterpret_cast<AudioRegionView *> (item->get_data ("regionview"))->name_active()) {
1238 if (mouse_mode == MouseObject && is_drawable()) {
1239 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1245 case AutomationTrackItem:
1246 if (is_drawable()) {
1247 Gdk::Cursor *cursor;
1248 switch (mouse_mode) {
1250 cursor = selector_cursor;
1253 cursor = zoom_cursor;
1256 cursor = cross_hair_cursor;
1260 track_canvas.get_window()->set_cursor (*cursor);
1262 AutomationTimeAxisView* atv;
1263 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1264 clear_entered_track = false;
1265 set_entered_track (atv);
1271 case RangeMarkerBarItem:
1272 case TransportMarkerBarItem:
1275 if (is_drawable()) {
1276 time_canvas.get_window()->set_cursor (*timebar_cursor);
1281 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1284 marker->set_color_rgba (color_map[cEnteredMarker]);
1286 case MeterMarkerItem:
1287 case TempoMarkerItem:
1288 if (is_drawable()) {
1289 time_canvas.get_window()->set_cursor (*timebar_cursor);
1292 case FadeInHandleItem:
1293 case FadeOutHandleItem:
1294 if (mouse_mode == MouseObject) {
1295 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1297 rect->property_fill_color_rgba() = 0;
1298 rect->property_outline_pixels() = 1;
1307 /* second pass to handle entered track status in a comprehensible way.
1310 switch (item_type) {
1312 case GainAutomationLineItem:
1313 case RedirectAutomationLineItem:
1314 case PanAutomationLineItem:
1315 case GainControlPointItem:
1316 case GainAutomationControlPointItem:
1317 case PanAutomationControlPointItem:
1318 case RedirectAutomationControlPointItem:
1319 /* these do not affect the current entered track state */
1320 clear_entered_track = false;
1323 case AutomationTrackItem:
1324 /* handled above already */
1328 set_entered_track (0);
1336 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1342 AudioRegionView* rv;
1345 switch (item_type) {
1346 case GainControlPointItem:
1347 case GainAutomationControlPointItem:
1348 case PanAutomationControlPointItem:
1349 case RedirectAutomationControlPointItem:
1350 cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
1351 if (cp->line.npoints() > 1) {
1352 if (!cp->selected) {
1353 cp->set_visible (false);
1357 if (is_drawable()) {
1358 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1361 hide_verbose_canvas_cursor ();
1364 case AudioRegionViewNameHighlight:
1365 case StartSelectionTrimItem:
1366 case EndSelectionTrimItem:
1367 case EditCursorItem:
1368 case PlayheadCursorItem:
1369 /* <CMT Additions> */
1370 case ImageFrameHandleStartItem:
1371 case ImageFrameHandleEndItem:
1372 case MarkerViewHandleStartItem:
1373 case MarkerViewHandleEndItem:
1374 /* </CMT Additions> */
1375 if (is_drawable()) {
1376 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1381 case GainAutomationLineItem:
1382 case RedirectAutomationLineItem:
1383 case PanAutomationLineItem:
1384 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1386 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1388 line->property_fill_color_rgba() = al->get_line_color();
1390 if (is_drawable()) {
1391 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1395 case AudioRegionViewName:
1396 /* see enter_handler() for notes */
1397 if (!reinterpret_cast<AudioRegionView *> (item->get_data ("regionview"))->name_active()) {
1398 if (is_drawable() && mouse_mode == MouseObject) {
1399 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1404 case RangeMarkerBarItem:
1405 case TransportMarkerBarItem:
1409 if (is_drawable()) {
1410 time_canvas.get_window()->set_cursor (*timebar_cursor);
1415 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1418 loc = find_location_from_marker (marker, is_start);
1419 if (loc) location_flags_changed (loc, this);
1421 case MeterMarkerItem:
1422 case TempoMarkerItem:
1424 if (is_drawable()) {
1425 time_canvas.get_window()->set_cursor (*timebar_cursor);
1430 case FadeInHandleItem:
1431 case FadeOutHandleItem:
1432 rv = static_cast<AudioRegionView*>(item->get_data ("regionview"));
1434 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1436 rect->property_fill_color_rgba() = rv->get_fill_color();
1437 rect->property_outline_pixels() = 0;
1442 case AutomationTrackItem:
1443 if (is_drawable()) {
1444 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1445 clear_entered_track = true;
1446 Glib::signal_idle().connect (mem_fun(*this, &Editor::left_automation_track));
1458 Editor::left_automation_track ()
1460 if (clear_entered_track) {
1461 set_entered_track (0);
1462 clear_entered_track = false;
1468 Editor::motion_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type, bool from_autoscroll)
1472 /* We call this so that MOTION_NOTIFY events continue to be
1473 delivered to the canvas. We need to do this because we set
1474 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
1475 the density of the events, at the expense of a round-trip
1476 to the server. Given that this will mostly occur on cases
1477 where DISPLAY = :0.0, and given the cost of what the motion
1478 event might do, its a good tradeoff.
1481 track_canvas.get_pointer (x, y);
1483 if (current_stepping_trackview) {
1484 /* don't keep the persistent stepped trackview if the mouse moves */
1485 current_stepping_trackview = 0;
1486 step_timeout.disconnect ();
1489 if (session && session->actively_recording()) {
1490 /* Sorry. no dragging stuff around while we record */
1494 drag_info.item_type = item_type;
1495 drag_info.current_pointer_frame = event_frame (event, &drag_info.current_pointer_x,
1496 &drag_info.current_pointer_y);
1498 if (!from_autoscroll && drag_info.item) {
1499 /* item != 0 is the best test i can think of for dragging.
1501 if (!drag_info.move_threshold_passed) {
1503 drag_info.move_threshold_passed = (abs ((int) (drag_info.current_pointer_x - drag_info.grab_x)) > 4);
1505 // and change the initial grab loc/frame if this drag info wants us to
1507 if (drag_info.want_move_threshold && drag_info.move_threshold_passed) {
1508 drag_info.grab_frame = drag_info.current_pointer_frame;
1509 drag_info.grab_x = drag_info.current_pointer_x;
1510 drag_info.grab_y = drag_info.current_pointer_y;
1511 drag_info.last_pointer_frame = drag_info.grab_frame;
1512 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
1517 switch (item_type) {
1518 case PlayheadCursorItem:
1519 case EditCursorItem:
1521 case GainControlPointItem:
1522 case RedirectAutomationControlPointItem:
1523 case GainAutomationControlPointItem:
1524 case PanAutomationControlPointItem:
1525 case TempoMarkerItem:
1526 case MeterMarkerItem:
1527 case AudioRegionViewNameHighlight:
1528 case StartSelectionTrimItem:
1529 case EndSelectionTrimItem:
1532 case RedirectAutomationLineItem:
1533 case GainAutomationLineItem:
1534 case PanAutomationLineItem:
1535 case FadeInHandleItem:
1536 case FadeOutHandleItem:
1537 /* <CMT Additions> */
1538 case ImageFrameHandleStartItem:
1539 case ImageFrameHandleEndItem:
1540 case MarkerViewHandleStartItem:
1541 case MarkerViewHandleEndItem:
1542 /* </CMT Additions> */
1543 if (drag_info.item && (event->motion.state & Gdk::BUTTON1_MASK ||
1544 (event->motion.state & Gdk::BUTTON2_MASK))) {
1545 if (!from_autoscroll) {
1546 maybe_autoscroll (event);
1548 (this->*(drag_info.motion_callback)) (item, event);
1557 switch (mouse_mode) {
1562 if (drag_info.item && (event->motion.state & GDK_BUTTON1_MASK ||
1563 (event->motion.state & GDK_BUTTON2_MASK))) {
1564 if (!from_autoscroll) {
1565 maybe_autoscroll (event);
1567 (this->*(drag_info.motion_callback)) (item, event);
1578 track_canvas_motion (event);
1579 // drag_info.last_pointer_frame = drag_info.current_pointer_frame;
1587 Editor::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
1589 if (drag_info.item == 0) {
1590 fatal << _("programming error: start_grab called without drag item") << endmsg;
1596 cursor = grabber_cursor;
1599 // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
1601 if (event->button.button == 2) {
1602 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Alt)) {
1603 drag_info.y_constrained = true;
1604 drag_info.x_constrained = false;
1606 drag_info.y_constrained = false;
1607 drag_info.x_constrained = true;
1610 drag_info.x_constrained = false;
1611 drag_info.y_constrained = false;
1614 drag_info.grab_frame = event_frame(event, &drag_info.grab_x, &drag_info.grab_y);
1615 drag_info.last_pointer_frame = drag_info.grab_frame;
1616 drag_info.current_pointer_frame = drag_info.grab_frame;
1617 drag_info.current_pointer_x = drag_info.grab_x;
1618 drag_info.current_pointer_y = drag_info.grab_y;
1619 drag_info.cumulative_x_drag = 0;
1620 drag_info.cumulative_y_drag = 0;
1621 drag_info.first_move = true;
1622 drag_info.move_threshold_passed = false;
1623 drag_info.want_move_threshold = false;
1624 drag_info.pointer_frame_offset = 0;
1625 drag_info.brushing = false;
1626 drag_info.copied_location = 0;
1628 drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK,
1630 event->button.time);
1632 if (session && session->transport_rolling()) {
1633 drag_info.was_rolling = true;
1635 drag_info.was_rolling = false;
1638 switch (snap_type) {
1639 case SnapToRegionStart:
1640 case SnapToRegionEnd:
1641 case SnapToRegionSync:
1642 case SnapToRegionBoundary:
1643 build_region_boundary_cache ();
1651 Editor::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t time)
1653 drag_info.item->ungrab (0);
1654 drag_info.item = new_item;
1657 cursor = grabber_cursor;
1660 drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK, *cursor, time);
1664 Editor::end_grab (ArdourCanvas::Item* item, GdkEvent* event)
1666 bool did_drag = false;
1668 stop_canvas_autoscroll ();
1670 if (drag_info.item == 0) {
1674 drag_info.item->ungrab (event->button.time);
1676 if (drag_info.finished_callback) {
1677 (this->*(drag_info.finished_callback)) (item, event);
1680 did_drag = !drag_info.first_move;
1682 hide_verbose_canvas_cursor();
1685 drag_info.copy = false;
1686 drag_info.motion_callback = 0;
1687 drag_info.finished_callback = 0;
1688 drag_info.last_trackview = 0;
1689 drag_info.last_frame_position = 0;
1690 drag_info.grab_frame = 0;
1691 drag_info.last_pointer_frame = 0;
1692 drag_info.current_pointer_frame = 0;
1693 drag_info.brushing = false;
1695 if (drag_info.copied_location) {
1696 delete drag_info.copied_location;
1697 drag_info.copied_location = 0;
1704 Editor::set_edit_cursor (GdkEvent* event)
1706 jack_nframes_t pointer_frame = event_frame (event);
1708 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1709 if (snap_type != SnapToEditCursor) {
1710 snap_to (pointer_frame);
1714 edit_cursor->set_position (pointer_frame);
1715 edit_cursor_clock.set (pointer_frame);
1719 Editor::set_playhead_cursor (GdkEvent* event)
1721 jack_nframes_t pointer_frame = event_frame (event);
1723 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1724 snap_to (pointer_frame);
1728 session->request_locate (pointer_frame, session->transport_rolling());
1733 Editor::start_fade_in_grab (ArdourCanvas::Item* item, GdkEvent* event)
1735 drag_info.item = item;
1736 drag_info.motion_callback = &Editor::fade_in_drag_motion_callback;
1737 drag_info.finished_callback = &Editor::fade_in_drag_finished_callback;
1741 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
1742 fatal << _("programming error: fade in canvas item has no regionview data pointer!") << endmsg;
1746 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1748 drag_info.pointer_frame_offset = drag_info.grab_frame - ((jack_nframes_t) arv->region.fade_in().back()->when + arv->region.position());
1752 Editor::fade_in_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1754 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1756 jack_nframes_t fade_length;
1758 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1759 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1765 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1769 if (pos < (arv->region.position() + 64)) {
1770 fade_length = 64; // this should be a minimum defined somewhere
1771 } else if (pos > arv->region.last_frame()) {
1772 fade_length = arv->region.length();
1774 fade_length = pos - arv->region.position();
1777 arv->reset_fade_in_shape_width (fade_length);
1779 show_verbose_duration_cursor (arv->region.position(), arv->region.position() + fade_length, 10);
1781 drag_info.first_move = false;
1785 Editor::fade_in_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1787 if (drag_info.first_move) return;
1789 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1791 jack_nframes_t fade_length;
1793 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1794 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1800 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1804 if (pos < (arv->region.position() + 64)) {
1805 fade_length = 64; // this should be a minimum defined somewhere
1807 else if (pos > arv->region.last_frame()) {
1808 fade_length = arv->region.length();
1811 fade_length = pos - arv->region.position();
1814 begin_reversible_command (_("change fade in length"));
1815 session->add_undo (arv->region.get_memento());
1816 arv->region.set_fade_in_length (fade_length);
1817 session->add_redo_no_execute (arv->region.get_memento());
1818 commit_reversible_command ();
1819 fade_in_drag_motion_callback (item, event);
1823 Editor::start_fade_out_grab (ArdourCanvas::Item* item, GdkEvent* event)
1825 drag_info.item = item;
1826 drag_info.motion_callback = &Editor::fade_out_drag_motion_callback;
1827 drag_info.finished_callback = &Editor::fade_out_drag_finished_callback;
1831 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
1832 fatal << _("programming error: fade out canvas item has no regionview data pointer!") << endmsg;
1836 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1838 drag_info.pointer_frame_offset = drag_info.grab_frame - (arv->region.length() - (jack_nframes_t) arv->region.fade_out().back()->when + arv->region.position());
1842 Editor::fade_out_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1844 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1846 jack_nframes_t fade_length;
1848 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1849 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1855 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1859 if (pos > (arv->region.last_frame() - 64)) {
1860 fade_length = 64; // this should really be a minimum fade defined somewhere
1862 else if (pos < arv->region.position()) {
1863 fade_length = arv->region.length();
1866 fade_length = arv->region.last_frame() - pos;
1869 arv->reset_fade_out_shape_width (fade_length);
1871 show_verbose_duration_cursor (arv->region.last_frame() - fade_length, arv->region.last_frame(), 10);
1873 drag_info.first_move = false;
1877 Editor::fade_out_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1879 if (drag_info.first_move) return;
1881 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1883 jack_nframes_t fade_length;
1885 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1886 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1892 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1896 if (pos > (arv->region.last_frame() - 64)) {
1897 fade_length = 64; // this should really be a minimum fade defined somewhere
1899 else if (pos < arv->region.position()) {
1900 fade_length = arv->region.length();
1903 fade_length = arv->region.last_frame() - pos;
1906 begin_reversible_command (_("change fade out length"));
1907 session->add_undo (arv->region.get_memento());
1908 arv->region.set_fade_out_length (fade_length);
1909 session->add_redo_no_execute (arv->region.get_memento());
1910 commit_reversible_command ();
1912 fade_out_drag_motion_callback (item, event);
1916 Editor::start_cursor_grab (ArdourCanvas::Item* item, GdkEvent* event)
1918 drag_info.item = item;
1919 drag_info.motion_callback = &Editor::cursor_drag_motion_callback;
1920 drag_info.finished_callback = &Editor::cursor_drag_finished_callback;
1924 if ((drag_info.data = (item->get_data ("cursor"))) == 0) {
1925 fatal << _("programming error: cursor canvas item has no cursor data pointer!") << endmsg;
1929 Cursor* cursor = (Cursor *) drag_info.data;
1931 if (session && cursor == playhead_cursor) {
1932 if (drag_info.was_rolling) {
1933 session->request_stop ();
1937 drag_info.pointer_frame_offset = drag_info.grab_frame - cursor->current_frame;
1939 show_verbose_time_cursor (cursor->current_frame, 10);
1943 Editor::cursor_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1945 Cursor* cursor = (Cursor *) drag_info.data;
1946 jack_nframes_t adjusted_frame;
1948 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1949 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1955 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1956 if (cursor != edit_cursor || snap_type != SnapToEditCursor) {
1957 snap_to (adjusted_frame);
1961 if (adjusted_frame == drag_info.last_pointer_frame) return;
1963 cursor->set_position (adjusted_frame);
1965 if (cursor == edit_cursor) {
1966 edit_cursor_clock.set (cursor->current_frame);
1969 show_verbose_time_cursor (cursor->current_frame, 10);
1971 drag_info.last_pointer_frame = adjusted_frame;
1972 drag_info.first_move = false;
1976 Editor::cursor_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1978 if (drag_info.first_move) return;
1980 cursor_drag_motion_callback (item, event);
1982 if (item == &playhead_cursor->canvas_item) {
1984 session->request_locate (playhead_cursor->current_frame, drag_info.was_rolling);
1986 } else if (item == &edit_cursor->canvas_item) {
1987 edit_cursor->set_position (edit_cursor->current_frame);
1988 edit_cursor_clock.set (edit_cursor->current_frame);
1993 Editor::update_marker_drag_item (Location *location)
1995 double x1 = frame_to_pixel (location->start());
1996 double x2 = frame_to_pixel (location->end());
1998 if (location->is_mark()) {
1999 marker_drag_line_points.front().set_x(x1);
2000 marker_drag_line_points.back().set_x(x1);
2001 marker_drag_line->property_points() = marker_drag_line_points;
2004 range_marker_drag_rect->property_x1() = x1;
2005 range_marker_drag_rect->property_x2() = x2;
2010 Editor::start_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2014 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2015 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2021 Location *location = find_location_from_marker (marker, is_start);
2023 drag_info.item = item;
2024 drag_info.data = marker;
2025 drag_info.motion_callback = &Editor::marker_drag_motion_callback;
2026 drag_info.finished_callback = &Editor::marker_drag_finished_callback;
2030 drag_info.copied_location = new Location (*location);
2031 drag_info.pointer_frame_offset = drag_info.grab_frame - (is_start ? location->start() : location->end());
2033 update_marker_drag_item (location);
2035 if (location->is_mark()) {
2036 marker_drag_line->show();
2037 marker_drag_line->raise_to_top();
2040 range_marker_drag_rect->show();
2041 range_marker_drag_rect->raise_to_top();
2044 if (is_start) show_verbose_time_cursor (location->start(), 10);
2045 else show_verbose_time_cursor (location->end(), 10);
2049 Editor::marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2051 jack_nframes_t f_delta;
2052 Marker* marker = (Marker *) drag_info.data;
2053 Location *real_location;
2054 Location *copy_location;
2056 bool move_both = false;
2059 jack_nframes_t newframe;
2060 if (drag_info.pointer_frame_offset <= (long) drag_info.current_pointer_frame) {
2061 newframe = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2067 jack_nframes_t next = newframe;
2069 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2070 snap_to (newframe, 0, true);
2073 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
2077 /* call this to find out if its the start or end */
2079 real_location = find_location_from_marker (marker, is_start);
2081 /* use the copy that we're "dragging" around */
2083 copy_location = drag_info.copied_location;
2085 f_delta = copy_location->end() - copy_location->start();
2087 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
2091 if (copy_location->is_mark()) {
2094 copy_location->set_start (newframe);
2098 if (is_start) { // start-of-range marker
2101 copy_location->set_start (newframe);
2102 copy_location->set_end (newframe + f_delta);
2103 } else if (newframe < copy_location->end()) {
2104 copy_location->set_start (newframe);
2106 snap_to (next, 1, true);
2107 copy_location->set_end (next);
2108 copy_location->set_start (newframe);
2111 } else { // end marker
2114 copy_location->set_end (newframe);
2115 copy_location->set_start (newframe - f_delta);
2116 } else if (newframe > copy_location->start()) {
2117 copy_location->set_end (newframe);
2119 } else if (newframe > 0) {
2120 snap_to (next, -1, true);
2121 copy_location->set_start (next);
2122 copy_location->set_end (newframe);
2127 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
2128 drag_info.first_move = false;
2130 update_marker_drag_item (copy_location);
2132 LocationMarkers* lm = find_location_markers (real_location);
2133 lm->set_position (copy_location->start(), copy_location->end());
2135 show_verbose_time_cursor (newframe, 10);
2139 Editor::marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2141 if (drag_info.first_move) {
2142 marker_drag_motion_callback (item, event);
2146 Marker* marker = (Marker *) drag_info.data;
2150 begin_reversible_command ( _("move marker") );
2151 session->add_undo( session->locations()->get_memento() );
2153 Location * location = find_location_from_marker (marker, is_start);
2156 if (location->is_mark()) {
2157 location->set_start (drag_info.copied_location->start());
2159 location->set (drag_info.copied_location->start(), drag_info.copied_location->end());
2163 session->add_redo_no_execute( session->locations()->get_memento() );
2164 commit_reversible_command ();
2166 marker_drag_line->hide();
2167 range_marker_drag_rect->hide();
2171 Editor::start_meter_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2174 MeterMarker* meter_marker;
2176 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2177 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2181 meter_marker = dynamic_cast<MeterMarker*> (marker);
2183 MetricSection& section (meter_marker->meter());
2185 if (!section.movable()) {
2189 drag_info.item = item;
2190 drag_info.data = marker;
2191 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2192 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2196 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2198 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2202 Editor::start_meter_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2205 MeterMarker* meter_marker;
2207 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2208 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2212 meter_marker = dynamic_cast<MeterMarker*> (marker);
2214 // create a dummy marker for visual representation of moving the copy.
2215 // The actual copying is not done before we reach the finish callback.
2217 snprintf (name, sizeof(name), "%g/%g", meter_marker->meter().beats_per_bar(), meter_marker->meter().note_divisor ());
2218 MeterMarker* new_marker = new MeterMarker(*this, *meter_group, color_map[cMeterMarker], name,
2219 *new MeterSection(meter_marker->meter()));
2221 drag_info.item = &new_marker->the_item();
2222 drag_info.copy = true;
2223 drag_info.data = new_marker;
2224 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2225 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2229 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2231 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2235 Editor::meter_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2237 MeterMarker* marker = (MeterMarker *) drag_info.data;
2238 jack_nframes_t adjusted_frame;
2240 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2241 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2247 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2248 snap_to (adjusted_frame);
2251 if (adjusted_frame == drag_info.last_pointer_frame) return;
2253 marker->set_position (adjusted_frame);
2256 drag_info.last_pointer_frame = adjusted_frame;
2257 drag_info.first_move = false;
2259 show_verbose_time_cursor (adjusted_frame, 10);
2263 Editor::meter_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2265 if (drag_info.first_move) return;
2267 meter_marker_drag_motion_callback (drag_info.item, event);
2269 MeterMarker* marker = (MeterMarker *) drag_info.data;
2272 TempoMap& map (session->tempo_map());
2273 map.bbt_time (drag_info.last_pointer_frame, when);
2275 if (drag_info.copy == true) {
2276 begin_reversible_command (_("copy meter mark"));
2277 session->add_undo (map.get_memento());
2278 map.add_meter (marker->meter(), when);
2279 session->add_redo_no_execute (map.get_memento());
2280 commit_reversible_command ();
2282 // delete the dummy marker we used for visual representation of copying.
2283 // a new visual marker will show up automatically.
2286 begin_reversible_command (_("move meter mark"));
2287 session->add_undo (map.get_memento());
2288 map.move_meter (marker->meter(), when);
2289 session->add_redo_no_execute (map.get_memento());
2290 commit_reversible_command ();
2295 Editor::start_tempo_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2298 TempoMarker* tempo_marker;
2300 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2301 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2305 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2306 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2310 MetricSection& section (tempo_marker->tempo());
2312 if (!section.movable()) {
2316 drag_info.item = item;
2317 drag_info.data = marker;
2318 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2319 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2323 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2324 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2328 Editor::start_tempo_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2331 TempoMarker* tempo_marker;
2333 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2334 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2338 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2339 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2343 // create a dummy marker for visual representation of moving the copy.
2344 // The actual copying is not done before we reach the finish callback.
2346 snprintf (name, sizeof (name), "%.2f", tempo_marker->tempo().beats_per_minute());
2347 TempoMarker* new_marker = new TempoMarker(*this, *tempo_group, color_map[cTempoMarker], name,
2348 *new TempoSection(tempo_marker->tempo()));
2350 drag_info.item = &new_marker->the_item();
2351 drag_info.copy = true;
2352 drag_info.data = new_marker;
2353 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2354 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2358 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2360 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2364 Editor::tempo_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2366 TempoMarker* marker = (TempoMarker *) drag_info.data;
2367 jack_nframes_t adjusted_frame;
2369 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2370 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2376 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2377 snap_to (adjusted_frame);
2380 if (adjusted_frame == drag_info.last_pointer_frame) return;
2382 /* OK, we've moved far enough to make it worth actually move the thing. */
2384 marker->set_position (adjusted_frame);
2386 show_verbose_time_cursor (adjusted_frame, 10);
2388 drag_info.last_pointer_frame = adjusted_frame;
2389 drag_info.first_move = false;
2393 Editor::tempo_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2395 if (drag_info.first_move) return;
2397 tempo_marker_drag_motion_callback (drag_info.item, event);
2399 TempoMarker* marker = (TempoMarker *) drag_info.data;
2402 TempoMap& map (session->tempo_map());
2403 map.bbt_time (drag_info.last_pointer_frame, when);
2405 if (drag_info.copy == true) {
2406 begin_reversible_command (_("copy tempo mark"));
2407 session->add_undo (map.get_memento());
2408 map.add_tempo (marker->tempo(), when);
2409 session->add_redo_no_execute (map.get_memento());
2410 commit_reversible_command ();
2412 // delete the dummy marker we used for visual representation of copying.
2413 // a new visual marker will show up automatically.
2416 begin_reversible_command (_("move tempo mark"));
2417 session->add_undo (map.get_memento());
2418 map.move_tempo (marker->tempo(), when);
2419 session->add_redo_no_execute (map.get_memento());
2420 commit_reversible_command ();
2425 Editor::remove_gain_control_point (ArdourCanvas::Item*item, GdkEvent* event)
2427 ControlPoint* control_point;
2429 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2430 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2434 // We shouldn't remove the first or last gain point
2435 if (control_point->line.is_last_point(*control_point) ||
2436 control_point->line.is_first_point(*control_point)) {
2440 control_point->line.remove_point (*control_point);
2444 Editor::remove_control_point (ArdourCanvas::Item*item, GdkEvent* event)
2446 ControlPoint* control_point;
2448 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2449 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2453 control_point->line.remove_point (*control_point);
2457 Editor::start_control_point_grab (ArdourCanvas::Item* item, GdkEvent* event)
2459 ControlPoint* control_point;
2461 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2462 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2466 drag_info.item = item;
2467 drag_info.data = control_point;
2468 drag_info.motion_callback = &Editor::control_point_drag_motion_callback;
2469 drag_info.finished_callback = &Editor::control_point_drag_finished_callback;
2471 start_grab (event, fader_cursor);
2473 control_point->line.start_drag (control_point, 0);
2475 float fraction = 1.0 - (control_point->get_y() / control_point->line.height());
2476 set_verbose_canvas_cursor (control_point->line.get_verbose_cursor_string (fraction),
2477 drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
2479 show_verbose_canvas_cursor ();
2483 Editor::control_point_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2485 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2487 double cx = drag_info.current_pointer_x;
2488 double cy = drag_info.current_pointer_y;
2490 drag_info.cumulative_x_drag = cx - drag_info.grab_x ;
2491 drag_info.cumulative_y_drag = cy - drag_info.grab_y ;
2493 if (drag_info.x_constrained) {
2494 cx = drag_info.grab_x;
2496 if (drag_info.y_constrained) {
2497 cy = drag_info.grab_y;
2500 cp->line.parent_group().w2i (cx, cy);
2504 cy = min ((double) cp->line.height(), cy);
2506 //translate cx to frames
2507 jack_nframes_t cx_frames = unit_to_frame (cx);
2509 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier()) && !drag_info.x_constrained) {
2510 snap_to (cx_frames);
2513 float fraction = 1.0 - (cy / cp->line.height());
2517 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
2523 cp->line.point_drag (*cp, cx_frames , fraction, push);
2525 set_verbose_canvas_cursor_text (cp->line.get_verbose_cursor_string (fraction));
2527 drag_info.first_move = false;
2531 Editor::control_point_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2533 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2535 if (drag_info.first_move) {
2539 if ((event->type == GDK_BUTTON_RELEASE) && (event->button.button == 1) && Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
2540 reset_point_selection ();
2544 control_point_drag_motion_callback (item, event);
2546 cp->line.end_drag (cp);
2550 Editor::start_line_grab_from_regionview (ArdourCanvas::Item* item, GdkEvent* event)
2552 switch (mouse_mode) {
2554 start_line_grab (clicked_regionview->get_gain_line(), event);
2562 Editor::start_line_grab_from_line (ArdourCanvas::Item* item, GdkEvent* event)
2566 if ((al = reinterpret_cast<AutomationLine*> (item->get_data ("line"))) == 0) {
2567 fatal << _("programming error: line canvas item has no line pointer!") << endmsg;
2571 start_line_grab (al, event);
2575 Editor::start_line_grab (AutomationLine* line, GdkEvent* event)
2579 jack_nframes_t frame_within_region;
2581 /* need to get x coordinate in terms of parent (TimeAxisItemView)
2585 cx = event->button.x;
2586 cy = event->button.y;
2587 line->parent_group().w2i (cx, cy);
2588 frame_within_region = (jack_nframes_t) floor (cx * frames_per_unit);
2590 if (!line->control_points_adjacent (frame_within_region, current_line_drag_info.before,
2591 current_line_drag_info.after)) {
2592 /* no adjacent points */
2596 drag_info.item = &line->grab_item();
2597 drag_info.data = line;
2598 drag_info.motion_callback = &Editor::line_drag_motion_callback;
2599 drag_info.finished_callback = &Editor::line_drag_finished_callback;
2601 start_grab (event, fader_cursor);
2603 double fraction = 1.0 - (cy / line->height());
2605 line->start_drag (0, fraction);
2607 set_verbose_canvas_cursor (line->get_verbose_cursor_string (fraction),
2608 drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
2609 show_verbose_canvas_cursor ();
2613 Editor::line_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2615 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
2616 double cx = drag_info.current_pointer_x;
2617 double cy = drag_info.current_pointer_y;
2619 line->parent_group().w2i (cx, cy);
2622 fraction = 1.0 - (cy / line->height());
2626 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
2632 line->line_drag (current_line_drag_info.before, current_line_drag_info.after, fraction, push);
2634 set_verbose_canvas_cursor_text (line->get_verbose_cursor_string (fraction));
2638 Editor::line_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2640 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
2641 line_drag_motion_callback (item, event);
2646 Editor::start_region_grab (ArdourCanvas::Item* item, GdkEvent* event)
2648 if (selection->audio_regions.empty() || clicked_regionview == 0) {
2652 drag_info.copy = false;
2653 drag_info.item = item;
2654 drag_info.data = clicked_regionview;
2655 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2656 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2661 TimeAxisView* tvp = clicked_trackview;
2662 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
2664 if (tv && tv->is_audio_track()) {
2665 speed = tv->get_diskstream()->speed();
2668 drag_info.last_frame_position = (jack_nframes_t) (clicked_regionview->region.position() / speed);
2669 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2670 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2671 // we want a move threshold
2672 drag_info.want_move_threshold = true;
2674 show_verbose_time_cursor (drag_info.last_frame_position, 10);
2676 begin_reversible_command (_("move region(s)"));
2680 Editor::start_region_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2682 if (selection->audio_regions.empty() || clicked_regionview == 0) {
2686 drag_info.copy = true;
2687 drag_info.item = item;
2688 drag_info.data = clicked_regionview;
2692 TimeAxisView* tv = &clicked_regionview->get_time_axis_view();
2693 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*>(tv);
2696 if (atv && atv->is_audio_track()) {
2697 speed = atv->get_diskstream()->speed();
2700 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2701 drag_info.last_frame_position = (jack_nframes_t) (clicked_regionview->region.position() / speed);
2702 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2703 // we want a move threshold
2704 drag_info.want_move_threshold = true;
2705 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2706 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2710 Editor::start_region_brush_grab (ArdourCanvas::Item* item, GdkEvent* event)
2712 if (selection->audio_regions.empty() || clicked_regionview == 0) {
2716 drag_info.copy = false;
2717 drag_info.item = item;
2718 drag_info.data = clicked_regionview;
2719 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2720 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2725 TimeAxisView* tvp = clicked_trackview;
2726 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
2728 if (tv && tv->is_audio_track()) {
2729 speed = tv->get_diskstream()->speed();
2732 drag_info.last_frame_position = (jack_nframes_t) (clicked_regionview->region.position() / speed);
2733 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2734 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2735 // we want a move threshold
2736 drag_info.want_move_threshold = true;
2737 drag_info.brushing = true;
2739 begin_reversible_command (_("Drag region brush"));
2743 Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2747 AudioRegionView *rv = reinterpret_cast<AudioRegionView*> (drag_info.data);
2748 jack_nframes_t pending_region_position = 0;
2749 int32_t pointer_y_span = 0, canvas_pointer_y_span = 0, original_pointer_order;
2750 int32_t visible_y_high = 0, visible_y_low = 512; //high meaning higher numbered.. not the height on the screen
2751 bool clamp_y_axis = false;
2752 vector<int32_t> height_list(512) ;
2753 vector<int32_t>::iterator j;
2755 show_verbose_time_cursor (drag_info.last_frame_position, 10);
2757 if (drag_info.copy && drag_info.move_threshold_passed && drag_info.want_move_threshold) {
2759 drag_info.want_move_threshold = false; // don't copy again
2761 /* this is committed in the grab finished callback. */
2763 begin_reversible_command (_("Drag region copy"));
2765 /* duplicate the region(s) */
2767 vector<AudioRegionView*> new_regionviews;
2769 set<Playlist*> affected_playlists;
2770 pair<set<Playlist*>::iterator,bool> insert_result;
2772 for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin(); i != selection->audio_regions.by_layer().end(); ++i) {
2773 AudioRegionView* rv;
2777 Playlist* to_playlist = rv->region.playlist();
2778 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*>(&rv->get_time_axis_view());
2780 insert_result = affected_playlists.insert (to_playlist);
2781 if (insert_result.second) {
2782 session->add_undo (to_playlist->get_memento ());
2785 latest_regionview = 0;
2787 sigc::connection c = atv->view->AudioRegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
2789 /* create a new region with the same name.
2792 AudioRegion* newregion = new AudioRegion (rv->region);
2794 /* if the original region was locked, we don't care */
2796 newregion->set_locked (false);
2798 to_playlist->add_region (*newregion, (jack_nframes_t) (rv->region.position() * atv->get_diskstream()->speed()));
2802 if (latest_regionview) {
2803 new_regionviews.push_back (latest_regionview);
2807 if (new_regionviews.empty()) {
2811 /* reset selection to new regionviews */
2813 selection->set (new_regionviews);
2815 /* reset drag_info data to reflect the fact that we are dragging the copies */
2817 drag_info.data = new_regionviews.front();
2818 swap_grab (new_regionviews.front()->get_canvas_group (), 0, event->motion.time);
2821 /* Which trackview is this ? */
2823 TimeAxisView* tvp = trackview_by_y_position (drag_info.current_pointer_y);
2824 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
2826 /* The region motion is only processed if the pointer is over
2830 if (!tv || !tv->is_audio_track()) {
2831 /* To make sure we hide the verbose canvas cursor when the mouse is
2832 not held over and audiotrack.
2834 hide_verbose_canvas_cursor ();
2838 original_pointer_order = drag_info.last_trackview->order;
2840 /************************************************************
2842 ************************************************************/
2844 if (drag_info.brushing) {
2845 clamp_y_axis = true;
2850 if ((pointer_y_span = (drag_info.last_trackview->order - tv->order)) != 0) {
2852 int32_t children = 0, numtracks = 0;
2853 // XXX hard coding track limit, oh my, so very very bad
2854 bitset <1024> tracks (0x00);
2855 /* get a bitmask representing the visible tracks */
2857 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2858 TimeAxisView *tracklist_timeview;
2859 tracklist_timeview = (*i);
2860 AudioTimeAxisView* atv2 = dynamic_cast<AudioTimeAxisView*>(tracklist_timeview);
2861 list<TimeAxisView*> children_list;
2863 /* zeroes are audio tracks. ones are other types. */
2865 if (!atv2->hidden()) {
2867 if (visible_y_high < atv2->order) {
2868 visible_y_high = atv2->order;
2870 if (visible_y_low > atv2->order) {
2871 visible_y_low = atv2->order;
2874 if (!atv2->is_audio_track()) {
2875 tracks = tracks |= (0x01 << atv2->order);
2878 height_list[atv2->order] = (*i)->height;
2880 if ((children_list = atv2->get_child_list()).size() > 0) {
2881 for (list<TimeAxisView*>::iterator j = children_list.begin(); j != children_list.end(); ++j) {
2882 tracks = tracks |= (0x01 << (atv2->order + children));
2883 height_list[atv2->order + children] = (*j)->height;
2891 /* find the actual span according to the canvas */
2893 canvas_pointer_y_span = pointer_y_span;
2894 if (drag_info.last_trackview->order >= tv->order) {
2896 for (y = tv->order; y < drag_info.last_trackview->order; y++) {
2897 if (height_list[y] == 0 ) {
2898 canvas_pointer_y_span--;
2903 for (y = drag_info.last_trackview->order;y <= tv->order; y++) {
2904 if ( height_list[y] == 0 ) {
2905 canvas_pointer_y_span++;
2910 for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin(); i != selection->audio_regions.by_layer().end(); ++i) {
2911 AudioRegionView* rv2;
2913 double ix1, ix2, iy1, iy2;
2916 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
2917 rv2->get_canvas_group()->i2w (ix1, iy1);
2918 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
2919 AudioTimeAxisView* atv2 = dynamic_cast<AudioTimeAxisView*>(tvp2);
2921 if (atv2->order != original_pointer_order) {
2922 /* this isn't the pointer track */
2924 if (canvas_pointer_y_span > 0) {
2926 /* moving up the canvas */
2927 if ((atv2->order - canvas_pointer_y_span) >= visible_y_low) {
2929 int32_t visible_tracks = 0;
2930 while (visible_tracks < canvas_pointer_y_span ) {
2933 while (height_list[atv2->order - (visible_tracks - n)] == 0) {
2934 /* we're passing through a hidden track */
2939 if (tracks[atv2->order - (canvas_pointer_y_span - n)] != 0x00) {
2940 clamp_y_axis = true;
2944 clamp_y_axis = true;
2947 } else if (canvas_pointer_y_span < 0) {
2949 /*moving down the canvas*/
2951 if ((atv2->order - (canvas_pointer_y_span - n)) <= visible_y_high) { // we will overflow
2954 int32_t visible_tracks = 0;
2956 while (visible_tracks > canvas_pointer_y_span ) {
2959 while (height_list[atv2->order - (visible_tracks - n)] == 0) {
2963 if ( tracks[atv2->order - ( canvas_pointer_y_span - n)] != 0x00) {
2964 clamp_y_axis = true;
2969 clamp_y_axis = true;
2975 /* this is the pointer's track */
2976 if ((atv2->order - pointer_y_span) > visible_y_high) { // we will overflow
2977 clamp_y_axis = true;
2978 } else if ((atv2->order - pointer_y_span) < visible_y_low) { // we will underflow
2979 clamp_y_axis = true;
2987 } else if (drag_info.last_trackview == tv) {
2988 clamp_y_axis = true;
2992 if (!clamp_y_axis) {
2993 drag_info.last_trackview = tv;
2996 /************************************************************
2998 ************************************************************/
3000 /* compute the amount of pointer motion in frames, and where
3001 the region would be if we moved it by that much.
3004 if (drag_info.move_threshold_passed) {
3006 if ((int32_t)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
3008 jack_nframes_t sync_frame;
3009 jack_nframes_t sync_offset;
3012 pending_region_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
3014 sync_offset = rv->region.sync_offset (sync_dir);
3015 sync_frame = rv->region.adjust_to_sync (pending_region_position);
3017 /* we snap if the snap modifier is not enabled.
3020 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3021 snap_to (sync_frame);
3024 if (sync_frame - sync_offset <= sync_frame) {
3025 pending_region_position = sync_frame - (sync_dir*sync_offset);
3027 pending_region_position = 0;
3031 pending_region_position = 0;
3034 if (pending_region_position > max_frames - rv->region.length()) {
3035 pending_region_position = drag_info.last_frame_position;
3038 // printf ("3: pending_region_position= %lu %lu\n", pending_region_position, drag_info.last_frame_position );
3040 if (pending_region_position != drag_info.last_frame_position && !drag_info.x_constrained) {
3042 /* now compute the canvas unit distance we need to move the regiondrag_info.last_trackview->order
3043 to make it appear at the new location.
3046 if (pending_region_position > drag_info.last_frame_position) {
3047 x_delta = ((double) (pending_region_position - drag_info.last_frame_position) / frames_per_unit);
3049 x_delta = -((double) (drag_info.last_frame_position - pending_region_position) / frames_per_unit);
3052 drag_info.last_frame_position = pending_region_position;
3059 /* threshold not passed */
3064 /*************************************************************
3066 ************************************************************/
3068 if (x_delta == 0 && (pointer_y_span == 0)) {
3069 /* haven't reached next snap point, and we're not switching
3070 trackviews. nothing to do.
3076 for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin(); i != selection->audio_regions.by_layer().end(); ++i) {
3078 AudioRegionView* rv2;
3081 /* if any regionview is at zero, we need to know so we can
3082 stop further leftward motion.
3085 double ix1, ix2, iy1, iy2;
3086 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3087 rv2->get_canvas_group()->i2w (ix1, iy1);
3096 /*************************************************************
3098 ************************************************************/
3100 pair<set<Playlist*>::iterator,bool> insert_result;
3101 const list<AudioRegionView*>& layered_regions = selection->audio_regions.by_layer();
3103 for (list<AudioRegionView*>::const_iterator i = layered_regions.begin(); i != layered_regions.end(); ++i) {
3105 AudioRegionView* rv;
3107 double ix1, ix2, iy1, iy2;
3108 int32_t temp_pointer_y_span = pointer_y_span;
3110 /* get item BBox, which will be relative to parent. so we have
3111 to query on a child, then convert to world coordinates using
3115 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3116 rv->get_canvas_group()->i2w (ix1, iy1);
3117 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3118 AudioTimeAxisView* canvas_atv = dynamic_cast<AudioTimeAxisView*>(tvp2);
3119 AudioTimeAxisView* temp_atv;
3121 if ((pointer_y_span != 0) && !clamp_y_axis) {
3124 for (j = height_list.begin(); j!= height_list.end(); j++) {
3125 if (x == canvas_atv->order) {
3126 /* we found the track the region is on */
3127 if (x != original_pointer_order) {
3128 /*this isn't from the same track we're dragging from */
3129 temp_pointer_y_span = canvas_pointer_y_span;
3131 while (temp_pointer_y_span > 0) {
3132 /* we're moving up canvas-wise,
3133 so we need to find the next track height
3135 if (j != height_list.begin()) {
3138 if (x != original_pointer_order) {
3139 /* we're not from the dragged track, so ignore hidden tracks. */
3141 temp_pointer_y_span++;
3145 temp_pointer_y_span--;
3147 while (temp_pointer_y_span < 0) {
3149 if (x != original_pointer_order) {
3151 temp_pointer_y_span--;
3155 if (j != height_list.end()) {
3158 temp_pointer_y_span++;
3160 /* find out where we'll be when we move and set height accordingly */
3162 tvp2 = trackview_by_y_position (iy1 + y_delta);
3163 temp_atv = dynamic_cast<AudioTimeAxisView*>(tvp2);
3164 rv->set_height (temp_atv->height);
3166 /* if you un-comment the following, the region colours will follow the track colours whilst dragging,
3167 personally, i think this can confuse things, but never mind.
3170 //const GdkColor& col (temp_atv->view->get_region_color());
3171 //rv->set_color (const_cast<GdkColor&>(col));
3178 /* prevent the regionview from being moved to before
3179 the zero position on the canvas.
3184 if (-x_delta > ix1) {
3187 } else if ((x_delta > 0) &&(rv->region.last_frame() > max_frames - x_delta)) {
3188 x_delta = max_frames - rv->region.last_frame();
3191 if (drag_info.first_move) {
3193 /* hide any dependent views */
3195 // rv->get_time_axis_view().hide_dependent_views (*rv);
3197 /* this is subtle. raising the regionview itself won't help,
3198 because raise_to_top() just puts the item on the top of
3199 its parent's stack. so, we need to put the trackview canvas_display group
3200 on the top, since its parent is the whole canvas.
3203 rv->get_canvas_group()->raise_to_top();
3204 rv->get_time_axis_view().canvas_display->raise_to_top();
3205 cursor_group->raise_to_top();
3207 /* freeze the playlists from notifying till
3211 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*> (&rv->get_time_axis_view());
3212 if (atv && atv->is_audio_track()) {
3213 AudioPlaylist* pl = dynamic_cast<AudioPlaylist*>(atv->get_diskstream()->playlist());
3215 /* only freeze and capture state once */
3217 insert_result = motion_frozen_playlists.insert (pl);
3218 if (insert_result.second) {
3220 session->add_undo(pl->get_memento());
3226 if (drag_info.brushing) {
3227 mouse_brush_insert_region (rv, pending_region_position);
3229 rv->move (x_delta, y_delta);
3233 if (drag_info.first_move) {
3234 cursor_group->raise_to_top();
3237 drag_info.first_move = false;
3239 if (x_delta != 0 && !drag_info.brushing) {
3240 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3246 Editor::region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3248 jack_nframes_t where;
3249 AudioRegionView* rv = reinterpret_cast<AudioRegionView *> (drag_info.data);
3250 pair<set<Playlist*>::iterator,bool> insert_result;
3251 bool nocommit = true;
3253 AudioTimeAxisView* atv;
3254 bool regionview_y_movement;
3255 bool regionview_x_movement;
3257 /* first_move is set to false if the regionview has been moved in the
3261 if (drag_info.first_move) {
3268 /* The regionview has been moved at some stage during the grab so we need
3269 to account for any mouse movement between this event and the last one.
3272 region_drag_motion_callback (item, event);
3274 if (drag_info.brushing) {
3275 /* all changes were made during motion event handlers */
3279 /* adjust for track speed */
3282 atv = dynamic_cast<AudioTimeAxisView*> (drag_info.last_trackview);
3283 if (atv && atv->get_diskstream()) {
3284 speed = atv->get_diskstream()->speed();
3287 regionview_x_movement = (drag_info.last_frame_position != (jack_nframes_t) (rv->region.position()/speed));
3288 regionview_y_movement = (drag_info.last_trackview != &rv->get_time_axis_view());
3290 //printf ("last_frame: %s position is %lu %g\n", rv->get_time_axis_view().name().c_str(), drag_info.last_frame_position, speed);
3291 //printf ("last_rackview: %s \n", drag_info.last_trackview->name().c_str());
3293 if (regionview_y_movement) {
3295 /* motion between tracks */
3297 list<AudioRegionView*> new_selection;
3299 /* moved to a different audio track. */
3301 for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin(); i != selection->audio_regions.by_layer().end(); ) {
3303 AudioRegionView* rv2 = (*i);
3305 /* the region that used to be in the old playlist is not
3306 moved to the new one - we make a copy of it. as a result,
3307 any existing editor for the region should no longer be
3311 if (!drag_info.copy) {
3312 rv2->hide_region_editor();
3314 new_selection.push_back (rv2);
3318 /* first, freeze the target tracks */
3320 for (list<AudioRegionView*>::const_iterator i = new_selection.begin(); i != new_selection.end();i++ ) {
3322 Playlist* from_playlist;
3323 Playlist* to_playlist;
3325 double ix1, ix2, iy1, iy2;
3327 (*i)->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3328 (*i)->get_canvas_group()->i2w (ix1, iy1);
3329 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3330 AudioTimeAxisView* atv2 = dynamic_cast<AudioTimeAxisView*>(tvp2);
3332 from_playlist = (*i)->region.playlist();
3333 to_playlist = atv2->playlist();
3335 /* the from_playlist was frozen in the "first_move" case
3336 of the motion handler. the insert can fail,
3337 but that doesn't matter. it just means
3338 we already have the playlist in the list.
3341 motion_frozen_playlists.insert (from_playlist);
3343 /* only freeze the to_playlist once */
3345 insert_result = motion_frozen_playlists.insert(to_playlist);
3346 if (insert_result.second) {
3347 to_playlist->freeze();
3348 session->add_undo(to_playlist->get_memento());
3353 /* now do it again with the actual operations */
3355 for (list<AudioRegionView*>::const_iterator i = new_selection.begin(); i != new_selection.end();i++ ) {
3357 Playlist* from_playlist;
3358 Playlist* to_playlist;
3360 double ix1, ix2, iy1, iy2;
3362 (*i)->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3363 (*i)->get_canvas_group()->i2w (ix1, iy1);
3364 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3365 AudioTimeAxisView* atv2 = dynamic_cast<AudioTimeAxisView*>(tvp2);
3367 from_playlist = (*i)->region.playlist();
3368 to_playlist = atv2->playlist();
3370 latest_regionview = 0;
3372 where = (jack_nframes_t) (unit_to_frame (ix1) * speed);
3373 Region* new_region = createRegion ((*i)->region);
3375 from_playlist->remove_region (&((*i)->region));
3377 sigc::connection c = atv2->view->AudioRegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3378 to_playlist->add_region (*new_region, where);
3381 if (latest_regionview) {
3382 selection->add (latest_regionview);
3388 /* motion within a single track */
3390 for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin(); i != selection->audio_regions.by_layer().end(); ++i) {
3394 if (rv->region.locked()) {
3398 if (regionview_x_movement) {
3399 double ownspeed = 1.0;
3400 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*> (&(rv->get_time_axis_view()));
3402 if (atv && atv->get_diskstream()) {
3403 ownspeed = atv->get_diskstream()->speed();
3406 /* base the new region position on the current position of the regionview.*/
3408 double ix1, ix2, iy1, iy2;
3410 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3411 rv->get_canvas_group()->i2w (ix1, iy1);
3412 where = (jack_nframes_t) (unit_to_frame (ix1) * ownspeed);
3416 where = rv->region.position();
3419 rv->get_time_axis_view().reveal_dependent_views (*rv);
3421 /* no need to add an undo here, we did that when we added this playlist to motion_frozen playlists */
3423 rv->region.set_position (where, (void *) this);
3428 for (set<Playlist*>::iterator p = motion_frozen_playlists.begin(); p != motion_frozen_playlists.end(); ++p) {
3430 session->add_redo_no_execute ((*p)->get_memento());
3433 motion_frozen_playlists.clear ();
3436 commit_reversible_command ();
3441 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
3443 /* Either add to or set the set the region selection, unless
3444 this is an alignment click (control used)
3447 if (Keyboard::modifier_state_contains (event->state, Keyboard::Control)) {
3448 TimeAxisView* tv = &rv.get_time_axis_view();
3449 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*>(tv);
3451 if (atv && atv->is_audio_track()) {
3452 speed = atv->get_diskstream()->speed();
3455 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Alt))) {
3457 align_region (rv.region, SyncPoint, (jack_nframes_t) (edit_cursor->current_frame * speed));
3459 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Shift))) {
3461 align_region (rv.region, End, (jack_nframes_t) (edit_cursor->current_frame * speed));
3465 align_region (rv.region, Start, (jack_nframes_t) (edit_cursor->current_frame * speed));
3471 Editor::show_verbose_time_cursor (jack_nframes_t frame, double offset, double xpos, double ypos)
3482 switch (ARDOUR_UI::instance()->secondary_clock.mode ()) {
3483 case AudioClock::BBT:
3484 session->bbt_time (frame, bbt);
3485 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, bbt.bars, bbt.beats, bbt.ticks);
3488 case AudioClock::SMPTE:
3489 session->smpte_time (frame, smpte);
3490 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
3493 case AudioClock::MinSec:
3494 /* XXX fix this to compute min/sec properly */
3495 session->smpte_time (frame, smpte);
3496 secs = smpte.seconds + ((float) smpte.frames / session->smpte_frames_per_second);
3497 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", smpte.hours, smpte.minutes, secs);
3501 snprintf (buf, sizeof(buf), "%u", frame);
3505 if (xpos >= 0 && ypos >=0) {
3506 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
3509 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
3511 show_verbose_canvas_cursor ();
3515 Editor::show_verbose_duration_cursor (jack_nframes_t start, jack_nframes_t end, double offset, double xpos, double ypos)
3522 Meter meter_at_start(session->tempo_map().meter_at(start));
3528 switch (ARDOUR_UI::instance()->secondary_clock.mode ()) {
3529 case AudioClock::BBT:
3530 session->bbt_time (start, sbbt);
3531 session->bbt_time (end, ebbt);
3534 /* XXX this computation won't work well if the
3535 user makes a selection that spans any meter changes.
3538 ebbt.bars -= sbbt.bars;
3539 if (ebbt.beats >= sbbt.beats) {
3540 ebbt.beats -= sbbt.beats;
3543 ebbt.beats = int(meter_at_start.beats_per_bar()) + ebbt.beats - sbbt.beats;
3545 if (ebbt.ticks >= sbbt.ticks) {
3546 ebbt.ticks -= sbbt.ticks;
3549 ebbt.ticks = int(Meter::ticks_per_beat) + ebbt.ticks - sbbt.ticks;
3552 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, ebbt.bars, ebbt.beats, ebbt.ticks);
3555 case AudioClock::SMPTE:
3556 session->smpte_duration (end - start, smpte);
3557 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
3560 case AudioClock::MinSec:
3561 /* XXX fix this to compute min/sec properly */
3562 session->smpte_duration (end - start, smpte);
3563 secs = smpte.seconds + ((float) smpte.frames / session->smpte_frames_per_second);
3564 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", smpte.hours, smpte.minutes, secs);
3568 snprintf (buf, sizeof(buf), "%u", end - start);
3572 if (xpos >= 0 && ypos >=0) {
3573 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
3576 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
3578 show_verbose_canvas_cursor ();
3582 Editor::collect_new_region_view (AudioRegionView* rv)
3584 latest_regionview = rv;
3588 Editor::start_selection_grab (ArdourCanvas::Item* item, GdkEvent* event)
3590 if (clicked_regionview == 0) {
3594 /* lets try to create new Region for the selection */
3596 vector<AudioRegion*> new_regions;
3597 create_region_from_selection (new_regions);
3599 if (new_regions.empty()) {
3603 /* XXX fix me one day to use all new regions */
3605 Region* region = new_regions.front();
3607 /* add it to the current stream/playlist.
3609 tricky: the streamview for the track will add a new regionview. we will
3610 catch the signal it sends when it creates the regionview to
3611 set the regionview we want to then drag.
3614 latest_regionview = 0;
3615 sigc::connection c = clicked_audio_trackview->view->AudioRegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3617 /* A selection grab currently creates two undo/redo operations, one for
3618 creating the new region and another for moving it.
3621 begin_reversible_command (_("selection grab"));
3623 Playlist* playlist = clicked_trackview->playlist();
3625 session->add_undo (playlist->get_memento ());
3626 clicked_trackview->playlist()->add_region (*region, selection->time[clicked_selection].start);
3627 session->add_redo_no_execute (playlist->get_memento ());
3629 commit_reversible_command ();
3633 if (latest_regionview == 0) {
3634 /* something went wrong */
3638 /* we need to deselect all other regionviews, and select this one
3639 i'm ignoring undo stuff, because the region creation will take care of it */
3640 selection->set (latest_regionview);
3642 drag_info.item = latest_regionview->get_canvas_group();
3643 drag_info.data = latest_regionview;
3644 drag_info.motion_callback = &Editor::region_drag_motion_callback;
3645 drag_info.finished_callback = &Editor::region_drag_finished_callback;
3649 drag_info.last_trackview = clicked_trackview;
3650 drag_info.last_frame_position = latest_regionview->region.position();
3651 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
3653 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3657 Editor::cancel_selection ()
3659 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
3660 (*i)->hide_selection ();
3662 begin_reversible_command (_("cancel selection"));
3663 selection->clear ();
3664 clicked_selection = 0;
3665 commit_reversible_command ();
3669 Editor::start_selection_op (ArdourCanvas::Item* item, GdkEvent* event, SelectionOp op)
3671 jack_nframes_t start = 0;
3672 jack_nframes_t end = 0;
3678 drag_info.item = item;
3679 drag_info.motion_callback = &Editor::drag_selection;
3680 drag_info.finished_callback = &Editor::end_selection_op;
3685 case CreateSelection:
3686 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
3687 drag_info.copy = true;
3689 drag_info.copy = false;
3691 start_grab (event, selector_cursor);
3694 case SelectionStartTrim:
3695 if (clicked_trackview) {
3696 clicked_trackview->order_selection_trims (item, true);
3698 start_grab (event, trimmer_cursor);
3699 start = selection->time[clicked_selection].start;
3700 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
3703 case SelectionEndTrim:
3704 if (clicked_trackview) {
3705 clicked_trackview->order_selection_trims (item, false);
3707 start_grab (event, trimmer_cursor);
3708 end = selection->time[clicked_selection].end;
3709 drag_info.pointer_frame_offset = drag_info.grab_frame - end;
3713 start = selection->time[clicked_selection].start;
3715 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
3719 if (selection_op == SelectionMove) {
3720 show_verbose_time_cursor(start, 10);
3722 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
3727 Editor::drag_selection (ArdourCanvas::Item* item, GdkEvent* event)
3729 jack_nframes_t start = 0;
3730 jack_nframes_t end = 0;
3731 jack_nframes_t length;
3732 jack_nframes_t pending_position;
3734 if ((int32_t) drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
3735 pending_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
3738 pending_position = 0;
3741 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3742 snap_to (pending_position);
3745 /* only alter selection if the current frame is
3746 different from the last frame position (adjusted)
3749 if (pending_position == drag_info.last_pointer_frame) return;
3751 switch (selection_op) {
3752 case CreateSelection:
3754 if (drag_info.first_move) {
3755 snap_to (drag_info.grab_frame);
3758 if (pending_position < drag_info.grab_frame) {
3759 start = pending_position;
3760 end = drag_info.grab_frame;
3762 end = pending_position;
3763 start = drag_info.grab_frame;
3766 /* first drag: Either add to the selection
3767 or create a new selection->
3770 if (drag_info.first_move) {
3772 begin_reversible_command (_("range selection"));
3774 if (drag_info.copy) {
3775 /* adding to the selection */
3776 clicked_selection = selection->add (start, end);
3777 drag_info.copy = false;
3779 /* new selection-> */
3780 clicked_selection = selection->set (clicked_trackview, start, end);
3785 case SelectionStartTrim:
3787 if (drag_info.first_move) {
3788 begin_reversible_command (_("trim selection start"));
3791 start = selection->time[clicked_selection].start;
3792 end = selection->time[clicked_selection].end;
3794 if (pending_position > end) {
3797 start = pending_position;
3801 case SelectionEndTrim:
3803 if (drag_info.first_move) {
3804 begin_reversible_command (_("trim selection end"));
3807 start = selection->time[clicked_selection].start;
3808 end = selection->time[clicked_selection].end;
3810 if (pending_position < start) {
3813 end = pending_position;
3820 if (drag_info.first_move) {
3821 begin_reversible_command (_("move selection"));
3824 start = selection->time[clicked_selection].start;
3825 end = selection->time[clicked_selection].end;
3827 length = end - start;
3829 start = pending_position;
3832 end = start + length;
3837 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
3838 start_canvas_autoscroll (1);
3842 selection->replace (clicked_selection, start, end);
3845 drag_info.last_pointer_frame = pending_position;
3846 drag_info.first_move = false;
3848 if (selection_op == SelectionMove) {
3849 show_verbose_time_cursor(start, 10);
3851 show_verbose_time_cursor(pending_position, 10);
3856 Editor::end_selection_op (ArdourCanvas::Item* item, GdkEvent* event)
3858 if (!drag_info.first_move) {
3859 drag_selection (item, event);
3860 /* XXX this is not object-oriented programming at all. ick */
3861 if (selection->time.consolidate()) {
3862 selection->TimeChanged ();
3864 commit_reversible_command ();
3866 /* just a click, no pointer movement.*/
3868 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
3870 selection->clear_time();
3875 /* XXX what happens if its a music selection? */
3876 session->set_audio_range (selection->time);
3877 stop_canvas_autoscroll ();
3881 Editor::start_trim (ArdourCanvas::Item* item, GdkEvent* event)
3884 TimeAxisView* tvp = clicked_trackview;
3885 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
3887 if (tv && tv->is_audio_track()) {
3888 speed = tv->get_diskstream()->speed();
3891 jack_nframes_t region_start = (jack_nframes_t) (clicked_regionview->region.position() / speed);
3892 jack_nframes_t region_end = (jack_nframes_t) (clicked_regionview->region.last_frame() / speed);
3893 jack_nframes_t region_length = (jack_nframes_t) (clicked_regionview->region.length() / speed);
3895 motion_frozen_playlists.clear();
3897 //drag_info.item = clicked_regionview->get_name_highlight();
3898 drag_info.item = item;
3899 drag_info.motion_callback = &Editor::trim_motion_callback;
3900 drag_info.finished_callback = &Editor::trim_finished_callback;
3902 start_grab (event, trimmer_cursor);
3904 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
3905 trim_op = ContentsTrim;
3907 /* These will get overridden for a point trim.*/
3908 if (drag_info.current_pointer_frame < (region_start + region_length/2)) {
3909 /* closer to start */
3910 trim_op = StartTrim;
3911 } else if (drag_info.current_pointer_frame > (region_end - region_length/2)) {
3919 show_verbose_time_cursor(region_start, 10);
3922 show_verbose_time_cursor(region_end, 10);
3925 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
3931 Editor::trim_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
3933 AudioRegionView* rv = clicked_regionview;
3934 jack_nframes_t frame_delta = 0;
3935 bool left_direction;
3936 bool obey_snap = !Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier());
3938 /* snap modifier works differently here..
3939 its' current state has to be passed to the
3940 various trim functions in order to work properly
3944 TimeAxisView* tvp = clicked_trackview;
3945 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
3946 pair<set<Playlist*>::iterator,bool> insert_result;
3948 if (tv && tv->is_audio_track()) {
3949 speed = tv->get_diskstream()->speed();
3952 if (drag_info.last_pointer_frame > drag_info.current_pointer_frame) {
3953 left_direction = true;
3955 left_direction = false;
3959 snap_to (drag_info.current_pointer_frame);
3962 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
3966 if (drag_info.first_move) {
3972 trim_type = "Region start trim";
3975 trim_type = "Region end trim";
3978 trim_type = "Region content trim";
3982 begin_reversible_command (trim_type);
3984 for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin(); i != selection->audio_regions.by_layer().end(); ++i) {
3985 (*i)->region.freeze ();
3986 (*i)->temporarily_hide_envelope ();
3988 Playlist * pl = (*i)->region.playlist();
3989 insert_result = motion_frozen_playlists.insert (pl);
3990 if (insert_result.second) {
3991 session->add_undo (pl->get_memento());
3996 if (left_direction) {
3997 frame_delta = (drag_info.last_pointer_frame - drag_info.current_pointer_frame);
3999 frame_delta = (drag_info.current_pointer_frame - drag_info.last_pointer_frame);
4004 if ((left_direction == false) && (drag_info.current_pointer_frame <= rv->region.first_frame()/speed)) {
4007 for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin(); i != selection->audio_regions.by_layer().end(); ++i) {
4008 single_start_trim (**i, frame_delta, left_direction, obey_snap);
4014 if ((left_direction == true) && (drag_info.current_pointer_frame > (jack_nframes_t) (rv->region.last_frame()/speed))) {
4017 for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin(); i != selection->audio_regions.by_layer().end(); ++i) {
4018 single_end_trim (**i, frame_delta, left_direction, obey_snap);
4025 bool swap_direction = false;
4027 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
4028 swap_direction = true;
4031 for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin();
4032 i != selection->audio_regions.by_layer().end(); ++i)
4034 single_contents_trim (**i, frame_delta, left_direction, swap_direction, obey_snap);
4042 show_verbose_time_cursor((jack_nframes_t) (rv->region.position()/speed), 10);
4045 show_verbose_time_cursor((jack_nframes_t) (rv->region.last_frame()/speed), 10);
4048 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4052 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4053 drag_info.first_move = false;
4057 Editor::single_contents_trim (AudioRegionView& rv, jack_nframes_t frame_delta, bool left_direction, bool swap_direction, bool obey_snap)
4059 Region& region (rv.region);
4061 if (region.locked()) {
4065 jack_nframes_t new_bound;
4068 TimeAxisView* tvp = clicked_trackview;
4069 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
4071 if (tv && tv->is_audio_track()) {
4072 speed = tv->get_diskstream()->speed();
4075 if (left_direction) {
4076 if (swap_direction) {
4077 new_bound = (jack_nframes_t) (region.position()/speed) + frame_delta;
4079 new_bound = (jack_nframes_t) (region.position()/speed) - frame_delta;
4082 if (swap_direction) {
4083 new_bound = (jack_nframes_t) (region.position()/speed) - frame_delta;
4085 new_bound = (jack_nframes_t) (region.position()/speed) + frame_delta;
4090 snap_to (new_bound);
4092 region.trim_start ((jack_nframes_t) (new_bound * speed), this);
4093 rv.region_changed (StartChanged);
4097 Editor::single_start_trim (AudioRegionView& rv, jack_nframes_t frame_delta, bool left_direction, bool obey_snap)
4099 Region& region (rv.region);
4101 if (region.locked()) {
4105 jack_nframes_t new_bound;
4108 TimeAxisView* tvp = clicked_trackview;
4109 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
4111 if (tv && tv->is_audio_track()) {
4112 speed = tv->get_diskstream()->speed();
4115 if (left_direction) {
4116 new_bound = (jack_nframes_t) (region.position()/speed) - frame_delta;
4118 new_bound = (jack_nframes_t) (region.position()/speed) + frame_delta;
4122 snap_to (new_bound, (left_direction ? 0 : 1));
4125 region.trim_front ((jack_nframes_t) (new_bound * speed), this);
4127 rv.region_changed (Change (LengthChanged|PositionChanged|StartChanged));
4131 Editor::single_end_trim (AudioRegionView& rv, jack_nframes_t frame_delta, bool left_direction, bool obey_snap)
4133 Region& region (rv.region);
4135 if (region.locked()) {
4139 jack_nframes_t new_bound;
4142 TimeAxisView* tvp = clicked_trackview;
4143 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
4145 if (tv && tv->is_audio_track()) {
4146 speed = tv->get_diskstream()->speed();
4149 if (left_direction) {
4150 new_bound = (jack_nframes_t) ((region.last_frame() + 1)/speed) - frame_delta;
4152 new_bound = (jack_nframes_t) ((region.last_frame() + 1)/speed) + frame_delta;
4156 snap_to (new_bound);
4158 region.trim_end ((jack_nframes_t) (new_bound * speed), this);
4159 rv.region_changed (LengthChanged);
4163 Editor::trim_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
4165 if (!drag_info.first_move) {
4166 trim_motion_callback (item, event);
4168 if (!clicked_regionview->get_selected()) {
4169 thaw_region_after_trim (*clicked_regionview);
4172 for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin();
4173 i != selection->audio_regions.by_layer().end(); ++i)
4175 thaw_region_after_trim (**i);
4179 for (set<Playlist*>::iterator p = motion_frozen_playlists.begin(); p != motion_frozen_playlists.end(); ++p) {
4181 session->add_redo_no_execute ((*p)->get_memento());
4184 motion_frozen_playlists.clear ();
4186 commit_reversible_command();
4188 /* no mouse movement */
4194 Editor::point_trim (GdkEvent* event)
4196 AudioRegionView* rv = clicked_regionview;
4197 jack_nframes_t new_bound = drag_info.current_pointer_frame;
4199 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4200 snap_to (new_bound);
4203 /* Choose action dependant on which button was pressed */
4204 switch (event->button.button) {
4206 trim_op = StartTrim;
4207 begin_reversible_command (_("Start point trim"));
4209 if (rv->get_selected()) {
4211 for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin();
4212 i != selection->audio_regions.by_layer().end(); ++i)
4214 if (!(*i)->region.locked()) {
4215 session->add_undo ((*i)->region.playlist()->get_memento());
4216 (*i)->region.trim_front (new_bound, this);
4217 session->add_redo_no_execute ((*i)->region.playlist()->get_memento());
4223 if (!rv->region.locked()) {
4224 session->add_undo (rv->region.playlist()->get_memento());
4225 rv->region.trim_front (new_bound, this);
4226 session->add_redo_no_execute (rv->region.playlist()->get_memento());
4230 commit_reversible_command();
4235 begin_reversible_command (_("End point trim"));
4237 if (rv->get_selected()) {
4239 for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin(); i != selection->audio_regions.by_layer().end(); ++i)
4241 if (!(*i)->region.locked()) {
4242 session->add_undo ((*i)->region.playlist()->get_memento());
4243 (*i)->region.trim_end (new_bound, this);
4244 session->add_redo_no_execute ((*i)->region.playlist()->get_memento());
4250 if (!rv->region.locked()) {
4251 session->add_undo (rv->region.playlist()->get_memento());
4252 rv->region.trim_end (new_bound, this);
4253 session->add_redo_no_execute (rv->region.playlist()->get_memento());
4257 commit_reversible_command();
4266 Editor::thaw_region_after_trim (AudioRegionView& rv)
4268 Region& region (rv.region);
4270 if (region.locked()) {
4274 region.thaw (_("trimmed region"));
4275 session->add_redo_no_execute (region.playlist()->get_memento());
4277 rv.unhide_envelope ();
4281 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* event)
4286 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
4287 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
4291 Location* location = find_location_from_marker (marker, is_start);
4292 location->set_hidden (true, this);
4297 Editor::start_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event, RangeMarkerOp op)
4303 drag_info.item = item;
4304 drag_info.motion_callback = &Editor::drag_range_markerbar_op;
4305 drag_info.finished_callback = &Editor::end_range_markerbar_op;
4307 range_marker_op = op;
4309 if (!temp_location) {
4310 temp_location = new Location;
4314 case CreateRangeMarker:
4315 case CreateTransportMarker:
4317 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
4318 drag_info.copy = true;
4320 drag_info.copy = false;
4322 start_grab (event, selector_cursor);
4326 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4331 Editor::drag_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4333 jack_nframes_t start = 0;
4334 jack_nframes_t end = 0;
4335 ArdourCanvas::SimpleRect *crect = (range_marker_op == CreateRangeMarker) ? range_bar_drag_rect: transport_bar_drag_rect;
4337 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4338 snap_to (drag_info.current_pointer_frame);
4341 /* only alter selection if the current frame is
4342 different from the last frame position.
4345 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4347 switch (range_marker_op) {
4348 case CreateRangeMarker:
4349 case CreateTransportMarker:
4350 if (drag_info.first_move) {
4351 snap_to (drag_info.grab_frame);
4354 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4355 start = drag_info.current_pointer_frame;
4356 end = drag_info.grab_frame;
4358 end = drag_info.current_pointer_frame;
4359 start = drag_info.grab_frame;
4362 /* first drag: Either add to the selection
4363 or create a new selection.
4366 if (drag_info.first_move) {
4368 temp_location->set (start, end);
4372 update_marker_drag_item (temp_location);
4373 range_marker_drag_rect->show();
4374 range_marker_drag_rect->raise_to_top();
4380 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
4381 start_canvas_autoscroll (1);
4385 temp_location->set (start, end);
4387 double x1 = frame_to_pixel (start);
4388 double x2 = frame_to_pixel (end);
4389 crect->property_x1() = x1;
4390 crect->property_x2() = x2;
4392 update_marker_drag_item (temp_location);
4395 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4396 drag_info.first_move = false;
4398 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4403 Editor::end_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4405 Location * newloc = 0;
4407 if (!drag_info.first_move) {
4408 drag_range_markerbar_op (item, event);
4410 switch (range_marker_op) {
4411 case CreateRangeMarker:
4412 begin_reversible_command (_("new range marker"));
4413 session->add_undo (session->locations()->get_memento());
4414 newloc = new Location(temp_location->start(), temp_location->end(), "unnamed", Location::IsRangeMarker);
4415 session->locations()->add (newloc, true);
4416 session->add_redo_no_execute (session->locations()->get_memento());
4417 commit_reversible_command ();
4419 range_bar_drag_rect->hide();
4420 range_marker_drag_rect->hide();
4423 case CreateTransportMarker:
4424 // popup menu to pick loop or punch
4425 new_transport_marker_context_menu (&event->button, item);
4430 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
4432 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
4434 jack_nframes_t start;
4437 start = session->locations()->first_mark_before (drag_info.grab_frame);
4438 end = session->locations()->first_mark_after (drag_info.grab_frame);
4440 if (end == max_frames) {
4441 end = session->current_end_frame ();
4445 start = session->current_start_frame ();
4448 switch (mouse_mode) {
4450 /* find the two markers on either side and then make the selection from it */
4451 cerr << "select between " << start << " .. " << end << endl;
4452 select_all_within (start, end, 0.0f, FLT_MAX, Selection::Set);
4456 /* find the two markers on either side of the click and make the range out of it */
4457 selection->set (0, start, end);
4466 stop_canvas_autoscroll ();
4472 Editor::start_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4474 drag_info.item = item;
4475 drag_info.motion_callback = &Editor::drag_mouse_zoom;
4476 drag_info.finished_callback = &Editor::end_mouse_zoom;
4478 start_grab (event, zoom_cursor);
4480 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4484 Editor::drag_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4486 jack_nframes_t start;
4489 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4490 snap_to (drag_info.current_pointer_frame);
4492 if (drag_info.first_move) {
4493 snap_to (drag_info.grab_frame);
4497 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4499 /* base start and end on initial click position */
4500 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4501 start = drag_info.current_pointer_frame;
4502 end = drag_info.grab_frame;
4504 end = drag_info.current_pointer_frame;
4505 start = drag_info.grab_frame;
4510 if (drag_info.first_move) {
4512 zoom_rect->raise_to_top();
4515 reposition_zoom_rect(start, end);
4517 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4518 drag_info.first_move = false;
4520 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4525 Editor::end_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4527 if (!drag_info.first_move) {
4528 drag_mouse_zoom (item, event);
4530 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
4531 temporal_zoom_by_frame (drag_info.grab_frame, drag_info.last_pointer_frame, "mouse zoom");
4533 temporal_zoom_by_frame (drag_info.last_pointer_frame, drag_info.grab_frame, "mouse zoom");
4536 temporal_zoom_to_frame (false, drag_info.grab_frame);
4538 temporal_zoom_step (false);
4539 center_screen (drag_info.grab_frame);
4547 Editor::reposition_zoom_rect (jack_nframes_t start, jack_nframes_t end)
4549 double x1 = frame_to_pixel (start);
4550 double x2 = frame_to_pixel (end);
4551 double y2 = canvas_height - 2;
4553 zoom_rect->property_x1() = x1;
4554 zoom_rect->property_y1() = 1.0;
4555 zoom_rect->property_x2() = x2;
4556 zoom_rect->property_y2() = y2;
4560 Editor::start_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4562 drag_info.item = item;
4563 drag_info.motion_callback = &Editor::drag_rubberband_select;
4564 drag_info.finished_callback = &Editor::end_rubberband_select;
4566 start_grab (event, cross_hair_cursor);
4568 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4572 Editor::drag_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4574 jack_nframes_t start;
4579 /* use a bigger drag threshold than the default */
4581 if (abs ((int) (drag_info.current_pointer_frame - drag_info.grab_frame)) < 8) {
4585 // if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4586 // snap_to (drag_info.current_pointer_frame);
4588 // if (drag_info.first_move) {
4589 // snap_to (drag_info.grab_frame);
4594 /* base start and end on initial click position */
4595 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4596 start = drag_info.current_pointer_frame;
4597 end = drag_info.grab_frame;
4599 end = drag_info.current_pointer_frame;
4600 start = drag_info.grab_frame;
4603 if (drag_info.current_pointer_y < drag_info.grab_y) {
4604 y1 = drag_info.current_pointer_y;
4605 y2 = drag_info.grab_y;
4608 y2 = drag_info.current_pointer_y;
4609 y1 = drag_info.grab_y;
4613 if (start != end || y1 != y2) {
4615 double x1 = frame_to_pixel (start);
4616 double x2 = frame_to_pixel (end);
4618 rubberband_rect->property_x1() = x1;
4619 rubberband_rect->property_y1() = y1;
4620 rubberband_rect->property_x2() = x2;
4621 rubberband_rect->property_y2() = y2;
4623 rubberband_rect->show();
4624 rubberband_rect->raise_to_top();
4626 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4627 drag_info.first_move = false;
4629 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4634 Editor::end_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4636 if (!drag_info.first_move) {
4638 drag_rubberband_select (item, event);
4641 if (drag_info.current_pointer_y < drag_info.grab_y) {
4642 y1 = drag_info.current_pointer_y;
4643 y2 = drag_info.grab_y;
4646 y2 = drag_info.current_pointer_y;
4647 y1 = drag_info.grab_y;
4651 Selection::Operation op = Keyboard::selection_type (event->button.state);
4654 begin_reversible_command (_("select regions"));
4656 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
4657 commit = select_all_within (drag_info.grab_frame, drag_info.last_pointer_frame, y1, y2, op);
4659 commit = select_all_within (drag_info.last_pointer_frame, drag_info.grab_frame, y1, y2, op);
4663 commit_reversible_command ();
4667 selection->clear_audio_regions();
4668 selection->clear_points ();
4669 selection->clear_lines ();
4672 rubberband_rect->hide();
4677 Editor::mouse_rename_region (ArdourCanvas::Item* item, GdkEvent* event)
4679 using namespace Gtkmm2ext;
4681 ArdourPrompter prompter (false);
4683 prompter.set_prompt (_("Name for region:"));
4684 prompter.set_initial_text (clicked_regionview->region.name());
4685 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
4686 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
4687 prompter.show_all ();
4688 switch (prompter.run ()) {
4689 case Gtk::RESPONSE_ACCEPT:
4691 prompter.get_result(str);
4693 clicked_regionview->region.set_name (str);
4701 Editor::start_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
4703 drag_info.item = item;
4704 drag_info.motion_callback = &Editor::time_fx_motion;
4705 drag_info.finished_callback = &Editor::end_time_fx;
4709 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4713 Editor::time_fx_motion (ArdourCanvas::Item *item, GdkEvent* event)
4715 AudioRegionView* rv = clicked_regionview;
4717 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4718 snap_to (drag_info.current_pointer_frame);
4721 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
4725 if (drag_info.current_pointer_frame > rv->region.position()) {
4726 rv->get_time_axis_view().show_timestretch (rv->region.position(), drag_info.current_pointer_frame);
4729 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4730 drag_info.first_move = false;
4732 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4736 Editor::end_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
4738 clicked_regionview->get_time_axis_view().hide_timestretch ();
4740 if (drag_info.first_move) {
4744 jack_nframes_t newlen = drag_info.last_pointer_frame - clicked_regionview->region.position();
4745 float percentage = (float) ((double) newlen - (double) clicked_regionview->region.length()) / ((double) newlen) * 100.0f;
4747 begin_reversible_command (_("timestretch"));
4749 if (run_timestretch (selection->audio_regions, percentage) == 0) {
4750 session->commit_reversible_command ();
4755 Editor::mouse_brush_insert_region (AudioRegionView* rv, jack_nframes_t pos)
4757 /* no brushing without a useful snap setting */
4759 switch (snap_mode) {
4761 return; /* can't work because it allows region to be placed anywhere */
4766 switch (snap_type) {
4769 case SnapToEditCursor:
4776 /* don't brush a copy over the original */
4778 if (pos == rv->region.position()) {
4782 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*>(&rv->get_time_axis_view());
4784 if (atv == 0 || !atv->is_audio_track()) {
4788 Playlist* playlist = atv->playlist();
4789 double speed = atv->get_diskstream()->speed();
4791 session->add_undo (playlist->get_memento());
4792 playlist->add_region (*(new AudioRegion (rv->region)), (jack_nframes_t) (pos * speed));
4793 session->add_redo_no_execute (playlist->get_memento());
4795 // playlist is frozen, so we have to update manually
4797 playlist->StateChanged (Change (~0)); /* EMIT SIGNAL */
4801 Editor::track_height_step_timeout ()
4804 struct timeval delta;
4806 gettimeofday (&now, 0);
4807 timersub (&now, &last_track_height_step_timestamp, &delta);
4809 if (delta.tv_sec * 1000000 + delta.tv_usec > 250000) { /* milliseconds */
4810 current_stepping_trackview = 0;