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 XMLNode &before, &after;
1816 before = arv->region.get_state();
1818 arv->region.set_fade_in_length (fade_length);
1820 after = arv->region.get_state();
1821 session->add_command(MementoCommand<ARDOUR::AudioRegion>(arv->region,
1824 commit_reversible_command ();
1825 fade_in_drag_motion_callback (item, event);
1829 Editor::start_fade_out_grab (ArdourCanvas::Item* item, GdkEvent* event)
1831 drag_info.item = item;
1832 drag_info.motion_callback = &Editor::fade_out_drag_motion_callback;
1833 drag_info.finished_callback = &Editor::fade_out_drag_finished_callback;
1837 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
1838 fatal << _("programming error: fade out canvas item has no regionview data pointer!") << endmsg;
1842 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1844 drag_info.pointer_frame_offset = drag_info.grab_frame - (arv->region.length() - (jack_nframes_t) arv->region.fade_out().back()->when + arv->region.position());
1848 Editor::fade_out_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1850 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1852 jack_nframes_t fade_length;
1854 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1855 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1861 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1865 if (pos > (arv->region.last_frame() - 64)) {
1866 fade_length = 64; // this should really be a minimum fade defined somewhere
1868 else if (pos < arv->region.position()) {
1869 fade_length = arv->region.length();
1872 fade_length = arv->region.last_frame() - pos;
1875 arv->reset_fade_out_shape_width (fade_length);
1877 show_verbose_duration_cursor (arv->region.last_frame() - fade_length, arv->region.last_frame(), 10);
1879 drag_info.first_move = false;
1883 Editor::fade_out_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1885 if (drag_info.first_move) return;
1887 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1889 jack_nframes_t fade_length;
1891 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1892 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1898 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1902 if (pos > (arv->region.last_frame() - 64)) {
1903 fade_length = 64; // this should really be a minimum fade defined somewhere
1905 else if (pos < arv->region.position()) {
1906 fade_length = arv->region.length();
1909 fade_length = arv->region.last_frame() - pos;
1912 begin_reversible_command (_("change fade out length"));
1913 XMLNode &before, &after;
1914 before = arv->region.get_state();
1916 arv->region.set_fade_out_length (fade_length);
1918 after = arv->region.get_state();
1919 session->add_command(MementoCommand<ARDOUR::AudioRegion>(arv->region, before, after));
1920 commit_reversible_command ();
1922 fade_out_drag_motion_callback (item, event);
1926 Editor::start_cursor_grab (ArdourCanvas::Item* item, GdkEvent* event)
1928 drag_info.item = item;
1929 drag_info.motion_callback = &Editor::cursor_drag_motion_callback;
1930 drag_info.finished_callback = &Editor::cursor_drag_finished_callback;
1934 if ((drag_info.data = (item->get_data ("cursor"))) == 0) {
1935 fatal << _("programming error: cursor canvas item has no cursor data pointer!") << endmsg;
1939 Cursor* cursor = (Cursor *) drag_info.data;
1941 if (session && cursor == playhead_cursor) {
1942 if (drag_info.was_rolling) {
1943 session->request_stop ();
1947 drag_info.pointer_frame_offset = drag_info.grab_frame - cursor->current_frame;
1949 show_verbose_time_cursor (cursor->current_frame, 10);
1953 Editor::cursor_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1955 Cursor* cursor = (Cursor *) drag_info.data;
1956 jack_nframes_t adjusted_frame;
1958 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1959 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1965 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1966 if (cursor != edit_cursor || snap_type != SnapToEditCursor) {
1967 snap_to (adjusted_frame);
1971 if (adjusted_frame == drag_info.last_pointer_frame) return;
1973 cursor->set_position (adjusted_frame);
1975 if (cursor == edit_cursor) {
1976 edit_cursor_clock.set (cursor->current_frame);
1979 show_verbose_time_cursor (cursor->current_frame, 10);
1981 drag_info.last_pointer_frame = adjusted_frame;
1982 drag_info.first_move = false;
1986 Editor::cursor_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1988 if (drag_info.first_move) return;
1990 cursor_drag_motion_callback (item, event);
1992 if (item == &playhead_cursor->canvas_item) {
1994 session->request_locate (playhead_cursor->current_frame, drag_info.was_rolling);
1996 } else if (item == &edit_cursor->canvas_item) {
1997 edit_cursor->set_position (edit_cursor->current_frame);
1998 edit_cursor_clock.set (edit_cursor->current_frame);
2003 Editor::update_marker_drag_item (Location *location)
2005 double x1 = frame_to_pixel (location->start());
2006 double x2 = frame_to_pixel (location->end());
2008 if (location->is_mark()) {
2009 marker_drag_line_points.front().set_x(x1);
2010 marker_drag_line_points.back().set_x(x1);
2011 marker_drag_line->property_points() = marker_drag_line_points;
2014 range_marker_drag_rect->property_x1() = x1;
2015 range_marker_drag_rect->property_x2() = x2;
2020 Editor::start_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2024 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2025 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2031 Location *location = find_location_from_marker (marker, is_start);
2033 drag_info.item = item;
2034 drag_info.data = marker;
2035 drag_info.motion_callback = &Editor::marker_drag_motion_callback;
2036 drag_info.finished_callback = &Editor::marker_drag_finished_callback;
2040 drag_info.copied_location = new Location (*location);
2041 drag_info.pointer_frame_offset = drag_info.grab_frame - (is_start ? location->start() : location->end());
2043 update_marker_drag_item (location);
2045 if (location->is_mark()) {
2046 marker_drag_line->show();
2047 marker_drag_line->raise_to_top();
2050 range_marker_drag_rect->show();
2051 range_marker_drag_rect->raise_to_top();
2054 if (is_start) show_verbose_time_cursor (location->start(), 10);
2055 else show_verbose_time_cursor (location->end(), 10);
2059 Editor::marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2061 jack_nframes_t f_delta;
2062 Marker* marker = (Marker *) drag_info.data;
2063 Location *real_location;
2064 Location *copy_location;
2066 bool move_both = false;
2069 jack_nframes_t newframe;
2070 if (drag_info.pointer_frame_offset <= (long) drag_info.current_pointer_frame) {
2071 newframe = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2077 jack_nframes_t next = newframe;
2079 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2080 snap_to (newframe, 0, true);
2083 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
2087 /* call this to find out if its the start or end */
2089 real_location = find_location_from_marker (marker, is_start);
2091 /* use the copy that we're "dragging" around */
2093 copy_location = drag_info.copied_location;
2095 f_delta = copy_location->end() - copy_location->start();
2097 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
2101 if (is_start) { // start marker
2104 copy_location->set_start (newframe);
2105 copy_location->set_end (newframe + f_delta);
2106 } else if (newframe < copy_location->end()) {
2107 copy_location->set_start (newframe);
2109 snap_to (next, 1, true);
2110 copy_location->set_end (next);
2111 copy_location->set_start (newframe);
2114 } else { // end marker
2117 copy_location->set_end (newframe);
2118 copy_location->set_start (newframe - f_delta);
2119 } else if (newframe > copy_location->start()) {
2120 copy_location->set_end (newframe);
2122 } else if (newframe > 0) {
2123 snap_to (next, -1, true);
2124 copy_location->set_start (next);
2125 copy_location->set_end (newframe);
2129 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
2130 drag_info.first_move = false;
2132 update_marker_drag_item (copy_location);
2134 LocationMarkers* lm = find_location_markers (real_location);
2135 lm->set_position (copy_location->start(), copy_location->end());
2137 show_verbose_time_cursor (newframe, 10);
2141 Editor::marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2143 if (drag_info.first_move) {
2144 marker_drag_motion_callback (item, event);
2148 Marker* marker = (Marker *) drag_info.data;
2152 begin_reversible_command ( _("move marker") );
2153 XMLNode &before, &after;
2154 before = session->locations()->get_state();
2156 Location * location = find_location_from_marker (marker, is_start);
2159 location->set (drag_info.copied_location->start(), drag_info.copied_location->end());
2162 after = session->locations()->get_state();
2163 session->add_command(MementoCommand<Locations>(session->locations(), before, after));
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 XMLNode &before, &after;
2277 if (drag_info.copy == true) {
2278 begin_reversible_command (_("copy meter mark"));
2279 before = map.get_state();
2280 map.add_meter (marker->meter(), when);
2281 after = map.get_state();
2282 session->add_command(MementoCommand<TempoMap>(map, before, after));
2283 commit_reversible_command ();
2285 // delete the dummy marker we used for visual representation of copying.
2286 // a new visual marker will show up automatically.
2289 begin_reversible_command (_("move meter mark"));
2290 before = map.get_state();
2291 map.move_meter (marker->meter(), when);
2292 after = map.get_state();
2293 session->add_command(MementoCommand<TempoMap>(map, before, after));
2294 commit_reversible_command ();
2299 Editor::start_tempo_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2302 TempoMarker* tempo_marker;
2304 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2305 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2309 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2310 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2314 MetricSection& section (tempo_marker->tempo());
2316 if (!section.movable()) {
2320 drag_info.item = item;
2321 drag_info.data = marker;
2322 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2323 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2327 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2328 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2332 Editor::start_tempo_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2335 TempoMarker* tempo_marker;
2337 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2338 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2342 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2343 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2347 // create a dummy marker for visual representation of moving the copy.
2348 // The actual copying is not done before we reach the finish callback.
2350 snprintf (name, sizeof (name), "%.2f", tempo_marker->tempo().beats_per_minute());
2351 TempoMarker* new_marker = new TempoMarker(*this, *tempo_group, color_map[cTempoMarker], name,
2352 *new TempoSection(tempo_marker->tempo()));
2354 drag_info.item = &new_marker->the_item();
2355 drag_info.copy = true;
2356 drag_info.data = new_marker;
2357 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2358 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2362 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2364 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2368 Editor::tempo_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2370 TempoMarker* marker = (TempoMarker *) drag_info.data;
2371 jack_nframes_t adjusted_frame;
2373 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2374 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2380 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2381 snap_to (adjusted_frame);
2384 if (adjusted_frame == drag_info.last_pointer_frame) return;
2386 /* OK, we've moved far enough to make it worth actually move the thing. */
2388 marker->set_position (adjusted_frame);
2390 show_verbose_time_cursor (adjusted_frame, 10);
2392 drag_info.last_pointer_frame = adjusted_frame;
2393 drag_info.first_move = false;
2397 Editor::tempo_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2399 if (drag_info.first_move) return;
2401 tempo_marker_drag_motion_callback (drag_info.item, event);
2403 TempoMarker* marker = (TempoMarker *) drag_info.data;
2406 TempoMap& map (session->tempo_map());
2407 map.bbt_time (drag_info.last_pointer_frame, when);
2409 XMLNode &before, &after;
2411 if (drag_info.copy == true) {
2412 begin_reversible_command (_("copy tempo mark"));
2413 before = map.get_state();
2414 map.add_tempo (marker->tempo(), when);
2415 after = map.get_state();
2416 session->add_command (MementoCommand<TempoMap>(map, before, after));
2417 commit_reversible_command ();
2419 // delete the dummy marker we used for visual representation of copying.
2420 // a new visual marker will show up automatically.
2423 begin_reversible_command (_("move tempo mark"));
2424 before = map.get_state();
2425 map.move_tempo (marker->tempo(), when);
2426 after = map.get_state();
2427 session->add_command (MementoCommand<TempoMap>(map, before, after));
2428 commit_reversible_command ();
2433 Editor::remove_gain_control_point (ArdourCanvas::Item*item, GdkEvent* event)
2435 ControlPoint* control_point;
2437 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2438 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2442 // We shouldn't remove the first or last gain point
2443 if (control_point->line.is_last_point(*control_point) ||
2444 control_point->line.is_first_point(*control_point)) {
2448 control_point->line.remove_point (*control_point);
2452 Editor::remove_control_point (ArdourCanvas::Item*item, GdkEvent* event)
2454 ControlPoint* control_point;
2456 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2457 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2461 control_point->line.remove_point (*control_point);
2465 Editor::start_control_point_grab (ArdourCanvas::Item* item, GdkEvent* event)
2467 ControlPoint* control_point;
2469 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2470 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2474 drag_info.item = item;
2475 drag_info.data = control_point;
2476 drag_info.motion_callback = &Editor::control_point_drag_motion_callback;
2477 drag_info.finished_callback = &Editor::control_point_drag_finished_callback;
2479 start_grab (event, fader_cursor);
2481 control_point->line.start_drag (control_point, 0);
2483 float fraction = 1.0 - (control_point->get_y() / control_point->line.height());
2484 set_verbose_canvas_cursor (control_point->line.get_verbose_cursor_string (fraction),
2485 drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
2487 show_verbose_canvas_cursor ();
2491 Editor::control_point_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2493 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2495 double cx = drag_info.current_pointer_x;
2496 double cy = drag_info.current_pointer_y;
2498 drag_info.cumulative_x_drag = cx - drag_info.grab_x ;
2499 drag_info.cumulative_y_drag = cy - drag_info.grab_y ;
2501 if (drag_info.x_constrained) {
2502 cx = drag_info.grab_x;
2504 if (drag_info.y_constrained) {
2505 cy = drag_info.grab_y;
2508 cp->line.parent_group().w2i (cx, cy);
2512 cy = min ((double) cp->line.height(), cy);
2514 //translate cx to frames
2515 jack_nframes_t cx_frames = unit_to_frame (cx);
2517 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier()) && !drag_info.x_constrained) {
2518 snap_to (cx_frames);
2521 float fraction = 1.0 - (cy / cp->line.height());
2525 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
2531 cp->line.point_drag (*cp, cx_frames , fraction, push);
2533 set_verbose_canvas_cursor_text (cp->line.get_verbose_cursor_string (fraction));
2535 drag_info.first_move = false;
2539 Editor::control_point_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2541 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2543 if (drag_info.first_move) {
2547 if ((event->type == GDK_BUTTON_RELEASE) && (event->button.button == 1) && Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
2548 reset_point_selection ();
2552 control_point_drag_motion_callback (item, event);
2554 cp->line.end_drag (cp);
2558 Editor::start_line_grab_from_regionview (ArdourCanvas::Item* item, GdkEvent* event)
2560 switch (mouse_mode) {
2562 start_line_grab (clicked_regionview->get_gain_line(), event);
2570 Editor::start_line_grab_from_line (ArdourCanvas::Item* item, GdkEvent* event)
2574 if ((al = reinterpret_cast<AutomationLine*> (item->get_data ("line"))) == 0) {
2575 fatal << _("programming error: line canvas item has no line pointer!") << endmsg;
2579 start_line_grab (al, event);
2583 Editor::start_line_grab (AutomationLine* line, GdkEvent* event)
2587 jack_nframes_t frame_within_region;
2589 /* need to get x coordinate in terms of parent (TimeAxisItemView)
2593 cx = event->button.x;
2594 cy = event->button.y;
2595 line->parent_group().w2i (cx, cy);
2596 frame_within_region = (jack_nframes_t) floor (cx * frames_per_unit);
2598 if (!line->control_points_adjacent (frame_within_region, current_line_drag_info.before,
2599 current_line_drag_info.after)) {
2600 /* no adjacent points */
2604 drag_info.item = &line->grab_item();
2605 drag_info.data = line;
2606 drag_info.motion_callback = &Editor::line_drag_motion_callback;
2607 drag_info.finished_callback = &Editor::line_drag_finished_callback;
2609 start_grab (event, fader_cursor);
2611 double fraction = 1.0 - (cy / line->height());
2613 line->start_drag (0, fraction);
2615 set_verbose_canvas_cursor (line->get_verbose_cursor_string (fraction),
2616 drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
2617 show_verbose_canvas_cursor ();
2621 Editor::line_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2623 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
2624 double cx = drag_info.current_pointer_x;
2625 double cy = drag_info.current_pointer_y;
2627 line->parent_group().w2i (cx, cy);
2630 fraction = 1.0 - (cy / line->height());
2634 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
2640 line->line_drag (current_line_drag_info.before, current_line_drag_info.after, fraction, push);
2642 set_verbose_canvas_cursor_text (line->get_verbose_cursor_string (fraction));
2646 Editor::line_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2648 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
2649 line_drag_motion_callback (item, event);
2654 Editor::start_region_grab (ArdourCanvas::Item* item, GdkEvent* event)
2656 if (selection->audio_regions.empty() || clicked_regionview == 0) {
2660 drag_info.copy = false;
2661 drag_info.item = item;
2662 drag_info.data = clicked_regionview;
2663 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2664 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2669 TimeAxisView* tvp = clicked_trackview;
2670 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
2672 if (tv && tv->is_audio_track()) {
2673 speed = tv->get_diskstream()->speed();
2676 drag_info.last_frame_position = (jack_nframes_t) (clicked_regionview->region.position() / speed);
2677 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2678 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2679 // we want a move threshold
2680 drag_info.want_move_threshold = true;
2682 show_verbose_time_cursor (drag_info.last_frame_position, 10);
2684 begin_reversible_command (_("move region(s)"));
2688 Editor::start_region_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2690 if (selection->audio_regions.empty() || clicked_regionview == 0) {
2694 drag_info.copy = true;
2695 drag_info.item = item;
2696 drag_info.data = clicked_regionview;
2700 TimeAxisView* tv = &clicked_regionview->get_time_axis_view();
2701 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*>(tv);
2704 if (atv && atv->is_audio_track()) {
2705 speed = atv->get_diskstream()->speed();
2708 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2709 drag_info.last_frame_position = (jack_nframes_t) (clicked_regionview->region.position() / speed);
2710 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2711 // we want a move threshold
2712 drag_info.want_move_threshold = true;
2713 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2714 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2718 Editor::start_region_brush_grab (ArdourCanvas::Item* item, GdkEvent* event)
2720 if (selection->audio_regions.empty() || clicked_regionview == 0) {
2724 drag_info.copy = false;
2725 drag_info.item = item;
2726 drag_info.data = clicked_regionview;
2727 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2728 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2733 TimeAxisView* tvp = clicked_trackview;
2734 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
2736 if (tv && tv->is_audio_track()) {
2737 speed = tv->get_diskstream()->speed();
2740 drag_info.last_frame_position = (jack_nframes_t) (clicked_regionview->region.position() / speed);
2741 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2742 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2743 // we want a move threshold
2744 drag_info.want_move_threshold = true;
2745 drag_info.brushing = true;
2747 begin_reversible_command (_("Drag region brush"));
2751 Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2755 AudioRegionView *rv = reinterpret_cast<AudioRegionView*> (drag_info.data);
2756 jack_nframes_t pending_region_position = 0;
2757 int32_t pointer_y_span = 0, canvas_pointer_y_span = 0, original_pointer_order;
2758 int32_t visible_y_high = 0, visible_y_low = 512; //high meaning higher numbered.. not the height on the screen
2759 bool clamp_y_axis = false;
2760 vector<int32_t> height_list(512) ;
2761 vector<int32_t>::iterator j;
2763 show_verbose_time_cursor (drag_info.last_frame_position, 10);
2765 if (drag_info.copy && drag_info.move_threshold_passed && drag_info.want_move_threshold) {
2767 drag_info.want_move_threshold = false; // don't copy again
2769 /* this is committed in the grab finished callback. */
2771 begin_reversible_command (_("Drag region copy"));
2773 /* duplicate the region(s) */
2775 vector<AudioRegionView*> new_regionviews;
2777 set<Playlist*> affected_playlists;
2778 pair<set<Playlist*>::iterator,bool> insert_result;
2780 for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin(); i != selection->audio_regions.by_layer().end(); ++i) {
2781 AudioRegionView* rv;
2785 Playlist* to_playlist = rv->region.playlist();
2786 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*>(&rv->get_time_axis_view());
2788 insert_result = affected_playlists.insert (to_playlist);
2789 if (insert_result.second) {
2790 session->add_command (MementoUndoCommand<Playlist>(*to_playlist, to_playlist->get_state()));
2793 latest_regionview = 0;
2795 sigc::connection c = atv->view->AudioRegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
2797 /* create a new region with the same name.
2800 AudioRegion* newregion = new AudioRegion (rv->region);
2802 /* if the original region was locked, we don't care */
2804 newregion->set_locked (false);
2806 to_playlist->add_region (*newregion, (jack_nframes_t) (rv->region.position() * atv->get_diskstream()->speed()));
2810 if (latest_regionview) {
2811 new_regionviews.push_back (latest_regionview);
2815 if (new_regionviews.empty()) {
2819 /* reset selection to new regionviews */
2821 selection->set (new_regionviews);
2823 /* reset drag_info data to reflect the fact that we are dragging the copies */
2825 drag_info.data = new_regionviews.front();
2826 swap_grab (new_regionviews.front()->get_canvas_group (), 0, event->motion.time);
2829 /* Which trackview is this ? */
2831 TimeAxisView* tvp = trackview_by_y_position (drag_info.current_pointer_y);
2832 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
2834 /* The region motion is only processed if the pointer is over
2838 if (!tv || !tv->is_audio_track()) {
2839 /* To make sure we hide the verbose canvas cursor when the mouse is
2840 not held over and audiotrack.
2842 hide_verbose_canvas_cursor ();
2846 original_pointer_order = drag_info.last_trackview->order;
2848 /************************************************************
2850 ************************************************************/
2852 if (drag_info.brushing) {
2853 clamp_y_axis = true;
2858 if ((pointer_y_span = (drag_info.last_trackview->order - tv->order)) != 0) {
2860 int32_t children = 0, numtracks = 0;
2861 // XXX hard coding track limit, oh my, so very very bad
2862 bitset <1024> tracks (0x00);
2863 /* get a bitmask representing the visible tracks */
2865 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2866 TimeAxisView *tracklist_timeview;
2867 tracklist_timeview = (*i);
2868 AudioTimeAxisView* atv2 = dynamic_cast<AudioTimeAxisView*>(tracklist_timeview);
2869 list<TimeAxisView*> children_list;
2871 /* zeroes are audio tracks. ones are other types. */
2873 if (!atv2->hidden()) {
2875 if (visible_y_high < atv2->order) {
2876 visible_y_high = atv2->order;
2878 if (visible_y_low > atv2->order) {
2879 visible_y_low = atv2->order;
2882 if (!atv2->is_audio_track()) {
2883 tracks = tracks |= (0x01 << atv2->order);
2886 height_list[atv2->order] = (*i)->height;
2888 if ((children_list = atv2->get_child_list()).size() > 0) {
2889 for (list<TimeAxisView*>::iterator j = children_list.begin(); j != children_list.end(); ++j) {
2890 tracks = tracks |= (0x01 << (atv2->order + children));
2891 height_list[atv2->order + children] = (*j)->height;
2899 /* find the actual span according to the canvas */
2901 canvas_pointer_y_span = pointer_y_span;
2902 if (drag_info.last_trackview->order >= tv->order) {
2904 for (y = tv->order; y < drag_info.last_trackview->order; y++) {
2905 if (height_list[y] == 0 ) {
2906 canvas_pointer_y_span--;
2911 for (y = drag_info.last_trackview->order;y <= tv->order; y++) {
2912 if ( height_list[y] == 0 ) {
2913 canvas_pointer_y_span++;
2918 for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin(); i != selection->audio_regions.by_layer().end(); ++i) {
2919 AudioRegionView* rv2;
2921 double ix1, ix2, iy1, iy2;
2924 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
2925 rv2->get_canvas_group()->i2w (ix1, iy1);
2926 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
2927 AudioTimeAxisView* atv2 = dynamic_cast<AudioTimeAxisView*>(tvp2);
2929 if (atv2->order != original_pointer_order) {
2930 /* this isn't the pointer track */
2932 if (canvas_pointer_y_span > 0) {
2934 /* moving up the canvas */
2935 if ((atv2->order - canvas_pointer_y_span) >= visible_y_low) {
2937 int32_t visible_tracks = 0;
2938 while (visible_tracks < canvas_pointer_y_span ) {
2941 while (height_list[atv2->order - (visible_tracks - n)] == 0) {
2942 /* we're passing through a hidden track */
2947 if (tracks[atv2->order - (canvas_pointer_y_span - n)] != 0x00) {
2948 clamp_y_axis = true;
2952 clamp_y_axis = true;
2955 } else if (canvas_pointer_y_span < 0) {
2957 /*moving down the canvas*/
2959 if ((atv2->order - (canvas_pointer_y_span - n)) <= visible_y_high) { // we will overflow
2962 int32_t visible_tracks = 0;
2964 while (visible_tracks > canvas_pointer_y_span ) {
2967 while (height_list[atv2->order - (visible_tracks - n)] == 0) {
2971 if ( tracks[atv2->order - ( canvas_pointer_y_span - n)] != 0x00) {
2972 clamp_y_axis = true;
2977 clamp_y_axis = true;
2983 /* this is the pointer's track */
2984 if ((atv2->order - pointer_y_span) > visible_y_high) { // we will overflow
2985 clamp_y_axis = true;
2986 } else if ((atv2->order - pointer_y_span) < visible_y_low) { // we will underflow
2987 clamp_y_axis = true;
2995 } else if (drag_info.last_trackview == tv) {
2996 clamp_y_axis = true;
3000 if (!clamp_y_axis) {
3001 drag_info.last_trackview = tv;
3004 /************************************************************
3006 ************************************************************/
3008 /* compute the amount of pointer motion in frames, and where
3009 the region would be if we moved it by that much.
3012 if (drag_info.move_threshold_passed) {
3014 if ((int32_t)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
3016 jack_nframes_t sync_frame;
3017 jack_nframes_t sync_offset;
3020 pending_region_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
3022 sync_offset = rv->region.sync_offset (sync_dir);
3023 sync_frame = rv->region.adjust_to_sync (pending_region_position);
3025 /* we snap if the snap modifier is not enabled.
3028 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3029 snap_to (sync_frame);
3032 if (sync_frame - sync_offset <= sync_frame) {
3033 pending_region_position = sync_frame - (sync_dir*sync_offset);
3035 pending_region_position = 0;
3039 pending_region_position = 0;
3042 if (pending_region_position > max_frames - rv->region.length()) {
3043 pending_region_position = drag_info.last_frame_position;
3046 // printf ("3: pending_region_position= %lu %lu\n", pending_region_position, drag_info.last_frame_position );
3048 if (pending_region_position != drag_info.last_frame_position && !drag_info.x_constrained) {
3050 /* now compute the canvas unit distance we need to move the regiondrag_info.last_trackview->order
3051 to make it appear at the new location.
3054 if (pending_region_position > drag_info.last_frame_position) {
3055 x_delta = ((double) (pending_region_position - drag_info.last_frame_position) / frames_per_unit);
3057 x_delta = -((double) (drag_info.last_frame_position - pending_region_position) / frames_per_unit);
3060 drag_info.last_frame_position = pending_region_position;
3067 /* threshold not passed */
3072 /*************************************************************
3074 ************************************************************/
3076 if (x_delta == 0 && (pointer_y_span == 0)) {
3077 /* haven't reached next snap point, and we're not switching
3078 trackviews. nothing to do.
3084 for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin(); i != selection->audio_regions.by_layer().end(); ++i) {
3086 AudioRegionView* rv2;
3089 /* if any regionview is at zero, we need to know so we can
3090 stop further leftward motion.
3093 double ix1, ix2, iy1, iy2;
3094 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3095 rv2->get_canvas_group()->i2w (ix1, iy1);
3104 /*************************************************************
3106 ************************************************************/
3108 pair<set<Playlist*>::iterator,bool> insert_result;
3109 const list<AudioRegionView*>& layered_regions = selection->audio_regions.by_layer();
3111 for (list<AudioRegionView*>::const_iterator i = layered_regions.begin(); i != layered_regions.end(); ++i) {
3113 AudioRegionView* rv;
3115 double ix1, ix2, iy1, iy2;
3116 int32_t temp_pointer_y_span = pointer_y_span;
3118 /* get item BBox, which will be relative to parent. so we have
3119 to query on a child, then convert to world coordinates using
3123 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3124 rv->get_canvas_group()->i2w (ix1, iy1);
3125 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3126 AudioTimeAxisView* canvas_atv = dynamic_cast<AudioTimeAxisView*>(tvp2);
3127 AudioTimeAxisView* temp_atv;
3129 if ((pointer_y_span != 0) && !clamp_y_axis) {
3132 for (j = height_list.begin(); j!= height_list.end(); j++) {
3133 if (x == canvas_atv->order) {
3134 /* we found the track the region is on */
3135 if (x != original_pointer_order) {
3136 /*this isn't from the same track we're dragging from */
3137 temp_pointer_y_span = canvas_pointer_y_span;
3139 while (temp_pointer_y_span > 0) {
3140 /* we're moving up canvas-wise,
3141 so we need to find the next track height
3143 if (j != height_list.begin()) {
3146 if (x != original_pointer_order) {
3147 /* we're not from the dragged track, so ignore hidden tracks. */
3149 temp_pointer_y_span++;
3153 temp_pointer_y_span--;
3155 while (temp_pointer_y_span < 0) {
3157 if (x != original_pointer_order) {
3159 temp_pointer_y_span--;
3163 if (j != height_list.end()) {
3166 temp_pointer_y_span++;
3168 /* find out where we'll be when we move and set height accordingly */
3170 tvp2 = trackview_by_y_position (iy1 + y_delta);
3171 temp_atv = dynamic_cast<AudioTimeAxisView*>(tvp2);
3172 rv->set_height (temp_atv->height);
3174 /* if you un-comment the following, the region colours will follow the track colours whilst dragging,
3175 personally, i think this can confuse things, but never mind.
3178 //const GdkColor& col (temp_atv->view->get_region_color());
3179 //rv->set_color (const_cast<GdkColor&>(col));
3186 /* prevent the regionview from being moved to before
3187 the zero position on the canvas.
3192 if (-x_delta > ix1) {
3195 } else if ((x_delta > 0) &&(rv->region.last_frame() > max_frames - x_delta)) {
3196 x_delta = max_frames - rv->region.last_frame();
3199 if (drag_info.first_move) {
3201 /* hide any dependent views */
3203 // rv->get_time_axis_view().hide_dependent_views (*rv);
3205 /* this is subtle. raising the regionview itself won't help,
3206 because raise_to_top() just puts the item on the top of
3207 its parent's stack. so, we need to put the trackview canvas_display group
3208 on the top, since its parent is the whole canvas.
3211 rv->get_canvas_group()->raise_to_top();
3212 rv->get_time_axis_view().canvas_display->raise_to_top();
3213 cursor_group->raise_to_top();
3215 /* freeze the playlists from notifying till
3219 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*> (&rv->get_time_axis_view());
3220 if (atv && atv->is_audio_track()) {
3221 AudioPlaylist* pl = atv->get_diskstream()->playlist();
3223 /* only freeze and capture state once */
3225 insert_result = motion_frozen_playlists.insert (pl);
3226 if (insert_result.second) {
3228 session->add_command(MementoUndoCommand<Playlist>(*pl, pl->get_state()));
3234 if (drag_info.brushing) {
3235 mouse_brush_insert_region (rv, pending_region_position);
3237 rv->move (x_delta, y_delta);
3241 if (drag_info.first_move) {
3242 cursor_group->raise_to_top();
3245 drag_info.first_move = false;
3247 if (x_delta != 0 && !drag_info.brushing) {
3248 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3254 Editor::region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3256 jack_nframes_t where;
3257 AudioRegionView* rv = reinterpret_cast<AudioRegionView *> (drag_info.data);
3258 pair<set<Playlist*>::iterator,bool> insert_result;
3259 bool nocommit = true;
3261 AudioTimeAxisView* atv;
3262 bool regionview_y_movement;
3263 bool regionview_x_movement;
3265 /* first_move is set to false if the regionview has been moved in the
3269 if (drag_info.first_move) {
3276 /* The regionview has been moved at some stage during the grab so we need
3277 to account for any mouse movement between this event and the last one.
3280 region_drag_motion_callback (item, event);
3282 if (drag_info.brushing) {
3283 /* all changes were made during motion event handlers */
3287 /* adjust for track speed */
3290 atv = dynamic_cast<AudioTimeAxisView*> (drag_info.last_trackview);
3291 if (atv && atv->get_diskstream()) {
3292 speed = atv->get_diskstream()->speed();
3295 regionview_x_movement = (drag_info.last_frame_position != (jack_nframes_t) (rv->region.position()/speed));
3296 regionview_y_movement = (drag_info.last_trackview != &rv->get_time_axis_view());
3298 //printf ("last_frame: %s position is %lu %g\n", rv->get_time_axis_view().name().c_str(), drag_info.last_frame_position, speed);
3299 //printf ("last_rackview: %s \n", drag_info.last_trackview->name().c_str());
3301 if (regionview_y_movement) {
3303 /* motion between tracks */
3305 list<AudioRegionView*> new_selection;
3307 /* moved to a different audio track. */
3309 for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin(); i != selection->audio_regions.by_layer().end(); ) {
3311 AudioRegionView* rv2 = (*i);
3313 /* the region that used to be in the old playlist is not
3314 moved to the new one - we make a copy of it. as a result,
3315 any existing editor for the region should no longer be
3319 if (!drag_info.copy) {
3320 rv2->hide_region_editor();
3322 new_selection.push_back (rv2);
3326 /* first, freeze the target tracks */
3328 for (list<AudioRegionView*>::const_iterator i = new_selection.begin(); i != new_selection.end();i++ ) {
3330 Playlist* from_playlist;
3331 Playlist* to_playlist;
3333 double ix1, ix2, iy1, iy2;
3335 (*i)->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3336 (*i)->get_canvas_group()->i2w (ix1, iy1);
3337 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3338 AudioTimeAxisView* atv2 = dynamic_cast<AudioTimeAxisView*>(tvp2);
3340 from_playlist = (*i)->region.playlist();
3341 to_playlist = atv2->playlist();
3343 /* the from_playlist was frozen in the "first_move" case
3344 of the motion handler. the insert can fail,
3345 but that doesn't matter. it just means
3346 we already have the playlist in the list.
3349 motion_frozen_playlists.insert (from_playlist);
3351 /* only freeze the to_playlist once */
3353 insert_result = motion_frozen_playlists.insert(to_playlist);
3354 if (insert_result.second) {
3355 to_playlist->freeze();
3356 session->add_command(MementoUndoCommand<Playlist>(*to_playlist, to_playlist->get_state()));
3361 /* now do it again with the actual operations */
3363 for (list<AudioRegionView*>::const_iterator i = new_selection.begin(); i != new_selection.end();i++ ) {
3365 Playlist* from_playlist;
3366 Playlist* to_playlist;
3368 double ix1, ix2, iy1, iy2;
3370 (*i)->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3371 (*i)->get_canvas_group()->i2w (ix1, iy1);
3372 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3373 AudioTimeAxisView* atv2 = dynamic_cast<AudioTimeAxisView*>(tvp2);
3375 from_playlist = (*i)->region.playlist();
3376 to_playlist = atv2->playlist();
3378 latest_regionview = 0;
3380 where = (jack_nframes_t) (unit_to_frame (ix1) * speed);
3381 Region* new_region = createRegion ((*i)->region);
3383 from_playlist->remove_region (&((*i)->region));
3385 sigc::connection c = atv2->view->AudioRegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3386 to_playlist->add_region (*new_region, where);
3389 if (latest_regionview) {
3390 selection->add (latest_regionview);
3396 /* motion within a single track */
3398 for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin(); i != selection->audio_regions.by_layer().end(); ++i) {
3402 if (rv->region.locked()) {
3406 if (regionview_x_movement) {
3407 double ownspeed = 1.0;
3408 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*> (&(rv->get_time_axis_view()));
3410 if (atv && atv->get_diskstream()) {
3411 ownspeed = atv->get_diskstream()->speed();
3414 /* base the new region position on the current position of the regionview.*/
3416 double ix1, ix2, iy1, iy2;
3418 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3419 rv->get_canvas_group()->i2w (ix1, iy1);
3420 where = (jack_nframes_t) (unit_to_frame (ix1) * ownspeed);
3424 where = rv->region.position();
3427 rv->get_time_axis_view().reveal_dependent_views (*rv);
3429 /* no need to add an undo here, we did that when we added this playlist to motion_frozen playlists */
3431 rv->region.set_position (where, (void *) this);
3436 for (set<Playlist*>::iterator p = motion_frozen_playlists.begin(); p != motion_frozen_playlists.end(); ++p) {
3438 session->add_command (MementoRedoCommand<Playlist>(*(*p), (*p)->get_state()));
3441 motion_frozen_playlists.clear ();
3444 commit_reversible_command ();
3449 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
3451 /* Either add to or set the set the region selection, unless
3452 this is an alignment click (control used)
3455 if (Keyboard::modifier_state_contains (event->state, Keyboard::Control)) {
3456 TimeAxisView* tv = &rv.get_time_axis_view();
3457 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*>(tv);
3459 if (atv && atv->is_audio_track()) {
3460 speed = atv->get_diskstream()->speed();
3463 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Alt))) {
3465 align_region (rv.region, SyncPoint, (jack_nframes_t) (edit_cursor->current_frame * speed));
3467 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Shift))) {
3469 align_region (rv.region, End, (jack_nframes_t) (edit_cursor->current_frame * speed));
3473 align_region (rv.region, Start, (jack_nframes_t) (edit_cursor->current_frame * speed));
3479 Editor::show_verbose_time_cursor (jack_nframes_t frame, double offset, double xpos, double ypos)
3490 switch (ARDOUR_UI::instance()->secondary_clock.mode ()) {
3491 case AudioClock::BBT:
3492 session->bbt_time (frame, bbt);
3493 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, bbt.bars, bbt.beats, bbt.ticks);
3496 case AudioClock::SMPTE:
3497 session->smpte_time (frame, smpte);
3498 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
3501 case AudioClock::MinSec:
3502 /* XXX fix this to compute min/sec properly */
3503 session->smpte_time (frame, smpte);
3504 secs = smpte.seconds + ((float) smpte.frames / session->smpte_frames_per_second);
3505 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", smpte.hours, smpte.minutes, secs);
3509 snprintf (buf, sizeof(buf), "%u", frame);
3513 if (xpos >= 0 && ypos >=0) {
3514 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
3517 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
3519 show_verbose_canvas_cursor ();
3523 Editor::show_verbose_duration_cursor (jack_nframes_t start, jack_nframes_t end, double offset, double xpos, double ypos)
3530 Meter meter_at_start(session->tempo_map().meter_at(start));
3536 switch (ARDOUR_UI::instance()->secondary_clock.mode ()) {
3537 case AudioClock::BBT:
3538 session->bbt_time (start, sbbt);
3539 session->bbt_time (end, ebbt);
3542 /* XXX this computation won't work well if the
3543 user makes a selection that spans any meter changes.
3546 ebbt.bars -= sbbt.bars;
3547 if (ebbt.beats >= sbbt.beats) {
3548 ebbt.beats -= sbbt.beats;
3551 ebbt.beats = int(meter_at_start.beats_per_bar()) + ebbt.beats - sbbt.beats;
3553 if (ebbt.ticks >= sbbt.ticks) {
3554 ebbt.ticks -= sbbt.ticks;
3557 ebbt.ticks = int(Meter::ticks_per_beat) + ebbt.ticks - sbbt.ticks;
3560 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, ebbt.bars, ebbt.beats, ebbt.ticks);
3563 case AudioClock::SMPTE:
3564 session->smpte_duration (end - start, smpte);
3565 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
3568 case AudioClock::MinSec:
3569 /* XXX fix this to compute min/sec properly */
3570 session->smpte_duration (end - start, smpte);
3571 secs = smpte.seconds + ((float) smpte.frames / session->smpte_frames_per_second);
3572 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", smpte.hours, smpte.minutes, secs);
3576 snprintf (buf, sizeof(buf), "%u", end - start);
3580 if (xpos >= 0 && ypos >=0) {
3581 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
3584 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
3586 show_verbose_canvas_cursor ();
3590 Editor::collect_new_region_view (AudioRegionView* rv)
3592 latest_regionview = rv;
3596 Editor::start_selection_grab (ArdourCanvas::Item* item, GdkEvent* event)
3598 if (clicked_regionview == 0) {
3602 /* lets try to create new Region for the selection */
3604 vector<AudioRegion*> new_regions;
3605 create_region_from_selection (new_regions);
3607 if (new_regions.empty()) {
3611 /* XXX fix me one day to use all new regions */
3613 Region* region = new_regions.front();
3615 /* add it to the current stream/playlist.
3617 tricky: the streamview for the track will add a new regionview. we will
3618 catch the signal it sends when it creates the regionview to
3619 set the regionview we want to then drag.
3622 latest_regionview = 0;
3623 sigc::connection c = clicked_audio_trackview->view->AudioRegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3625 /* A selection grab currently creates two undo/redo operations, one for
3626 creating the new region and another for moving it.
3629 begin_reversible_command (_("selection grab"));
3631 Playlist* playlist = clicked_trackview->playlist();
3633 before = playlist->get_state();
3634 clicked_trackview->playlist()->add_region (*region, selection->time[clicked_selection].start);
3635 after = playlist->get_state();
3636 session->add_command(MementoCommand<Playlist>(*playlist, before, after));
3638 commit_reversible_command ();
3642 if (latest_regionview == 0) {
3643 /* something went wrong */
3647 /* we need to deselect all other regionviews, and select this one
3648 i'm ignoring undo stuff, because the region creation will take care of it */
3649 selection->set (latest_regionview);
3651 drag_info.item = latest_regionview->get_canvas_group();
3652 drag_info.data = latest_regionview;
3653 drag_info.motion_callback = &Editor::region_drag_motion_callback;
3654 drag_info.finished_callback = &Editor::region_drag_finished_callback;
3658 drag_info.last_trackview = clicked_trackview;
3659 drag_info.last_frame_position = latest_regionview->region.position();
3660 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
3662 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3666 Editor::cancel_selection ()
3668 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
3669 (*i)->hide_selection ();
3671 begin_reversible_command (_("cancel selection"));
3672 selection->clear ();
3673 clicked_selection = 0;
3674 commit_reversible_command ();
3678 Editor::start_selection_op (ArdourCanvas::Item* item, GdkEvent* event, SelectionOp op)
3680 jack_nframes_t start = 0;
3681 jack_nframes_t end = 0;
3687 drag_info.item = item;
3688 drag_info.motion_callback = &Editor::drag_selection;
3689 drag_info.finished_callback = &Editor::end_selection_op;
3694 case CreateSelection:
3695 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
3696 drag_info.copy = true;
3698 drag_info.copy = false;
3700 start_grab (event, selector_cursor);
3703 case SelectionStartTrim:
3704 if (clicked_trackview) {
3705 clicked_trackview->order_selection_trims (item, true);
3707 start_grab (event, trimmer_cursor);
3708 start = selection->time[clicked_selection].start;
3709 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
3712 case SelectionEndTrim:
3713 if (clicked_trackview) {
3714 clicked_trackview->order_selection_trims (item, false);
3716 start_grab (event, trimmer_cursor);
3717 end = selection->time[clicked_selection].end;
3718 drag_info.pointer_frame_offset = drag_info.grab_frame - end;
3722 start = selection->time[clicked_selection].start;
3724 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
3728 if (selection_op == SelectionMove) {
3729 show_verbose_time_cursor(start, 10);
3731 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
3736 Editor::drag_selection (ArdourCanvas::Item* item, GdkEvent* event)
3738 jack_nframes_t start = 0;
3739 jack_nframes_t end = 0;
3740 jack_nframes_t length;
3741 jack_nframes_t pending_position;
3743 if ((int32_t) drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
3744 pending_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
3747 pending_position = 0;
3750 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3751 snap_to (pending_position);
3754 /* only alter selection if the current frame is
3755 different from the last frame position (adjusted)
3758 if (pending_position == drag_info.last_pointer_frame) return;
3760 switch (selection_op) {
3761 case CreateSelection:
3763 if (drag_info.first_move) {
3764 snap_to (drag_info.grab_frame);
3767 if (pending_position < drag_info.grab_frame) {
3768 start = pending_position;
3769 end = drag_info.grab_frame;
3771 end = pending_position;
3772 start = drag_info.grab_frame;
3775 /* first drag: Either add to the selection
3776 or create a new selection->
3779 if (drag_info.first_move) {
3781 begin_reversible_command (_("range selection"));
3783 if (drag_info.copy) {
3784 /* adding to the selection */
3785 clicked_selection = selection->add (start, end);
3786 drag_info.copy = false;
3788 /* new selection-> */
3789 clicked_selection = selection->set (clicked_trackview, start, end);
3794 case SelectionStartTrim:
3796 if (drag_info.first_move) {
3797 begin_reversible_command (_("trim selection start"));
3800 start = selection->time[clicked_selection].start;
3801 end = selection->time[clicked_selection].end;
3803 if (pending_position > end) {
3806 start = pending_position;
3810 case SelectionEndTrim:
3812 if (drag_info.first_move) {
3813 begin_reversible_command (_("trim selection end"));
3816 start = selection->time[clicked_selection].start;
3817 end = selection->time[clicked_selection].end;
3819 if (pending_position < start) {
3822 end = pending_position;
3829 if (drag_info.first_move) {
3830 begin_reversible_command (_("move selection"));
3833 start = selection->time[clicked_selection].start;
3834 end = selection->time[clicked_selection].end;
3836 length = end - start;
3838 start = pending_position;
3841 end = start + length;
3846 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
3847 start_canvas_autoscroll (1);
3851 selection->replace (clicked_selection, start, end);
3854 drag_info.last_pointer_frame = pending_position;
3855 drag_info.first_move = false;
3857 if (selection_op == SelectionMove) {
3858 show_verbose_time_cursor(start, 10);
3860 show_verbose_time_cursor(pending_position, 10);
3865 Editor::end_selection_op (ArdourCanvas::Item* item, GdkEvent* event)
3867 if (!drag_info.first_move) {
3868 drag_selection (item, event);
3869 /* XXX this is not object-oriented programming at all. ick */
3870 if (selection->time.consolidate()) {
3871 selection->TimeChanged ();
3873 commit_reversible_command ();
3875 /* just a click, no pointer movement.*/
3877 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
3879 selection->clear_time();
3884 /* XXX what happens if its a music selection? */
3885 session->set_audio_range (selection->time);
3886 stop_canvas_autoscroll ();
3890 Editor::start_trim (ArdourCanvas::Item* item, GdkEvent* event)
3893 TimeAxisView* tvp = clicked_trackview;
3894 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
3896 if (tv && tv->is_audio_track()) {
3897 speed = tv->get_diskstream()->speed();
3900 jack_nframes_t region_start = (jack_nframes_t) (clicked_regionview->region.position() / speed);
3901 jack_nframes_t region_end = (jack_nframes_t) (clicked_regionview->region.last_frame() / speed);
3902 jack_nframes_t region_length = (jack_nframes_t) (clicked_regionview->region.length() / speed);
3904 motion_frozen_playlists.clear();
3906 //drag_info.item = clicked_regionview->get_name_highlight();
3907 drag_info.item = item;
3908 drag_info.motion_callback = &Editor::trim_motion_callback;
3909 drag_info.finished_callback = &Editor::trim_finished_callback;
3911 start_grab (event, trimmer_cursor);
3913 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
3914 trim_op = ContentsTrim;
3916 /* These will get overridden for a point trim.*/
3917 if (drag_info.current_pointer_frame < (region_start + region_length/2)) {
3918 /* closer to start */
3919 trim_op = StartTrim;
3920 } else if (drag_info.current_pointer_frame > (region_end - region_length/2)) {
3928 show_verbose_time_cursor(region_start, 10);
3931 show_verbose_time_cursor(region_end, 10);
3934 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
3940 Editor::trim_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
3942 AudioRegionView* rv = clicked_regionview;
3943 jack_nframes_t frame_delta = 0;
3944 bool left_direction;
3945 bool obey_snap = !Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier());
3947 /* snap modifier works differently here..
3948 its' current state has to be passed to the
3949 various trim functions in order to work properly
3953 TimeAxisView* tvp = clicked_trackview;
3954 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
3955 pair<set<Playlist*>::iterator,bool> insert_result;
3957 if (tv && tv->is_audio_track()) {
3958 speed = tv->get_diskstream()->speed();
3961 if (drag_info.last_pointer_frame > drag_info.current_pointer_frame) {
3962 left_direction = true;
3964 left_direction = false;
3968 snap_to (drag_info.current_pointer_frame);
3971 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
3975 if (drag_info.first_move) {
3981 trim_type = "Region start trim";
3984 trim_type = "Region end trim";
3987 trim_type = "Region content trim";
3991 begin_reversible_command (trim_type);
3993 for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin(); i != selection->audio_regions.by_layer().end(); ++i) {
3994 (*i)->region.freeze ();
3995 (*i)->temporarily_hide_envelope ();
3997 Playlist * pl = (*i)->region.playlist();
3998 insert_result = motion_frozen_playlists.insert (pl);
3999 if (insert_result.second) {
4000 session->add_command(MementoUndoCommand<Playlist>(*pl, pl->get_state()));
4005 if (left_direction) {
4006 frame_delta = (drag_info.last_pointer_frame - drag_info.current_pointer_frame);
4008 frame_delta = (drag_info.current_pointer_frame - drag_info.last_pointer_frame);
4013 if ((left_direction == false) && (drag_info.current_pointer_frame <= rv->region.first_frame()/speed)) {
4016 for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin(); i != selection->audio_regions.by_layer().end(); ++i) {
4017 single_start_trim (**i, frame_delta, left_direction, obey_snap);
4023 if ((left_direction == true) && (drag_info.current_pointer_frame > (jack_nframes_t) (rv->region.last_frame()/speed))) {
4026 for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin(); i != selection->audio_regions.by_layer().end(); ++i) {
4027 single_end_trim (**i, frame_delta, left_direction, obey_snap);
4034 bool swap_direction = false;
4036 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
4037 swap_direction = true;
4040 for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin();
4041 i != selection->audio_regions.by_layer().end(); ++i)
4043 single_contents_trim (**i, frame_delta, left_direction, swap_direction, obey_snap);
4051 show_verbose_time_cursor((jack_nframes_t) (rv->region.position()/speed), 10);
4054 show_verbose_time_cursor((jack_nframes_t) (rv->region.last_frame()/speed), 10);
4057 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4061 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4062 drag_info.first_move = false;
4066 Editor::single_contents_trim (AudioRegionView& rv, jack_nframes_t frame_delta, bool left_direction, bool swap_direction, bool obey_snap)
4068 Region& region (rv.region);
4070 if (region.locked()) {
4074 jack_nframes_t new_bound;
4077 TimeAxisView* tvp = clicked_trackview;
4078 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
4080 if (tv && tv->is_audio_track()) {
4081 speed = tv->get_diskstream()->speed();
4084 if (left_direction) {
4085 if (swap_direction) {
4086 new_bound = (jack_nframes_t) (region.position()/speed) + frame_delta;
4088 new_bound = (jack_nframes_t) (region.position()/speed) - frame_delta;
4091 if (swap_direction) {
4092 new_bound = (jack_nframes_t) (region.position()/speed) - frame_delta;
4094 new_bound = (jack_nframes_t) (region.position()/speed) + frame_delta;
4099 snap_to (new_bound);
4101 region.trim_start ((jack_nframes_t) (new_bound * speed), this);
4102 rv.region_changed (StartChanged);
4106 Editor::single_start_trim (AudioRegionView& rv, jack_nframes_t frame_delta, bool left_direction, bool obey_snap)
4108 Region& region (rv.region);
4110 if (region.locked()) {
4114 jack_nframes_t new_bound;
4117 TimeAxisView* tvp = clicked_trackview;
4118 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
4120 if (tv && tv->is_audio_track()) {
4121 speed = tv->get_diskstream()->speed();
4124 if (left_direction) {
4125 new_bound = (jack_nframes_t) (region.position()/speed) - frame_delta;
4127 new_bound = (jack_nframes_t) (region.position()/speed) + frame_delta;
4131 snap_to (new_bound, (left_direction ? 0 : 1));
4134 region.trim_front ((jack_nframes_t) (new_bound * speed), this);
4136 rv.region_changed (Change (LengthChanged|PositionChanged|StartChanged));
4140 Editor::single_end_trim (AudioRegionView& rv, jack_nframes_t frame_delta, bool left_direction, bool obey_snap)
4142 Region& region (rv.region);
4144 if (region.locked()) {
4148 jack_nframes_t new_bound;
4151 TimeAxisView* tvp = clicked_trackview;
4152 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
4154 if (tv && tv->is_audio_track()) {
4155 speed = tv->get_diskstream()->speed();
4158 if (left_direction) {
4159 new_bound = (jack_nframes_t) ((region.last_frame() + 1)/speed) - frame_delta;
4161 new_bound = (jack_nframes_t) ((region.last_frame() + 1)/speed) + frame_delta;
4165 snap_to (new_bound);
4167 region.trim_end ((jack_nframes_t) (new_bound * speed), this);
4168 rv.region_changed (LengthChanged);
4172 Editor::trim_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
4174 if (!drag_info.first_move) {
4175 trim_motion_callback (item, event);
4177 if (!clicked_regionview->get_selected()) {
4178 thaw_region_after_trim (*clicked_regionview);
4181 for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin();
4182 i != selection->audio_regions.by_layer().end(); ++i)
4184 thaw_region_after_trim (**i);
4188 for (set<Playlist*>::iterator p = motion_frozen_playlists.begin(); p != motion_frozen_playlists.end(); ++p) {
4190 session->add_command (MementoRedoCommand<Playlist>(*(*p), (*p)->get_state()));
4193 motion_frozen_playlists.clear ();
4195 commit_reversible_command();
4197 /* no mouse movement */
4203 Editor::point_trim (GdkEvent* event)
4205 AudioRegionView* rv = clicked_regionview;
4206 jack_nframes_t new_bound = drag_info.current_pointer_frame;
4208 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4209 snap_to (new_bound);
4212 /* Choose action dependant on which button was pressed */
4213 switch (event->button.button) {
4215 trim_op = StartTrim;
4216 begin_reversible_command (_("Start point trim"));
4218 if (rv->get_selected()) {
4220 for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin();
4221 i != selection->audio_regions.by_layer().end(); ++i)
4223 if (!(*i)->region.locked()) {
4224 XMLNode &before, &after;
4225 Playlist *pl = (*i)->region.playlist();
4226 before = pl->get_state();
4227 (*i)->region.trim_front (new_bound, this);
4228 after = pl->get_state();
4229 session->add_command(MementoCommand<Playlist>(*pl, before, after));
4235 if (!rv->region.locked()) {
4236 XMLNode &before, &after;
4237 Playlist *pl = rv->region.playlist();
4238 before = pl->get_state();
4239 rv->region.trim_front (new_bound, this);
4240 after = pl->get_state();
4241 session->add_command(MementoCommand<Playlist>(*pl, before, after));
4245 commit_reversible_command();
4250 begin_reversible_command (_("End point trim"));
4252 if (rv->get_selected()) {
4254 for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin(); i != selection->audio_regions.by_layer().end(); ++i)
4256 if (!(*i)->region.locked()) {
4257 XMLNode &before, &after;
4258 Playlist *pl = (*i)->region.playlist();
4259 before = pl->get_state();
4260 (*i)->region.trim_end (new_bound, this);
4261 after = pl->get_state();
4262 session->add_command(MementoCommand<Playlist>(*pl, before, after));
4268 if (!rv->region.locked()) {
4269 XMLNode &before, &after;
4270 Playlist *pl = rv->region.playlist();
4271 before = pl->get_state();
4272 rv->region.trim_end (new_bound, this);
4273 after = pl->get_state();
4274 session->add_command (MementoCommand<Playlist>(*pl, before, after));
4278 commit_reversible_command();
4287 Editor::thaw_region_after_trim (AudioRegionView& rv)
4289 Region& region (rv.region);
4291 if (region.locked()) {
4295 region.thaw (_("trimmed region"));
4296 XMLNode &after = region.playlist()->get_state();
4297 session->add_command (MementoRedoCommand<Playlist>(*(region.playlist()), after));
4299 rv.unhide_envelope ();
4303 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* event)
4308 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
4309 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
4313 Location* location = find_location_from_marker (marker, is_start);
4314 location->set_hidden (true, this);
4319 Editor::start_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event, RangeMarkerOp op)
4325 drag_info.item = item;
4326 drag_info.motion_callback = &Editor::drag_range_markerbar_op;
4327 drag_info.finished_callback = &Editor::end_range_markerbar_op;
4329 range_marker_op = op;
4331 if (!temp_location) {
4332 temp_location = new Location;
4336 case CreateRangeMarker:
4337 case CreateTransportMarker:
4339 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
4340 drag_info.copy = true;
4342 drag_info.copy = false;
4344 start_grab (event, selector_cursor);
4348 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4353 Editor::drag_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4355 jack_nframes_t start = 0;
4356 jack_nframes_t end = 0;
4357 ArdourCanvas::SimpleRect *crect = (range_marker_op == CreateRangeMarker) ? range_bar_drag_rect: transport_bar_drag_rect;
4359 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4360 snap_to (drag_info.current_pointer_frame);
4363 /* only alter selection if the current frame is
4364 different from the last frame position.
4367 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4369 switch (range_marker_op) {
4370 case CreateRangeMarker:
4371 case CreateTransportMarker:
4372 if (drag_info.first_move) {
4373 snap_to (drag_info.grab_frame);
4376 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4377 start = drag_info.current_pointer_frame;
4378 end = drag_info.grab_frame;
4380 end = drag_info.current_pointer_frame;
4381 start = drag_info.grab_frame;
4384 /* first drag: Either add to the selection
4385 or create a new selection.
4388 if (drag_info.first_move) {
4390 temp_location->set (start, end);
4394 update_marker_drag_item (temp_location);
4395 range_marker_drag_rect->show();
4396 range_marker_drag_rect->raise_to_top();
4402 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
4403 start_canvas_autoscroll (1);
4407 temp_location->set (start, end);
4409 double x1 = frame_to_pixel (start);
4410 double x2 = frame_to_pixel (end);
4411 crect->property_x1() = x1;
4412 crect->property_x2() = x2;
4414 update_marker_drag_item (temp_location);
4417 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4418 drag_info.first_move = false;
4420 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4425 Editor::end_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4427 Location * newloc = 0;
4429 if (!drag_info.first_move) {
4430 drag_range_markerbar_op (item, event);
4432 switch (range_marker_op) {
4433 case CreateRangeMarker:
4434 begin_reversible_command (_("new range marker"));
4435 XMLNode &before, &after;
4436 before = session->locations()->get_state();
4437 newloc = new Location(temp_location->start(), temp_location->end(), "unnamed", Location::IsRangeMarker);
4438 session->locations()->add (newloc, true);
4439 after = session->locations()->get_state();
4440 session->add_command(MementoCommand<Locations>(session->locations(), before, after));
4441 commit_reversible_command ();
4443 range_bar_drag_rect->hide();
4444 range_marker_drag_rect->hide();
4447 case CreateTransportMarker:
4448 // popup menu to pick loop or punch
4449 new_transport_marker_context_menu (&event->button, item);
4454 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
4456 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
4458 jack_nframes_t start;
4461 start = session->locations()->first_mark_before (drag_info.grab_frame);
4462 end = session->locations()->first_mark_after (drag_info.grab_frame);
4464 if (end == max_frames) {
4465 end = session->current_end_frame ();
4469 start = session->current_start_frame ();
4472 switch (mouse_mode) {
4474 /* find the two markers on either side and then make the selection from it */
4475 cerr << "select between " << start << " .. " << end << endl;
4476 select_all_within (start, end, 0.0f, FLT_MAX, Selection::Set);
4480 /* find the two markers on either side of the click and make the range out of it */
4481 selection->set (0, start, end);
4490 stop_canvas_autoscroll ();
4496 Editor::start_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4498 drag_info.item = item;
4499 drag_info.motion_callback = &Editor::drag_mouse_zoom;
4500 drag_info.finished_callback = &Editor::end_mouse_zoom;
4502 start_grab (event, zoom_cursor);
4504 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4508 Editor::drag_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4510 jack_nframes_t start;
4513 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4514 snap_to (drag_info.current_pointer_frame);
4516 if (drag_info.first_move) {
4517 snap_to (drag_info.grab_frame);
4521 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4523 /* base start and end on initial click position */
4524 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4525 start = drag_info.current_pointer_frame;
4526 end = drag_info.grab_frame;
4528 end = drag_info.current_pointer_frame;
4529 start = drag_info.grab_frame;
4534 if (drag_info.first_move) {
4536 zoom_rect->raise_to_top();
4539 reposition_zoom_rect(start, end);
4541 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4542 drag_info.first_move = false;
4544 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4549 Editor::end_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4551 if (!drag_info.first_move) {
4552 drag_mouse_zoom (item, event);
4554 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
4555 temporal_zoom_by_frame (drag_info.grab_frame, drag_info.last_pointer_frame, "mouse zoom");
4557 temporal_zoom_by_frame (drag_info.last_pointer_frame, drag_info.grab_frame, "mouse zoom");
4560 temporal_zoom_to_frame (false, drag_info.grab_frame);
4562 temporal_zoom_step (false);
4563 center_screen (drag_info.grab_frame);
4571 Editor::reposition_zoom_rect (jack_nframes_t start, jack_nframes_t end)
4573 double x1 = frame_to_pixel (start);
4574 double x2 = frame_to_pixel (end);
4575 double y2 = canvas_height - 2;
4577 zoom_rect->property_x1() = x1;
4578 zoom_rect->property_y1() = 1.0;
4579 zoom_rect->property_x2() = x2;
4580 zoom_rect->property_y2() = y2;
4584 Editor::start_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4586 drag_info.item = item;
4587 drag_info.motion_callback = &Editor::drag_rubberband_select;
4588 drag_info.finished_callback = &Editor::end_rubberband_select;
4590 start_grab (event, cross_hair_cursor);
4592 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4596 Editor::drag_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4598 jack_nframes_t start;
4603 /* use a bigger drag threshold than the default */
4605 if (abs ((int) (drag_info.current_pointer_frame - drag_info.grab_frame)) < 8) {
4609 // if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4610 // snap_to (drag_info.current_pointer_frame);
4612 // if (drag_info.first_move) {
4613 // snap_to (drag_info.grab_frame);
4618 /* base start and end on initial click position */
4619 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4620 start = drag_info.current_pointer_frame;
4621 end = drag_info.grab_frame;
4623 end = drag_info.current_pointer_frame;
4624 start = drag_info.grab_frame;
4627 if (drag_info.current_pointer_y < drag_info.grab_y) {
4628 y1 = drag_info.current_pointer_y;
4629 y2 = drag_info.grab_y;
4632 y2 = drag_info.current_pointer_y;
4633 y1 = drag_info.grab_y;
4637 if (start != end || y1 != y2) {
4639 double x1 = frame_to_pixel (start);
4640 double x2 = frame_to_pixel (end);
4642 rubberband_rect->property_x1() = x1;
4643 rubberband_rect->property_y1() = y1;
4644 rubberband_rect->property_x2() = x2;
4645 rubberband_rect->property_y2() = y2;
4647 rubberband_rect->show();
4648 rubberband_rect->raise_to_top();
4650 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4651 drag_info.first_move = false;
4653 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4658 Editor::end_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4660 if (!drag_info.first_move) {
4662 drag_rubberband_select (item, event);
4665 if (drag_info.current_pointer_y < drag_info.grab_y) {
4666 y1 = drag_info.current_pointer_y;
4667 y2 = drag_info.grab_y;
4670 y2 = drag_info.current_pointer_y;
4671 y1 = drag_info.grab_y;
4675 Selection::Operation op = Keyboard::selection_type (event->button.state);
4678 begin_reversible_command (_("select regions"));
4680 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
4681 commit = select_all_within (drag_info.grab_frame, drag_info.last_pointer_frame, y1, y2, op);
4683 commit = select_all_within (drag_info.last_pointer_frame, drag_info.grab_frame, y1, y2, op);
4687 commit_reversible_command ();
4691 selection->clear_audio_regions();
4692 selection->clear_points ();
4693 selection->clear_lines ();
4696 rubberband_rect->hide();
4701 Editor::mouse_rename_region (ArdourCanvas::Item* item, GdkEvent* event)
4703 using namespace Gtkmm2ext;
4705 ArdourPrompter prompter (false);
4707 prompter.set_prompt (_("Name for region:"));
4708 prompter.set_initial_text (clicked_regionview->region.name());
4709 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
4710 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
4711 prompter.show_all ();
4712 switch (prompter.run ()) {
4713 case Gtk::RESPONSE_ACCEPT:
4715 prompter.get_result(str);
4717 clicked_regionview->region.set_name (str);
4725 Editor::start_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
4727 drag_info.item = item;
4728 drag_info.motion_callback = &Editor::time_fx_motion;
4729 drag_info.finished_callback = &Editor::end_time_fx;
4733 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4737 Editor::time_fx_motion (ArdourCanvas::Item *item, GdkEvent* event)
4739 AudioRegionView* rv = clicked_regionview;
4741 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4742 snap_to (drag_info.current_pointer_frame);
4745 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
4749 if (drag_info.current_pointer_frame > rv->region.position()) {
4750 rv->get_time_axis_view().show_timestretch (rv->region.position(), drag_info.current_pointer_frame);
4753 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4754 drag_info.first_move = false;
4756 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4760 Editor::end_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
4762 clicked_regionview->get_time_axis_view().hide_timestretch ();
4764 if (drag_info.first_move) {
4768 jack_nframes_t newlen = drag_info.last_pointer_frame - clicked_regionview->region.position();
4769 float percentage = (float) ((double) newlen - (double) clicked_regionview->region.length()) / ((double) newlen) * 100.0f;
4771 begin_reversible_command (_("timestretch"));
4773 if (run_timestretch (selection->audio_regions, percentage) == 0) {
4774 session->commit_reversible_command ();
4779 Editor::mouse_brush_insert_region (AudioRegionView* rv, jack_nframes_t pos)
4781 /* no brushing without a useful snap setting */
4783 switch (snap_mode) {
4785 return; /* can't work because it allows region to be placed anywhere */
4790 switch (snap_type) {
4793 case SnapToEditCursor:
4800 /* don't brush a copy over the original */
4802 if (pos == rv->region.position()) {
4806 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*>(&rv->get_time_axis_view());
4808 if (atv == 0 || !atv->is_audio_track()) {
4812 Playlist* playlist = atv->playlist();
4813 double speed = atv->get_diskstream()->speed();
4815 XMLNode &before, &after;
4816 before = playlist->get_state();
4817 playlist->add_region (*(new AudioRegion (rv->region)), (jack_nframes_t) (pos * speed));
4818 after = playlist->get_state();
4819 session->add_command(MementoCommand<Playlist>(*playlist, before, after));
4821 // playlist is frozen, so we have to update manually
4823 playlist->StateChanged (Change (~0)); /* EMIT SIGNAL */
4827 Editor::track_height_step_timeout ()
4830 struct timeval delta;
4832 gettimeofday (&now, 0);
4833 timersub (&now, &last_track_height_step_timestamp, &delta);
4835 if (delta.tv_sec * 1000000 + delta.tv_usec > 250000) { /* milliseconds */
4836 current_stepping_trackview = 0;