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>
30 #include <pbd/memento_command.h>
32 #include "ardour_ui.h"
34 #include "time_axis_view.h"
35 #include "audio_time_axis.h"
36 #include "regionview.h"
38 #include "streamview.h"
39 #include "region_gain_line.h"
40 #include "automation_time_axis.h"
43 #include "selection.h"
46 #include "rgb_macros.h"
48 #include <ardour/types.h>
49 #include <ardour/route.h>
50 #include <ardour/audio_track.h>
51 #include <ardour/audio_diskstream.h>
52 #include <ardour/playlist.h>
53 #include <ardour/audioplaylist.h>
54 #include <ardour/audioregion.h>
55 #include <ardour/dB.h>
56 #include <ardour/utils.h>
57 #include <ardour/region_factory.h>
64 using namespace ARDOUR;
68 using namespace Editing;
71 Editor::event_frame (GdkEvent* event, double* pcx, double* pcy)
85 switch (event->type) {
86 case GDK_BUTTON_RELEASE:
87 case GDK_BUTTON_PRESS:
88 case GDK_2BUTTON_PRESS:
89 case GDK_3BUTTON_PRESS:
90 track_canvas.w2c(event->button.x, event->button.y, *pcx, *pcy);
92 case GDK_MOTION_NOTIFY:
93 track_canvas.w2c(event->motion.x, event->motion.y, *pcx, *pcy);
95 case GDK_ENTER_NOTIFY:
96 case GDK_LEAVE_NOTIFY:
97 track_canvas.w2c(event->crossing.x, event->crossing.y, *pcx, *pcy);
100 case GDK_KEY_RELEASE:
101 // track_canvas.w2c(event->key.x, event->key.y, *pcx, *pcy);
104 warning << string_compose (_("Editor::event_frame() used on unhandled event type %1"), event->type) << endmsg;
108 /* note that pixel_to_frame() never returns less than zero, so even if the pixel
109 position is negative (as can be the case with motion events in particular),
110 the frame location is always positive.
113 return pixel_to_frame (*pcx);
117 Editor::mouse_mode_toggled (MouseMode m)
119 if (ignore_mouse_mode_toggle) {
125 if (mouse_select_button.get_active()) {
131 if (mouse_move_button.get_active()) {
137 if (mouse_gain_button.get_active()) {
143 if (mouse_zoom_button.get_active()) {
149 if (mouse_timefx_button.get_active()) {
155 if (mouse_audition_button.get_active()) {
166 Editor::set_mouse_mode (MouseMode m, bool force)
168 if (drag_info.item) {
172 if (!force && m == mouse_mode) {
180 if (mouse_mode != MouseRange) {
182 /* in all modes except range, hide the range selection,
183 show the object (region) selection.
186 for (AudioRegionSelection::iterator i = selection->audio_regions.begin(); i != selection->audio_regions.end(); ++i) {
187 (*i)->set_should_show_selection (true);
189 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
190 (*i)->hide_selection ();
196 in range mode,show the range selection.
199 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
200 if ((*i)->get_selected()) {
201 (*i)->show_selection (selection->time);
206 /* XXX the hack of unsetting all other buttongs should go
207 away once GTK2 allows us to use regular radio buttons drawn like
208 normal buttons, rather than my silly GroupedButton hack.
211 ignore_mouse_mode_toggle = true;
213 switch (mouse_mode) {
215 mouse_select_button.set_active (true);
216 current_canvas_cursor = selector_cursor;
220 mouse_move_button.set_active (true);
221 current_canvas_cursor = grabber_cursor;
225 mouse_gain_button.set_active (true);
226 current_canvas_cursor = cross_hair_cursor;
230 mouse_zoom_button.set_active (true);
231 current_canvas_cursor = zoom_cursor;
235 mouse_timefx_button.set_active (true);
236 current_canvas_cursor = time_fx_cursor; // just use playhead
240 mouse_audition_button.set_active (true);
241 current_canvas_cursor = speaker_cursor;
245 ignore_mouse_mode_toggle = false;
248 track_canvas.get_window()->set_cursor(*current_canvas_cursor);
253 Editor::step_mouse_mode (bool next)
255 switch (current_mouse_mode()) {
257 if (next) set_mouse_mode (MouseRange);
258 else set_mouse_mode (MouseTimeFX);
262 if (next) set_mouse_mode (MouseZoom);
263 else set_mouse_mode (MouseObject);
267 if (next) set_mouse_mode (MouseGain);
268 else set_mouse_mode (MouseRange);
272 if (next) set_mouse_mode (MouseTimeFX);
273 else set_mouse_mode (MouseZoom);
277 if (next) set_mouse_mode (MouseAudition);
278 else set_mouse_mode (MouseGain);
282 if (next) set_mouse_mode (MouseObject);
283 else set_mouse_mode (MouseTimeFX);
289 Editor::button_selection (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
295 /* in object/audition/timefx mode, any button press sets
296 the selection if the object can be selected. this is a
297 bit of hack, because we want to avoid this if the
298 mouse operation is a region alignment.
300 note: not dbl-click or triple-click
303 if (((mouse_mode != MouseObject) &&
304 (mouse_mode != MouseAudition || item_type != RegionItem) &&
305 (mouse_mode != MouseTimeFX || item_type != RegionItem)) ||
306 (event->type != GDK_BUTTON_PRESS && event->type != GDK_BUTTON_RELEASE || event->button.button > 3)) {
311 Selection::Operation op = Keyboard::selection_type (event->button.state);
312 bool press = (event->type == GDK_BUTTON_PRESS);
314 begin_reversible_command (_("select on click"));
318 c1 = set_selected_track_from_click (press, op, true, true);
319 c2 = set_selected_regionview_from_click (press, op, true);
323 case AudioRegionViewNameHighlight:
324 case AudioRegionViewName:
325 c1 = set_selected_track_from_click (press, op, true, true);
326 c2 = set_selected_regionview_from_click (press, op, true);
330 case GainAutomationControlPointItem:
331 case PanAutomationControlPointItem:
332 case RedirectAutomationControlPointItem:
333 c1 = set_selected_track_from_click (press, op, true, true);
334 c2 = set_selected_control_point_from_click (press, op, false);
339 commit = set_selected_track_from_click (press, op, true, true);
342 case AutomationTrackItem:
343 commit = set_selected_track_from_click (press, op, true, true);
350 #define SELECT_TRACK_FROM_CANVAS_IN_RANGE_MODE
351 #ifdef SELECT_TRACK_FROM_CANVAS_IN_RANGE_MODE
352 /* in range mode, button 1/2/3 press potentially selects a track */
354 if (mouse_mode == MouseRange &&
355 event->type == GDK_BUTTON_PRESS &&
356 event->button.button <= 3) {
361 case AutomationTrackItem:
362 commit = set_selected_track_from_click (press, op, true, true);
371 commit_reversible_command ();
376 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
378 jack_nframes_t where = event_frame (event, 0, 0);
380 track_canvas.grab_focus();
382 if (session && session->actively_recording()) {
386 button_selection (item, event, item_type);
388 if (drag_info.item == 0 &&
389 (Keyboard::is_delete_event (&event->button) ||
390 Keyboard::is_context_menu_event (&event->button) ||
391 Keyboard::is_edit_event (&event->button))) {
393 /* handled by button release */
397 switch (event->button.button) {
400 if (event->type == GDK_BUTTON_PRESS) {
402 if (drag_info.item) {
403 drag_info.item->ungrab (event->button.time);
406 /* single mouse clicks on any of these item types operate
407 independent of mouse mode, mostly because they are
408 not on the main track canvas or because we want
414 case PlayheadCursorItem:
415 start_cursor_grab (item, event);
419 if (Keyboard::modifier_state_equals (event->button.state,
420 Keyboard::ModifierMask(Keyboard::Control|Keyboard::Shift))) {
421 hide_marker (item, event);
423 start_marker_grab (item, event);
427 case TempoMarkerItem:
428 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
429 start_tempo_marker_copy_grab (item, event);
431 start_tempo_marker_grab (item, event);
435 case MeterMarkerItem:
436 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
437 start_meter_marker_copy_grab (item, event);
439 start_meter_marker_grab (item, event);
449 case RangeMarkerBarItem:
450 start_range_markerbar_op (item, event, CreateRangeMarker);
454 case TransportMarkerBarItem:
455 start_range_markerbar_op (item, event, CreateTransportMarker);
464 switch (mouse_mode) {
467 case StartSelectionTrimItem:
468 start_selection_op (item, event, SelectionStartTrim);
471 case EndSelectionTrimItem:
472 start_selection_op (item, event, SelectionEndTrim);
476 if (Keyboard::modifier_state_contains
477 (event->button.state, Keyboard::ModifierMask(Keyboard::Alt))) {
478 // contains and not equals because I can't use alt as a modifier alone.
479 start_selection_grab (item, event);
480 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
481 /* grab selection for moving */
482 start_selection_op (item, event, SelectionMove);
485 /* this was debated, but decided the more common action was to
486 make a new selection */
487 start_selection_op (item, event, CreateSelection);
492 start_selection_op (item, event, CreateSelection);
498 if (Keyboard::modifier_state_contains (event->button.state,
499 Keyboard::ModifierMask(Keyboard::Control|Keyboard::Alt))
500 && event->type == GDK_BUTTON_PRESS) {
502 start_rubberband_select (item, event);
504 } else if (event->type == GDK_BUTTON_PRESS) {
507 case FadeInHandleItem:
508 start_fade_in_grab (item, event);
511 case FadeOutHandleItem:
512 start_fade_out_grab (item, event);
516 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
517 start_region_copy_grab (item, event);
518 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
519 start_region_brush_grab (item, event);
521 start_region_grab (item, event);
525 case AudioRegionViewNameHighlight:
526 start_trim (item, event);
530 case AudioRegionViewName:
531 /* rename happens on edit clicks */
532 start_trim (clicked_regionview->get_name_highlight(), event);
536 case GainAutomationControlPointItem:
537 case PanAutomationControlPointItem:
538 case RedirectAutomationControlPointItem:
539 start_control_point_grab (item, event);
543 case GainAutomationLineItem:
544 case PanAutomationLineItem:
545 case RedirectAutomationLineItem:
546 start_line_grab_from_line (item, event);
551 case AutomationTrackItem:
552 start_rubberband_select (item, event);
555 /* <CMT Additions> */
556 case ImageFrameHandleStartItem:
557 imageframe_start_handle_op(item, event) ;
560 case ImageFrameHandleEndItem:
561 imageframe_end_handle_op(item, event) ;
564 case MarkerViewHandleStartItem:
565 markerview_item_start_handle_op(item, event) ;
568 case MarkerViewHandleEndItem:
569 markerview_item_end_handle_op(item, event) ;
572 /* </CMT Additions> */
574 /* <CMT Additions> */
576 start_markerview_grab(item, event) ;
579 start_imageframe_grab(item, event) ;
581 /* </CMT Additions> */
597 // start_line_grab_from_regionview (item, event);
600 case GainControlPointItem:
601 start_control_point_grab (item, event);
605 start_line_grab_from_line (item, event);
608 case GainAutomationControlPointItem:
609 case PanAutomationControlPointItem:
610 case RedirectAutomationControlPointItem:
611 start_control_point_grab (item, event);
622 case GainAutomationControlPointItem:
623 case PanAutomationControlPointItem:
624 case RedirectAutomationControlPointItem:
625 start_control_point_grab (item, event);
628 case GainAutomationLineItem:
629 case PanAutomationLineItem:
630 case RedirectAutomationLineItem:
631 start_line_grab_from_line (item, event);
635 // XXX need automation mode to identify which
637 // start_line_grab_from_regionview (item, event);
647 if (event->type == GDK_BUTTON_PRESS) {
648 start_mouse_zoom (item, event);
655 if (item_type == RegionItem) {
656 start_time_fx (item, event);
661 /* handled in release */
670 switch (mouse_mode) {
672 if (event->type == GDK_BUTTON_PRESS) {
675 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
676 start_region_copy_grab (item, event);
678 start_region_grab (item, event);
682 case GainAutomationControlPointItem:
683 case PanAutomationControlPointItem:
684 case RedirectAutomationControlPointItem:
685 start_control_point_grab (item, event);
696 case AudioRegionViewNameHighlight:
697 start_trim (item, event);
701 case AudioRegionViewName:
702 start_trim (clicked_regionview->get_name_highlight(), event);
713 if (event->type == GDK_BUTTON_PRESS) {
714 /* relax till release */
721 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
722 temporal_zoom_session();
724 temporal_zoom_to_frame (true, event_frame(event));
739 switch (mouse_mode) {
741 //temporal_zoom_to_frame (true, where);
742 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
743 temporal_zoom_to_frame (true, where);
746 temporal_zoom_step (true);
751 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::Alt))) {
752 scroll_backward (0.6f);
755 else if (Keyboard::no_modifier_keys_pressed (&event->button)) {
756 scroll_tracks_up_line ();
758 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
759 if (clicked_trackview) {
760 if (!current_stepping_trackview) {
761 step_timeout = Glib::signal_timeout().connect (mem_fun(*this, &Editor::track_height_step_timeout), 500);
762 current_stepping_trackview = clicked_trackview;
764 gettimeofday (&last_track_height_step_timestamp, 0);
765 current_stepping_trackview->step_height (true);
768 else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
769 temporal_zoom_to_frame (true, where);
776 switch (mouse_mode) {
778 // temporal_zoom_to_frame (false, where);
779 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
780 temporal_zoom_to_frame (false, where);
783 temporal_zoom_step (false);
788 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::Alt))) {
789 scroll_forward (0.6f);
792 else if (Keyboard::no_modifier_keys_pressed (&event->button)) {
793 scroll_tracks_down_line ();
795 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
796 if (clicked_trackview) {
797 if (!current_stepping_trackview) {
798 step_timeout = Glib::signal_timeout().connect (mem_fun(*this, &Editor::track_height_step_timeout), 500);
799 current_stepping_trackview = clicked_trackview;
801 gettimeofday (&last_track_height_step_timestamp, 0);
802 current_stepping_trackview->step_height (false);
804 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
805 temporal_zoom_to_frame (false, where);
820 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
822 jack_nframes_t where = event_frame (event, 0, 0);
824 /* no action if we're recording */
826 if (session && session->actively_recording()) {
830 /* first, see if we're finishing a drag ... */
832 if (drag_info.item) {
833 if (end_grab (item, event)) {
834 /* grab dragged, so do nothing else */
839 button_selection (item, event, item_type);
841 /* edit events get handled here */
843 if (drag_info.item == 0 && Keyboard::is_edit_event (&event->button)) {
849 case TempoMarkerItem:
850 edit_tempo_marker (item);
853 case MeterMarkerItem:
854 edit_meter_marker (item);
857 case AudioRegionViewName:
858 if (clicked_regionview->name_active()) {
859 return mouse_rename_region (item, event);
869 /* context menu events get handled here */
871 if (Keyboard::is_context_menu_event (&event->button)) {
873 if (drag_info.item == 0) {
875 /* no matter which button pops up the context menu, tell the menu
876 widget to use button 1 to drive menu selection.
881 case FadeInHandleItem:
883 case FadeOutHandleItem:
884 popup_fade_context_menu (1, event->button.time, item, item_type);
888 popup_track_context_menu (1, event->button.time, item_type, false, where);
892 case AudioRegionViewNameHighlight:
893 case AudioRegionViewName:
894 popup_track_context_menu (1, event->button.time, item_type, false, where);
898 popup_track_context_menu (1, event->button.time, item_type, true, where);
901 case AutomationTrackItem:
902 popup_track_context_menu (1, event->button.time, item_type, false, where);
906 case RangeMarkerBarItem:
907 case TransportMarkerBarItem:
910 popup_ruler_menu (pixel_to_frame(event->button.x), item_type);
914 marker_context_menu (&event->button, item);
917 case TempoMarkerItem:
918 tm_marker_context_menu (&event->button, item);
921 case MeterMarkerItem:
922 tm_marker_context_menu (&event->button, item);
925 case CrossfadeViewItem:
926 popup_track_context_menu (1, event->button.time, item_type, false, where);
929 /* <CMT Additions> */
931 popup_imageframe_edit_menu(1, event->button.time, item, true) ;
933 case ImageFrameTimeAxisItem:
934 popup_imageframe_edit_menu(1, event->button.time, item, false) ;
937 popup_marker_time_axis_edit_menu(1, event->button.time, item, true) ;
939 case MarkerTimeAxisItem:
940 popup_marker_time_axis_edit_menu(1, event->button.time, item, false) ;
942 /* <CMT Additions> */
953 /* delete events get handled here */
955 if (drag_info.item == 0 && Keyboard::is_delete_event (&event->button)) {
958 case TempoMarkerItem:
959 remove_tempo_marker (item);
962 case MeterMarkerItem:
963 remove_meter_marker (item);
967 remove_marker (*item, event);
971 if (mouse_mode == MouseObject) {
972 remove_clicked_region ();
976 case GainControlPointItem:
977 if (mouse_mode == MouseGain) {
978 remove_gain_control_point (item, event);
982 case GainAutomationControlPointItem:
983 case PanAutomationControlPointItem:
984 case RedirectAutomationControlPointItem:
985 remove_control_point (item, event);
994 switch (event->button.button) {
998 /* see comments in button_press_handler */
1000 case PlayheadCursorItem:
1003 case GainAutomationLineItem:
1004 case PanAutomationLineItem:
1005 case RedirectAutomationLineItem:
1006 case StartSelectionTrimItem:
1007 case EndSelectionTrimItem:
1011 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1012 snap_to (where, 0, true);
1014 mouse_add_new_marker (where);
1018 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1021 mouse_add_new_tempo_event (where);
1025 mouse_add_new_meter_event (pixel_to_frame (event->button.x));
1033 switch (mouse_mode) {
1035 switch (item_type) {
1036 case AutomationTrackItem:
1037 dynamic_cast<AutomationTimeAxisView*>(clicked_trackview)->add_automation_event
1051 switch (item_type) {
1053 clicked_regionview->add_gain_point_event (item, event);
1057 case AutomationTrackItem:
1058 dynamic_cast<AutomationTimeAxisView*>(clicked_trackview)->
1059 add_automation_event (item, event, where, event->button.y);
1068 switch (item_type) {
1070 audition_selected_region ();
1087 switch (mouse_mode) {
1090 switch (item_type) {
1092 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
1094 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::Shift|Keyboard::Alt))) {
1097 // Button2 click is unused
1110 // x_style_paste (where, 1.0);
1130 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1136 switch (item_type) {
1137 case GainControlPointItem:
1138 if (mouse_mode == MouseGain) {
1139 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1140 cp->set_visible (true);
1144 at_y = cp->get_y ();
1145 cp->item->i2w (at_x, at_y);
1149 fraction = 1.0 - (cp->get_y() / cp->line.height());
1151 set_verbose_canvas_cursor (cp->line.get_verbose_cursor_string (fraction), at_x, at_y);
1152 show_verbose_canvas_cursor ();
1154 if (is_drawable()) {
1155 track_canvas.get_window()->set_cursor (*fader_cursor);
1160 case GainAutomationControlPointItem:
1161 case PanAutomationControlPointItem:
1162 case RedirectAutomationControlPointItem:
1163 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1164 cp->set_visible (true);
1168 at_y = cp->get_y ();
1169 cp->item->i2w (at_x, at_y);
1173 fraction = 1.0 - (cp->get_y() / cp->line.height());
1175 set_verbose_canvas_cursor (cp->line.get_verbose_cursor_string (fraction), at_x, at_y);
1176 show_verbose_canvas_cursor ();
1178 if (is_drawable()) {
1179 track_canvas.get_window()->set_cursor (*fader_cursor);
1184 if (mouse_mode == MouseGain) {
1185 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1187 line->property_fill_color_rgba() = color_map[cEnteredGainLine];
1188 if (is_drawable()) {
1189 track_canvas.get_window()->set_cursor (*fader_cursor);
1194 case GainAutomationLineItem:
1195 case RedirectAutomationLineItem:
1196 case PanAutomationLineItem:
1198 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1200 line->property_fill_color_rgba() = color_map[cEnteredAutomationLine];
1202 if (is_drawable()) {
1203 track_canvas.get_window()->set_cursor (*fader_cursor);
1207 case AudioRegionViewNameHighlight:
1208 if (is_drawable() && mouse_mode == MouseObject) {
1209 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1213 case StartSelectionTrimItem:
1214 case EndSelectionTrimItem:
1215 /* <CMT Additions> */
1216 case ImageFrameHandleStartItem:
1217 case ImageFrameHandleEndItem:
1218 case MarkerViewHandleStartItem:
1219 case MarkerViewHandleEndItem:
1220 /* </CMT Additions> */
1222 if (is_drawable()) {
1223 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1227 case EditCursorItem:
1228 case PlayheadCursorItem:
1229 if (is_drawable()) {
1230 track_canvas.get_window()->set_cursor (*grabber_cursor);
1234 case AudioRegionViewName:
1236 /* when the name is not an active item, the entire name highlight is for trimming */
1238 if (!reinterpret_cast<AudioRegionView *> (item->get_data ("regionview"))->name_active()) {
1239 if (mouse_mode == MouseObject && is_drawable()) {
1240 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1246 case AutomationTrackItem:
1247 if (is_drawable()) {
1248 Gdk::Cursor *cursor;
1249 switch (mouse_mode) {
1251 cursor = selector_cursor;
1254 cursor = zoom_cursor;
1257 cursor = cross_hair_cursor;
1261 track_canvas.get_window()->set_cursor (*cursor);
1263 AutomationTimeAxisView* atv;
1264 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1265 clear_entered_track = false;
1266 set_entered_track (atv);
1272 case RangeMarkerBarItem:
1273 case TransportMarkerBarItem:
1276 if (is_drawable()) {
1277 time_canvas.get_window()->set_cursor (*timebar_cursor);
1282 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1285 marker->set_color_rgba (color_map[cEnteredMarker]);
1287 case MeterMarkerItem:
1288 case TempoMarkerItem:
1289 if (is_drawable()) {
1290 time_canvas.get_window()->set_cursor (*timebar_cursor);
1293 case FadeInHandleItem:
1294 case FadeOutHandleItem:
1295 if (mouse_mode == MouseObject) {
1296 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1298 rect->property_fill_color_rgba() = 0;
1299 rect->property_outline_pixels() = 1;
1308 /* second pass to handle entered track status in a comprehensible way.
1311 switch (item_type) {
1313 case GainAutomationLineItem:
1314 case RedirectAutomationLineItem:
1315 case PanAutomationLineItem:
1316 case GainControlPointItem:
1317 case GainAutomationControlPointItem:
1318 case PanAutomationControlPointItem:
1319 case RedirectAutomationControlPointItem:
1320 /* these do not affect the current entered track state */
1321 clear_entered_track = false;
1324 case AutomationTrackItem:
1325 /* handled above already */
1329 set_entered_track (0);
1337 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1343 AudioRegionView* rv;
1346 switch (item_type) {
1347 case GainControlPointItem:
1348 case GainAutomationControlPointItem:
1349 case PanAutomationControlPointItem:
1350 case RedirectAutomationControlPointItem:
1351 cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
1352 if (cp->line.npoints() > 1) {
1353 if (!cp->selected) {
1354 cp->set_visible (false);
1358 if (is_drawable()) {
1359 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1362 hide_verbose_canvas_cursor ();
1365 case AudioRegionViewNameHighlight:
1366 case StartSelectionTrimItem:
1367 case EndSelectionTrimItem:
1368 case EditCursorItem:
1369 case PlayheadCursorItem:
1370 /* <CMT Additions> */
1371 case ImageFrameHandleStartItem:
1372 case ImageFrameHandleEndItem:
1373 case MarkerViewHandleStartItem:
1374 case MarkerViewHandleEndItem:
1375 /* </CMT Additions> */
1376 if (is_drawable()) {
1377 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1382 case GainAutomationLineItem:
1383 case RedirectAutomationLineItem:
1384 case PanAutomationLineItem:
1385 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1387 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1389 line->property_fill_color_rgba() = al->get_line_color();
1391 if (is_drawable()) {
1392 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1396 case AudioRegionViewName:
1397 /* see enter_handler() for notes */
1398 if (!reinterpret_cast<AudioRegionView *> (item->get_data ("regionview"))->name_active()) {
1399 if (is_drawable() && mouse_mode == MouseObject) {
1400 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1405 case RangeMarkerBarItem:
1406 case TransportMarkerBarItem:
1410 if (is_drawable()) {
1411 time_canvas.get_window()->set_cursor (*timebar_cursor);
1416 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1419 loc = find_location_from_marker (marker, is_start);
1420 if (loc) location_flags_changed (loc, this);
1422 case MeterMarkerItem:
1423 case TempoMarkerItem:
1425 if (is_drawable()) {
1426 time_canvas.get_window()->set_cursor (*timebar_cursor);
1431 case FadeInHandleItem:
1432 case FadeOutHandleItem:
1433 rv = static_cast<AudioRegionView*>(item->get_data ("regionview"));
1435 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1437 rect->property_fill_color_rgba() = rv->get_fill_color();
1438 rect->property_outline_pixels() = 0;
1443 case AutomationTrackItem:
1444 if (is_drawable()) {
1445 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1446 clear_entered_track = true;
1447 Glib::signal_idle().connect (mem_fun(*this, &Editor::left_automation_track));
1459 Editor::left_automation_track ()
1461 if (clear_entered_track) {
1462 set_entered_track (0);
1463 clear_entered_track = false;
1469 Editor::motion_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type, bool from_autoscroll)
1473 /* We call this so that MOTION_NOTIFY events continue to be
1474 delivered to the canvas. We need to do this because we set
1475 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
1476 the density of the events, at the expense of a round-trip
1477 to the server. Given that this will mostly occur on cases
1478 where DISPLAY = :0.0, and given the cost of what the motion
1479 event might do, its a good tradeoff.
1482 track_canvas.get_pointer (x, y);
1484 if (current_stepping_trackview) {
1485 /* don't keep the persistent stepped trackview if the mouse moves */
1486 current_stepping_trackview = 0;
1487 step_timeout.disconnect ();
1490 if (session && session->actively_recording()) {
1491 /* Sorry. no dragging stuff around while we record */
1495 drag_info.item_type = item_type;
1496 drag_info.current_pointer_frame = event_frame (event, &drag_info.current_pointer_x,
1497 &drag_info.current_pointer_y);
1499 if (!from_autoscroll && drag_info.item) {
1500 /* item != 0 is the best test i can think of for dragging.
1502 if (!drag_info.move_threshold_passed) {
1504 drag_info.move_threshold_passed = (abs ((int) (drag_info.current_pointer_x - drag_info.grab_x)) > 4);
1506 // and change the initial grab loc/frame if this drag info wants us to
1508 if (drag_info.want_move_threshold && drag_info.move_threshold_passed) {
1509 drag_info.grab_frame = drag_info.current_pointer_frame;
1510 drag_info.grab_x = drag_info.current_pointer_x;
1511 drag_info.grab_y = drag_info.current_pointer_y;
1512 drag_info.last_pointer_frame = drag_info.grab_frame;
1513 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
1518 switch (item_type) {
1519 case PlayheadCursorItem:
1520 case EditCursorItem:
1522 case GainControlPointItem:
1523 case RedirectAutomationControlPointItem:
1524 case GainAutomationControlPointItem:
1525 case PanAutomationControlPointItem:
1526 case TempoMarkerItem:
1527 case MeterMarkerItem:
1528 case AudioRegionViewNameHighlight:
1529 case StartSelectionTrimItem:
1530 case EndSelectionTrimItem:
1533 case RedirectAutomationLineItem:
1534 case GainAutomationLineItem:
1535 case PanAutomationLineItem:
1536 case FadeInHandleItem:
1537 case FadeOutHandleItem:
1538 /* <CMT Additions> */
1539 case ImageFrameHandleStartItem:
1540 case ImageFrameHandleEndItem:
1541 case MarkerViewHandleStartItem:
1542 case MarkerViewHandleEndItem:
1543 /* </CMT Additions> */
1544 if (drag_info.item && (event->motion.state & Gdk::BUTTON1_MASK ||
1545 (event->motion.state & Gdk::BUTTON2_MASK))) {
1546 if (!from_autoscroll) {
1547 maybe_autoscroll (event);
1549 (this->*(drag_info.motion_callback)) (item, event);
1558 switch (mouse_mode) {
1563 if (drag_info.item && (event->motion.state & GDK_BUTTON1_MASK ||
1564 (event->motion.state & GDK_BUTTON2_MASK))) {
1565 if (!from_autoscroll) {
1566 maybe_autoscroll (event);
1568 (this->*(drag_info.motion_callback)) (item, event);
1579 track_canvas_motion (event);
1580 // drag_info.last_pointer_frame = drag_info.current_pointer_frame;
1588 Editor::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
1590 if (drag_info.item == 0) {
1591 fatal << _("programming error: start_grab called without drag item") << endmsg;
1597 cursor = grabber_cursor;
1600 // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
1602 if (event->button.button == 2) {
1603 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Alt)) {
1604 drag_info.y_constrained = true;
1605 drag_info.x_constrained = false;
1607 drag_info.y_constrained = false;
1608 drag_info.x_constrained = true;
1611 drag_info.x_constrained = false;
1612 drag_info.y_constrained = false;
1615 drag_info.grab_frame = event_frame(event, &drag_info.grab_x, &drag_info.grab_y);
1616 drag_info.last_pointer_frame = drag_info.grab_frame;
1617 drag_info.current_pointer_frame = drag_info.grab_frame;
1618 drag_info.current_pointer_x = drag_info.grab_x;
1619 drag_info.current_pointer_y = drag_info.grab_y;
1620 drag_info.cumulative_x_drag = 0;
1621 drag_info.cumulative_y_drag = 0;
1622 drag_info.first_move = true;
1623 drag_info.move_threshold_passed = false;
1624 drag_info.want_move_threshold = false;
1625 drag_info.pointer_frame_offset = 0;
1626 drag_info.brushing = false;
1627 drag_info.copied_location = 0;
1629 drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK,
1631 event->button.time);
1633 if (session && session->transport_rolling()) {
1634 drag_info.was_rolling = true;
1636 drag_info.was_rolling = false;
1639 switch (snap_type) {
1640 case SnapToRegionStart:
1641 case SnapToRegionEnd:
1642 case SnapToRegionSync:
1643 case SnapToRegionBoundary:
1644 build_region_boundary_cache ();
1652 Editor::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t time)
1654 drag_info.item->ungrab (0);
1655 drag_info.item = new_item;
1658 cursor = grabber_cursor;
1661 drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK, *cursor, time);
1665 Editor::end_grab (ArdourCanvas::Item* item, GdkEvent* event)
1667 bool did_drag = false;
1669 stop_canvas_autoscroll ();
1671 if (drag_info.item == 0) {
1675 drag_info.item->ungrab (event->button.time);
1677 if (drag_info.finished_callback) {
1678 (this->*(drag_info.finished_callback)) (item, event);
1681 did_drag = !drag_info.first_move;
1683 hide_verbose_canvas_cursor();
1686 drag_info.copy = false;
1687 drag_info.motion_callback = 0;
1688 drag_info.finished_callback = 0;
1689 drag_info.last_trackview = 0;
1690 drag_info.last_frame_position = 0;
1691 drag_info.grab_frame = 0;
1692 drag_info.last_pointer_frame = 0;
1693 drag_info.current_pointer_frame = 0;
1694 drag_info.brushing = false;
1696 if (drag_info.copied_location) {
1697 delete drag_info.copied_location;
1698 drag_info.copied_location = 0;
1705 Editor::set_edit_cursor (GdkEvent* event)
1707 jack_nframes_t pointer_frame = event_frame (event);
1709 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1710 if (snap_type != SnapToEditCursor) {
1711 snap_to (pointer_frame);
1715 edit_cursor->set_position (pointer_frame);
1716 edit_cursor_clock.set (pointer_frame);
1720 Editor::set_playhead_cursor (GdkEvent* event)
1722 jack_nframes_t pointer_frame = event_frame (event);
1724 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1725 snap_to (pointer_frame);
1729 session->request_locate (pointer_frame, session->transport_rolling());
1734 Editor::start_fade_in_grab (ArdourCanvas::Item* item, GdkEvent* event)
1736 drag_info.item = item;
1737 drag_info.motion_callback = &Editor::fade_in_drag_motion_callback;
1738 drag_info.finished_callback = &Editor::fade_in_drag_finished_callback;
1742 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
1743 fatal << _("programming error: fade in canvas item has no regionview data pointer!") << endmsg;
1747 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1749 drag_info.pointer_frame_offset = drag_info.grab_frame - ((jack_nframes_t) arv->region.fade_in().back()->when + arv->region.position());
1753 Editor::fade_in_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1755 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1757 jack_nframes_t fade_length;
1759 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1760 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1766 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1770 if (pos < (arv->region.position() + 64)) {
1771 fade_length = 64; // this should be a minimum defined somewhere
1772 } else if (pos > arv->region.last_frame()) {
1773 fade_length = arv->region.length();
1775 fade_length = pos - arv->region.position();
1778 arv->reset_fade_in_shape_width (fade_length);
1780 show_verbose_duration_cursor (arv->region.position(), arv->region.position() + fade_length, 10);
1782 drag_info.first_move = false;
1786 Editor::fade_in_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1788 if (drag_info.first_move) return;
1790 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1792 jack_nframes_t fade_length;
1794 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1795 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1801 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1805 if (pos < (arv->region.position() + 64)) {
1806 fade_length = 64; // this should be a minimum defined somewhere
1808 else if (pos > arv->region.last_frame()) {
1809 fade_length = arv->region.length();
1812 fade_length = pos - arv->region.position();
1815 begin_reversible_command (_("change fade in length"));
1816 XMLNode &before = arv->region.get_state();
1818 arv->region.set_fade_in_length (fade_length);
1820 XMLNode &after = arv->region.get_state();
1821 session->add_command(new 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 = arv->region.get_state();
1915 arv->region.set_fade_out_length (fade_length);
1917 XMLNode &after = arv->region.get_state();
1918 session->add_command(new MementoCommand<ARDOUR::AudioRegion>(arv->region, before, after));
1919 commit_reversible_command ();
1921 fade_out_drag_motion_callback (item, event);
1925 Editor::start_cursor_grab (ArdourCanvas::Item* item, GdkEvent* event)
1927 drag_info.item = item;
1928 drag_info.motion_callback = &Editor::cursor_drag_motion_callback;
1929 drag_info.finished_callback = &Editor::cursor_drag_finished_callback;
1933 if ((drag_info.data = (item->get_data ("cursor"))) == 0) {
1934 fatal << _("programming error: cursor canvas item has no cursor data pointer!") << endmsg;
1938 Cursor* cursor = (Cursor *) drag_info.data;
1940 if (session && cursor == playhead_cursor) {
1941 if (drag_info.was_rolling) {
1942 session->request_stop ();
1946 drag_info.pointer_frame_offset = drag_info.grab_frame - cursor->current_frame;
1948 show_verbose_time_cursor (cursor->current_frame, 10);
1952 Editor::cursor_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1954 Cursor* cursor = (Cursor *) drag_info.data;
1955 jack_nframes_t adjusted_frame;
1957 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1958 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1964 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1965 if (cursor != edit_cursor || snap_type != SnapToEditCursor) {
1966 snap_to (adjusted_frame);
1970 if (adjusted_frame == drag_info.last_pointer_frame) return;
1972 cursor->set_position (adjusted_frame);
1974 if (cursor == edit_cursor) {
1975 edit_cursor_clock.set (cursor->current_frame);
1978 show_verbose_time_cursor (cursor->current_frame, 10);
1980 drag_info.last_pointer_frame = adjusted_frame;
1981 drag_info.first_move = false;
1985 Editor::cursor_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1987 if (drag_info.first_move) return;
1989 cursor_drag_motion_callback (item, event);
1991 if (item == &playhead_cursor->canvas_item) {
1993 session->request_locate (playhead_cursor->current_frame, drag_info.was_rolling);
1995 } else if (item == &edit_cursor->canvas_item) {
1996 edit_cursor->set_position (edit_cursor->current_frame);
1997 edit_cursor_clock.set (edit_cursor->current_frame);
2002 Editor::update_marker_drag_item (Location *location)
2004 double x1 = frame_to_pixel (location->start());
2005 double x2 = frame_to_pixel (location->end());
2007 if (location->is_mark()) {
2008 marker_drag_line_points.front().set_x(x1);
2009 marker_drag_line_points.back().set_x(x1);
2010 marker_drag_line->property_points() = marker_drag_line_points;
2013 range_marker_drag_rect->property_x1() = x1;
2014 range_marker_drag_rect->property_x2() = x2;
2019 Editor::start_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2023 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2024 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2030 Location *location = find_location_from_marker (marker, is_start);
2032 drag_info.item = item;
2033 drag_info.data = marker;
2034 drag_info.motion_callback = &Editor::marker_drag_motion_callback;
2035 drag_info.finished_callback = &Editor::marker_drag_finished_callback;
2039 drag_info.copied_location = new Location (*location);
2040 drag_info.pointer_frame_offset = drag_info.grab_frame - (is_start ? location->start() : location->end());
2042 update_marker_drag_item (location);
2044 if (location->is_mark()) {
2045 marker_drag_line->show();
2046 marker_drag_line->raise_to_top();
2049 range_marker_drag_rect->show();
2050 range_marker_drag_rect->raise_to_top();
2053 if (is_start) show_verbose_time_cursor (location->start(), 10);
2054 else show_verbose_time_cursor (location->end(), 10);
2058 Editor::marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2060 jack_nframes_t f_delta;
2061 Marker* marker = (Marker *) drag_info.data;
2062 Location *real_location;
2063 Location *copy_location;
2065 bool move_both = false;
2068 jack_nframes_t newframe;
2069 if (drag_info.pointer_frame_offset <= (long) drag_info.current_pointer_frame) {
2070 newframe = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2076 jack_nframes_t next = newframe;
2078 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2079 snap_to (newframe, 0, true);
2082 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
2086 /* call this to find out if its the start or end */
2088 real_location = find_location_from_marker (marker, is_start);
2090 /* use the copy that we're "dragging" around */
2092 copy_location = drag_info.copied_location;
2094 f_delta = copy_location->end() - copy_location->start();
2096 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
2100 if (is_start) { // start marker
2103 copy_location->set_start (newframe);
2104 copy_location->set_end (newframe + f_delta);
2105 } else if (newframe < copy_location->end()) {
2106 copy_location->set_start (newframe);
2108 snap_to (next, 1, true);
2109 copy_location->set_end (next);
2110 copy_location->set_start (newframe);
2113 } else { // end marker
2116 copy_location->set_end (newframe);
2117 copy_location->set_start (newframe - f_delta);
2118 } else if (newframe > copy_location->start()) {
2119 copy_location->set_end (newframe);
2121 } else if (newframe > 0) {
2122 snap_to (next, -1, true);
2123 copy_location->set_start (next);
2124 copy_location->set_end (newframe);
2128 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
2129 drag_info.first_move = false;
2131 update_marker_drag_item (copy_location);
2133 LocationMarkers* lm = find_location_markers (real_location);
2134 lm->set_position (copy_location->start(), copy_location->end());
2136 show_verbose_time_cursor (newframe, 10);
2140 Editor::marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2142 if (drag_info.first_move) {
2143 marker_drag_motion_callback (item, event);
2147 Marker* marker = (Marker *) drag_info.data;
2151 begin_reversible_command ( _("move marker") );
2152 XMLNode &before = session->locations()->get_state();
2154 Location * location = find_location_from_marker (marker, is_start);
2157 location->set (drag_info.copied_location->start(), drag_info.copied_location->end());
2160 XMLNode &after = session->locations()->get_state();
2161 session->add_command(new MementoCommand<Locations>(*(session->locations()), before, after));
2162 commit_reversible_command ();
2164 marker_drag_line->hide();
2165 range_marker_drag_rect->hide();
2169 Editor::start_meter_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2172 MeterMarker* meter_marker;
2174 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2175 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2179 meter_marker = dynamic_cast<MeterMarker*> (marker);
2181 MetricSection& section (meter_marker->meter());
2183 if (!section.movable()) {
2187 drag_info.item = item;
2188 drag_info.data = marker;
2189 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2190 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2194 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2196 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2200 Editor::start_meter_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2203 MeterMarker* meter_marker;
2205 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2206 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2210 meter_marker = dynamic_cast<MeterMarker*> (marker);
2212 // create a dummy marker for visual representation of moving the copy.
2213 // The actual copying is not done before we reach the finish callback.
2215 snprintf (name, sizeof(name), "%g/%g", meter_marker->meter().beats_per_bar(), meter_marker->meter().note_divisor ());
2216 MeterMarker* new_marker = new MeterMarker(*this, *meter_group, color_map[cMeterMarker], name,
2217 *new MeterSection(meter_marker->meter()));
2219 drag_info.item = &new_marker->the_item();
2220 drag_info.copy = true;
2221 drag_info.data = new_marker;
2222 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2223 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2227 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2229 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2233 Editor::meter_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2235 MeterMarker* marker = (MeterMarker *) drag_info.data;
2236 jack_nframes_t adjusted_frame;
2238 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2239 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2245 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2246 snap_to (adjusted_frame);
2249 if (adjusted_frame == drag_info.last_pointer_frame) return;
2251 marker->set_position (adjusted_frame);
2254 drag_info.last_pointer_frame = adjusted_frame;
2255 drag_info.first_move = false;
2257 show_verbose_time_cursor (adjusted_frame, 10);
2261 Editor::meter_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2263 if (drag_info.first_move) return;
2265 meter_marker_drag_motion_callback (drag_info.item, event);
2267 MeterMarker* marker = (MeterMarker *) drag_info.data;
2270 TempoMap& map (session->tempo_map());
2271 map.bbt_time (drag_info.last_pointer_frame, when);
2273 if (drag_info.copy == true) {
2274 begin_reversible_command (_("copy meter mark"));
2275 XMLNode &before = map.get_state();
2276 map.add_meter (marker->meter(), when);
2277 XMLNode &after = map.get_state();
2278 session->add_command(new MementoCommand<TempoMap>(map, before, after));
2279 commit_reversible_command ();
2281 // delete the dummy marker we used for visual representation of copying.
2282 // a new visual marker will show up automatically.
2285 begin_reversible_command (_("move meter mark"));
2286 XMLNode &before = map.get_state();
2287 map.move_meter (marker->meter(), when);
2288 XMLNode &after = map.get_state();
2289 session->add_command(new MementoCommand<TempoMap>(map, before, after));
2290 commit_reversible_command ();
2295 Editor::start_tempo_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2298 TempoMarker* tempo_marker;
2300 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2301 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2305 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2306 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2310 MetricSection& section (tempo_marker->tempo());
2312 if (!section.movable()) {
2316 drag_info.item = item;
2317 drag_info.data = marker;
2318 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2319 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2323 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2324 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2328 Editor::start_tempo_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2331 TempoMarker* tempo_marker;
2333 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2334 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2338 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2339 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2343 // create a dummy marker for visual representation of moving the copy.
2344 // The actual copying is not done before we reach the finish callback.
2346 snprintf (name, sizeof (name), "%.2f", tempo_marker->tempo().beats_per_minute());
2347 TempoMarker* new_marker = new TempoMarker(*this, *tempo_group, color_map[cTempoMarker], name,
2348 *new TempoSection(tempo_marker->tempo()));
2350 drag_info.item = &new_marker->the_item();
2351 drag_info.copy = true;
2352 drag_info.data = new_marker;
2353 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2354 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2358 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2360 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2364 Editor::tempo_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2366 TempoMarker* marker = (TempoMarker *) drag_info.data;
2367 jack_nframes_t adjusted_frame;
2369 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2370 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2376 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2377 snap_to (adjusted_frame);
2380 if (adjusted_frame == drag_info.last_pointer_frame) return;
2382 /* OK, we've moved far enough to make it worth actually move the thing. */
2384 marker->set_position (adjusted_frame);
2386 show_verbose_time_cursor (adjusted_frame, 10);
2388 drag_info.last_pointer_frame = adjusted_frame;
2389 drag_info.first_move = false;
2393 Editor::tempo_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2395 if (drag_info.first_move) return;
2397 tempo_marker_drag_motion_callback (drag_info.item, event);
2399 TempoMarker* marker = (TempoMarker *) drag_info.data;
2402 TempoMap& map (session->tempo_map());
2403 map.bbt_time (drag_info.last_pointer_frame, when);
2405 if (drag_info.copy == true) {
2406 begin_reversible_command (_("copy tempo mark"));
2407 XMLNode &before = map.get_state();
2408 map.add_tempo (marker->tempo(), when);
2409 XMLNode &after = map.get_state();
2410 session->add_command (new MementoCommand<TempoMap>(map, before, after));
2411 commit_reversible_command ();
2413 // delete the dummy marker we used for visual representation of copying.
2414 // a new visual marker will show up automatically.
2417 begin_reversible_command (_("move tempo mark"));
2418 XMLNode &before = map.get_state();
2419 map.move_tempo (marker->tempo(), when);
2420 XMLNode &after = map.get_state();
2421 session->add_command (new MementoCommand<TempoMap>(map, before, after));
2422 commit_reversible_command ();
2427 Editor::remove_gain_control_point (ArdourCanvas::Item*item, GdkEvent* event)
2429 ControlPoint* control_point;
2431 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2432 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2436 // We shouldn't remove the first or last gain point
2437 if (control_point->line.is_last_point(*control_point) ||
2438 control_point->line.is_first_point(*control_point)) {
2442 control_point->line.remove_point (*control_point);
2446 Editor::remove_control_point (ArdourCanvas::Item*item, GdkEvent* event)
2448 ControlPoint* control_point;
2450 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2451 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2455 control_point->line.remove_point (*control_point);
2459 Editor::start_control_point_grab (ArdourCanvas::Item* item, GdkEvent* event)
2461 ControlPoint* control_point;
2463 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2464 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2468 drag_info.item = item;
2469 drag_info.data = control_point;
2470 drag_info.motion_callback = &Editor::control_point_drag_motion_callback;
2471 drag_info.finished_callback = &Editor::control_point_drag_finished_callback;
2473 start_grab (event, fader_cursor);
2475 control_point->line.start_drag (control_point, 0);
2477 float fraction = 1.0 - (control_point->get_y() / control_point->line.height());
2478 set_verbose_canvas_cursor (control_point->line.get_verbose_cursor_string (fraction),
2479 drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
2481 show_verbose_canvas_cursor ();
2485 Editor::control_point_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2487 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2489 double cx = drag_info.current_pointer_x;
2490 double cy = drag_info.current_pointer_y;
2492 drag_info.cumulative_x_drag = cx - drag_info.grab_x ;
2493 drag_info.cumulative_y_drag = cy - drag_info.grab_y ;
2495 if (drag_info.x_constrained) {
2496 cx = drag_info.grab_x;
2498 if (drag_info.y_constrained) {
2499 cy = drag_info.grab_y;
2502 cp->line.parent_group().w2i (cx, cy);
2506 cy = min ((double) cp->line.height(), cy);
2508 //translate cx to frames
2509 jack_nframes_t cx_frames = unit_to_frame (cx);
2511 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier()) && !drag_info.x_constrained) {
2512 snap_to (cx_frames);
2515 float fraction = 1.0 - (cy / cp->line.height());
2519 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
2525 cp->line.point_drag (*cp, cx_frames , fraction, push);
2527 set_verbose_canvas_cursor_text (cp->line.get_verbose_cursor_string (fraction));
2529 drag_info.first_move = false;
2533 Editor::control_point_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2535 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2537 if (drag_info.first_move) {
2541 if ((event->type == GDK_BUTTON_RELEASE) && (event->button.button == 1) && Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
2542 reset_point_selection ();
2546 control_point_drag_motion_callback (item, event);
2548 cp->line.end_drag (cp);
2552 Editor::start_line_grab_from_regionview (ArdourCanvas::Item* item, GdkEvent* event)
2554 switch (mouse_mode) {
2556 start_line_grab (clicked_regionview->get_gain_line(), event);
2564 Editor::start_line_grab_from_line (ArdourCanvas::Item* item, GdkEvent* event)
2568 if ((al = reinterpret_cast<AutomationLine*> (item->get_data ("line"))) == 0) {
2569 fatal << _("programming error: line canvas item has no line pointer!") << endmsg;
2573 start_line_grab (al, event);
2577 Editor::start_line_grab (AutomationLine* line, GdkEvent* event)
2581 jack_nframes_t frame_within_region;
2583 /* need to get x coordinate in terms of parent (TimeAxisItemView)
2587 cx = event->button.x;
2588 cy = event->button.y;
2589 line->parent_group().w2i (cx, cy);
2590 frame_within_region = (jack_nframes_t) floor (cx * frames_per_unit);
2592 if (!line->control_points_adjacent (frame_within_region, current_line_drag_info.before,
2593 current_line_drag_info.after)) {
2594 /* no adjacent points */
2598 drag_info.item = &line->grab_item();
2599 drag_info.data = line;
2600 drag_info.motion_callback = &Editor::line_drag_motion_callback;
2601 drag_info.finished_callback = &Editor::line_drag_finished_callback;
2603 start_grab (event, fader_cursor);
2605 double fraction = 1.0 - (cy / line->height());
2607 line->start_drag (0, fraction);
2609 set_verbose_canvas_cursor (line->get_verbose_cursor_string (fraction),
2610 drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
2611 show_verbose_canvas_cursor ();
2615 Editor::line_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2617 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
2618 double cx = drag_info.current_pointer_x;
2619 double cy = drag_info.current_pointer_y;
2621 line->parent_group().w2i (cx, cy);
2624 fraction = 1.0 - (cy / line->height());
2628 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
2634 line->line_drag (current_line_drag_info.before, current_line_drag_info.after, fraction, push);
2636 set_verbose_canvas_cursor_text (line->get_verbose_cursor_string (fraction));
2640 Editor::line_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2642 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
2643 line_drag_motion_callback (item, event);
2648 Editor::start_region_grab (ArdourCanvas::Item* item, GdkEvent* event)
2650 if (selection->audio_regions.empty() || clicked_regionview == 0) {
2654 drag_info.copy = false;
2655 drag_info.item = item;
2656 drag_info.data = clicked_regionview;
2657 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2658 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2663 TimeAxisView* tvp = clicked_trackview;
2664 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
2666 if (tv && tv->is_audio_track()) {
2667 speed = tv->get_diskstream()->speed();
2670 drag_info.last_frame_position = (jack_nframes_t) (clicked_regionview->region.position() / speed);
2671 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2672 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2673 // we want a move threshold
2674 drag_info.want_move_threshold = true;
2676 show_verbose_time_cursor (drag_info.last_frame_position, 10);
2678 begin_reversible_command (_("move region(s)"));
2682 Editor::start_region_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2684 if (selection->audio_regions.empty() || clicked_regionview == 0) {
2688 drag_info.copy = true;
2689 drag_info.item = item;
2690 drag_info.data = clicked_regionview;
2694 TimeAxisView* tv = &clicked_regionview->get_time_axis_view();
2695 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*>(tv);
2698 if (atv && atv->is_audio_track()) {
2699 speed = atv->get_diskstream()->speed();
2702 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2703 drag_info.last_frame_position = (jack_nframes_t) (clicked_regionview->region.position() / speed);
2704 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2705 // we want a move threshold
2706 drag_info.want_move_threshold = true;
2707 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2708 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2712 Editor::start_region_brush_grab (ArdourCanvas::Item* item, GdkEvent* event)
2714 if (selection->audio_regions.empty() || clicked_regionview == 0) {
2718 drag_info.copy = false;
2719 drag_info.item = item;
2720 drag_info.data = clicked_regionview;
2721 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2722 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2727 TimeAxisView* tvp = clicked_trackview;
2728 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
2730 if (tv && tv->is_audio_track()) {
2731 speed = tv->get_diskstream()->speed();
2734 drag_info.last_frame_position = (jack_nframes_t) (clicked_regionview->region.position() / speed);
2735 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2736 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2737 // we want a move threshold
2738 drag_info.want_move_threshold = true;
2739 drag_info.brushing = true;
2741 begin_reversible_command (_("Drag region brush"));
2745 Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2749 AudioRegionView *rv = reinterpret_cast<AudioRegionView*> (drag_info.data);
2750 jack_nframes_t pending_region_position = 0;
2751 int32_t pointer_y_span = 0, canvas_pointer_y_span = 0, original_pointer_order;
2752 int32_t visible_y_high = 0, visible_y_low = 512; //high meaning higher numbered.. not the height on the screen
2753 bool clamp_y_axis = false;
2754 vector<int32_t> height_list(512) ;
2755 vector<int32_t>::iterator j;
2757 show_verbose_time_cursor (drag_info.last_frame_position, 10);
2759 if (drag_info.copy && drag_info.move_threshold_passed && drag_info.want_move_threshold) {
2761 drag_info.want_move_threshold = false; // don't copy again
2763 /* this is committed in the grab finished callback. */
2765 begin_reversible_command (_("Drag region copy"));
2767 /* duplicate the region(s) */
2769 vector<AudioRegionView*> new_regionviews;
2771 set<Playlist*> affected_playlists;
2772 pair<set<Playlist*>::iterator,bool> insert_result;
2774 for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin(); i != selection->audio_regions.by_layer().end(); ++i) {
2775 AudioRegionView* rv;
2779 Playlist* to_playlist = rv->region.playlist();
2780 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*>(&rv->get_time_axis_view());
2782 insert_result = affected_playlists.insert (to_playlist);
2783 if (insert_result.second) {
2784 session->add_command (new MementoUndoCommand<Playlist>(*to_playlist, to_playlist->get_state()));
2787 latest_regionview = 0;
2789 sigc::connection c = atv->view->AudioRegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
2791 /* create a new region with the same name.
2794 AudioRegion* newregion = new AudioRegion (rv->region);
2796 /* if the original region was locked, we don't care */
2798 newregion->set_locked (false);
2800 to_playlist->add_region (*newregion, (jack_nframes_t) (rv->region.position() * atv->get_diskstream()->speed()));
2804 if (latest_regionview) {
2805 new_regionviews.push_back (latest_regionview);
2809 if (new_regionviews.empty()) {
2813 /* reset selection to new regionviews */
2815 selection->set (new_regionviews);
2817 /* reset drag_info data to reflect the fact that we are dragging the copies */
2819 drag_info.data = new_regionviews.front();
2820 swap_grab (new_regionviews.front()->get_canvas_group (), 0, event->motion.time);
2823 /* Which trackview is this ? */
2825 TimeAxisView* tvp = trackview_by_y_position (drag_info.current_pointer_y);
2826 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
2828 /* The region motion is only processed if the pointer is over
2832 if (!tv || !tv->is_audio_track()) {
2833 /* To make sure we hide the verbose canvas cursor when the mouse is
2834 not held over and audiotrack.
2836 hide_verbose_canvas_cursor ();
2840 original_pointer_order = drag_info.last_trackview->order;
2842 /************************************************************
2844 ************************************************************/
2846 if (drag_info.brushing) {
2847 clamp_y_axis = true;
2852 if ((pointer_y_span = (drag_info.last_trackview->order - tv->order)) != 0) {
2854 int32_t children = 0, numtracks = 0;
2855 // XXX hard coding track limit, oh my, so very very bad
2856 bitset <1024> tracks (0x00);
2857 /* get a bitmask representing the visible tracks */
2859 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2860 TimeAxisView *tracklist_timeview;
2861 tracklist_timeview = (*i);
2862 AudioTimeAxisView* atv2 = dynamic_cast<AudioTimeAxisView*>(tracklist_timeview);
2863 list<TimeAxisView*> children_list;
2865 /* zeroes are audio tracks. ones are other types. */
2867 if (!atv2->hidden()) {
2869 if (visible_y_high < atv2->order) {
2870 visible_y_high = atv2->order;
2872 if (visible_y_low > atv2->order) {
2873 visible_y_low = atv2->order;
2876 if (!atv2->is_audio_track()) {
2877 tracks = tracks |= (0x01 << atv2->order);
2880 height_list[atv2->order] = (*i)->height;
2882 if ((children_list = atv2->get_child_list()).size() > 0) {
2883 for (list<TimeAxisView*>::iterator j = children_list.begin(); j != children_list.end(); ++j) {
2884 tracks = tracks |= (0x01 << (atv2->order + children));
2885 height_list[atv2->order + children] = (*j)->height;
2893 /* find the actual span according to the canvas */
2895 canvas_pointer_y_span = pointer_y_span;
2896 if (drag_info.last_trackview->order >= tv->order) {
2898 for (y = tv->order; y < drag_info.last_trackview->order; y++) {
2899 if (height_list[y] == 0 ) {
2900 canvas_pointer_y_span--;
2905 for (y = drag_info.last_trackview->order;y <= tv->order; y++) {
2906 if ( height_list[y] == 0 ) {
2907 canvas_pointer_y_span++;
2912 for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin(); i != selection->audio_regions.by_layer().end(); ++i) {
2913 AudioRegionView* rv2;
2915 double ix1, ix2, iy1, iy2;
2918 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
2919 rv2->get_canvas_group()->i2w (ix1, iy1);
2920 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
2921 AudioTimeAxisView* atv2 = dynamic_cast<AudioTimeAxisView*>(tvp2);
2923 if (atv2->order != original_pointer_order) {
2924 /* this isn't the pointer track */
2926 if (canvas_pointer_y_span > 0) {
2928 /* moving up the canvas */
2929 if ((atv2->order - canvas_pointer_y_span) >= visible_y_low) {
2931 int32_t visible_tracks = 0;
2932 while (visible_tracks < canvas_pointer_y_span ) {
2935 while (height_list[atv2->order - (visible_tracks - n)] == 0) {
2936 /* we're passing through a hidden track */
2941 if (tracks[atv2->order - (canvas_pointer_y_span - n)] != 0x00) {
2942 clamp_y_axis = true;
2946 clamp_y_axis = true;
2949 } else if (canvas_pointer_y_span < 0) {
2951 /*moving down the canvas*/
2953 if ((atv2->order - (canvas_pointer_y_span - n)) <= visible_y_high) { // we will overflow
2956 int32_t visible_tracks = 0;
2958 while (visible_tracks > canvas_pointer_y_span ) {
2961 while (height_list[atv2->order - (visible_tracks - n)] == 0) {
2965 if ( tracks[atv2->order - ( canvas_pointer_y_span - n)] != 0x00) {
2966 clamp_y_axis = true;
2971 clamp_y_axis = true;
2977 /* this is the pointer's track */
2978 if ((atv2->order - pointer_y_span) > visible_y_high) { // we will overflow
2979 clamp_y_axis = true;
2980 } else if ((atv2->order - pointer_y_span) < visible_y_low) { // we will underflow
2981 clamp_y_axis = true;
2989 } else if (drag_info.last_trackview == tv) {
2990 clamp_y_axis = true;
2994 if (!clamp_y_axis) {
2995 drag_info.last_trackview = tv;
2998 /************************************************************
3000 ************************************************************/
3002 /* compute the amount of pointer motion in frames, and where
3003 the region would be if we moved it by that much.
3006 if (drag_info.move_threshold_passed) {
3008 if ((int32_t)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
3010 jack_nframes_t sync_frame;
3011 jack_nframes_t sync_offset;
3014 pending_region_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
3016 sync_offset = rv->region.sync_offset (sync_dir);
3017 sync_frame = rv->region.adjust_to_sync (pending_region_position);
3019 /* we snap if the snap modifier is not enabled.
3022 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3023 snap_to (sync_frame);
3026 if (sync_frame - sync_offset <= sync_frame) {
3027 pending_region_position = sync_frame - (sync_dir*sync_offset);
3029 pending_region_position = 0;
3033 pending_region_position = 0;
3036 if (pending_region_position > max_frames - rv->region.length()) {
3037 pending_region_position = drag_info.last_frame_position;
3040 // printf ("3: pending_region_position= %lu %lu\n", pending_region_position, drag_info.last_frame_position );
3042 if (pending_region_position != drag_info.last_frame_position && !drag_info.x_constrained) {
3044 /* now compute the canvas unit distance we need to move the regiondrag_info.last_trackview->order
3045 to make it appear at the new location.
3048 if (pending_region_position > drag_info.last_frame_position) {
3049 x_delta = ((double) (pending_region_position - drag_info.last_frame_position) / frames_per_unit);
3051 x_delta = -((double) (drag_info.last_frame_position - pending_region_position) / frames_per_unit);
3054 drag_info.last_frame_position = pending_region_position;
3061 /* threshold not passed */
3066 /*************************************************************
3068 ************************************************************/
3070 if (x_delta == 0 && (pointer_y_span == 0)) {
3071 /* haven't reached next snap point, and we're not switching
3072 trackviews. nothing to do.
3078 for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin(); i != selection->audio_regions.by_layer().end(); ++i) {
3080 AudioRegionView* rv2;
3083 /* if any regionview is at zero, we need to know so we can
3084 stop further leftward motion.
3087 double ix1, ix2, iy1, iy2;
3088 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3089 rv2->get_canvas_group()->i2w (ix1, iy1);
3098 /*************************************************************
3100 ************************************************************/
3102 pair<set<Playlist*>::iterator,bool> insert_result;
3103 const list<AudioRegionView*>& layered_regions = selection->audio_regions.by_layer();
3105 for (list<AudioRegionView*>::const_iterator i = layered_regions.begin(); i != layered_regions.end(); ++i) {
3107 AudioRegionView* rv;
3109 double ix1, ix2, iy1, iy2;
3110 int32_t temp_pointer_y_span = pointer_y_span;
3112 /* get item BBox, which will be relative to parent. so we have
3113 to query on a child, then convert to world coordinates using
3117 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3118 rv->get_canvas_group()->i2w (ix1, iy1);
3119 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3120 AudioTimeAxisView* canvas_atv = dynamic_cast<AudioTimeAxisView*>(tvp2);
3121 AudioTimeAxisView* temp_atv;
3123 if ((pointer_y_span != 0) && !clamp_y_axis) {
3126 for (j = height_list.begin(); j!= height_list.end(); j++) {
3127 if (x == canvas_atv->order) {
3128 /* we found the track the region is on */
3129 if (x != original_pointer_order) {
3130 /*this isn't from the same track we're dragging from */
3131 temp_pointer_y_span = canvas_pointer_y_span;
3133 while (temp_pointer_y_span > 0) {
3134 /* we're moving up canvas-wise,
3135 so we need to find the next track height
3137 if (j != height_list.begin()) {
3140 if (x != original_pointer_order) {
3141 /* we're not from the dragged track, so ignore hidden tracks. */
3143 temp_pointer_y_span++;
3147 temp_pointer_y_span--;
3149 while (temp_pointer_y_span < 0) {
3151 if (x != original_pointer_order) {
3153 temp_pointer_y_span--;
3157 if (j != height_list.end()) {
3160 temp_pointer_y_span++;
3162 /* find out where we'll be when we move and set height accordingly */
3164 tvp2 = trackview_by_y_position (iy1 + y_delta);
3165 temp_atv = dynamic_cast<AudioTimeAxisView*>(tvp2);
3166 rv->set_height (temp_atv->height);
3168 /* if you un-comment the following, the region colours will follow the track colours whilst dragging,
3169 personally, i think this can confuse things, but never mind.
3172 //const GdkColor& col (temp_atv->view->get_region_color());
3173 //rv->set_color (const_cast<GdkColor&>(col));
3180 /* prevent the regionview from being moved to before
3181 the zero position on the canvas.
3186 if (-x_delta > ix1) {
3189 } else if ((x_delta > 0) &&(rv->region.last_frame() > max_frames - x_delta)) {
3190 x_delta = max_frames - rv->region.last_frame();
3193 if (drag_info.first_move) {
3195 /* hide any dependent views */
3197 // rv->get_time_axis_view().hide_dependent_views (*rv);
3199 /* this is subtle. raising the regionview itself won't help,
3200 because raise_to_top() just puts the item on the top of
3201 its parent's stack. so, we need to put the trackview canvas_display group
3202 on the top, since its parent is the whole canvas.
3205 rv->get_canvas_group()->raise_to_top();
3206 rv->get_time_axis_view().canvas_display->raise_to_top();
3207 cursor_group->raise_to_top();
3209 /* freeze the playlists from notifying till
3213 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*> (&rv->get_time_axis_view());
3214 if (atv && atv->is_audio_track()) {
3215 AudioPlaylist* pl = atv->get_diskstream()->playlist();
3217 /* only freeze and capture state once */
3219 insert_result = motion_frozen_playlists.insert (pl);
3220 if (insert_result.second) {
3222 session->add_command(new MementoUndoCommand<Playlist>(*pl, pl->get_state()));
3228 if (drag_info.brushing) {
3229 mouse_brush_insert_region (rv, pending_region_position);
3231 rv->move (x_delta, y_delta);
3235 if (drag_info.first_move) {
3236 cursor_group->raise_to_top();
3239 drag_info.first_move = false;
3241 if (x_delta != 0 && !drag_info.brushing) {
3242 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3248 Editor::region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3250 jack_nframes_t where;
3251 AudioRegionView* rv = reinterpret_cast<AudioRegionView *> (drag_info.data);
3252 pair<set<Playlist*>::iterator,bool> insert_result;
3253 bool nocommit = true;
3255 AudioTimeAxisView* atv;
3256 bool regionview_y_movement;
3257 bool regionview_x_movement;
3259 /* first_move is set to false if the regionview has been moved in the
3263 if (drag_info.first_move) {
3270 /* The regionview has been moved at some stage during the grab so we need
3271 to account for any mouse movement between this event and the last one.
3274 region_drag_motion_callback (item, event);
3276 if (drag_info.brushing) {
3277 /* all changes were made during motion event handlers */
3281 /* adjust for track speed */
3284 atv = dynamic_cast<AudioTimeAxisView*> (drag_info.last_trackview);
3285 if (atv && atv->get_diskstream()) {
3286 speed = atv->get_diskstream()->speed();
3289 regionview_x_movement = (drag_info.last_frame_position != (jack_nframes_t) (rv->region.position()/speed));
3290 regionview_y_movement = (drag_info.last_trackview != &rv->get_time_axis_view());
3292 //printf ("last_frame: %s position is %lu %g\n", rv->get_time_axis_view().name().c_str(), drag_info.last_frame_position, speed);
3293 //printf ("last_rackview: %s \n", drag_info.last_trackview->name().c_str());
3295 if (regionview_y_movement) {
3297 /* motion between tracks */
3299 list<AudioRegionView*> new_selection;
3301 /* moved to a different audio track. */
3303 for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin(); i != selection->audio_regions.by_layer().end(); ) {
3305 AudioRegionView* rv2 = (*i);
3307 /* the region that used to be in the old playlist is not
3308 moved to the new one - we make a copy of it. as a result,
3309 any existing editor for the region should no longer be
3313 if (!drag_info.copy) {
3314 rv2->hide_region_editor();
3316 new_selection.push_back (rv2);
3320 /* first, freeze the target tracks */
3322 for (list<AudioRegionView*>::const_iterator i = new_selection.begin(); i != new_selection.end();i++ ) {
3324 Playlist* from_playlist;
3325 Playlist* to_playlist;
3327 double ix1, ix2, iy1, iy2;
3329 (*i)->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3330 (*i)->get_canvas_group()->i2w (ix1, iy1);
3331 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3332 AudioTimeAxisView* atv2 = dynamic_cast<AudioTimeAxisView*>(tvp2);
3334 from_playlist = (*i)->region.playlist();
3335 to_playlist = atv2->playlist();
3337 /* the from_playlist was frozen in the "first_move" case
3338 of the motion handler. the insert can fail,
3339 but that doesn't matter. it just means
3340 we already have the playlist in the list.
3343 motion_frozen_playlists.insert (from_playlist);
3345 /* only freeze the to_playlist once */
3347 insert_result = motion_frozen_playlists.insert(to_playlist);
3348 if (insert_result.second) {
3349 to_playlist->freeze();
3350 session->add_command(new MementoUndoCommand<Playlist>(*to_playlist, to_playlist->get_state()));
3355 /* now do it again with the actual operations */
3357 for (list<AudioRegionView*>::const_iterator i = new_selection.begin(); i != new_selection.end();i++ ) {
3359 Playlist* from_playlist;
3360 Playlist* to_playlist;
3362 double ix1, ix2, iy1, iy2;
3364 (*i)->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3365 (*i)->get_canvas_group()->i2w (ix1, iy1);
3366 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3367 AudioTimeAxisView* atv2 = dynamic_cast<AudioTimeAxisView*>(tvp2);
3369 from_playlist = (*i)->region.playlist();
3370 to_playlist = atv2->playlist();
3372 latest_regionview = 0;
3374 where = (jack_nframes_t) (unit_to_frame (ix1) * speed);
3375 Region* new_region = createRegion ((*i)->region);
3377 from_playlist->remove_region (&((*i)->region));
3379 sigc::connection c = atv2->view->AudioRegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3380 to_playlist->add_region (*new_region, where);
3383 if (latest_regionview) {
3384 selection->add (latest_regionview);
3390 /* motion within a single track */
3392 for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin(); i != selection->audio_regions.by_layer().end(); ++i) {
3396 if (rv->region.locked()) {
3400 if (regionview_x_movement) {
3401 double ownspeed = 1.0;
3402 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*> (&(rv->get_time_axis_view()));
3404 if (atv && atv->get_diskstream()) {
3405 ownspeed = atv->get_diskstream()->speed();
3408 /* base the new region position on the current position of the regionview.*/
3410 double ix1, ix2, iy1, iy2;
3412 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3413 rv->get_canvas_group()->i2w (ix1, iy1);
3414 where = (jack_nframes_t) (unit_to_frame (ix1) * ownspeed);
3418 where = rv->region.position();
3421 rv->get_time_axis_view().reveal_dependent_views (*rv);
3423 /* no need to add an undo here, we did that when we added this playlist to motion_frozen playlists */
3425 rv->region.set_position (where, (void *) this);
3430 for (set<Playlist*>::iterator p = motion_frozen_playlists.begin(); p != motion_frozen_playlists.end(); ++p) {
3432 session->add_command (new MementoRedoCommand<Playlist>(*(*p), (*p)->get_state()));
3435 motion_frozen_playlists.clear ();
3438 commit_reversible_command ();
3443 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
3445 /* Either add to or set the set the region selection, unless
3446 this is an alignment click (control used)
3449 if (Keyboard::modifier_state_contains (event->state, Keyboard::Control)) {
3450 TimeAxisView* tv = &rv.get_time_axis_view();
3451 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*>(tv);
3453 if (atv && atv->is_audio_track()) {
3454 speed = atv->get_diskstream()->speed();
3457 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Alt))) {
3459 align_region (rv.region, SyncPoint, (jack_nframes_t) (edit_cursor->current_frame * speed));
3461 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Shift))) {
3463 align_region (rv.region, End, (jack_nframes_t) (edit_cursor->current_frame * speed));
3467 align_region (rv.region, Start, (jack_nframes_t) (edit_cursor->current_frame * speed));
3473 Editor::show_verbose_time_cursor (jack_nframes_t frame, double offset, double xpos, double ypos)
3484 switch (ARDOUR_UI::instance()->secondary_clock.mode ()) {
3485 case AudioClock::BBT:
3486 session->bbt_time (frame, bbt);
3487 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, bbt.bars, bbt.beats, bbt.ticks);
3490 case AudioClock::SMPTE:
3491 session->smpte_time (frame, smpte);
3492 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
3495 case AudioClock::MinSec:
3496 /* XXX fix this to compute min/sec properly */
3497 session->smpte_time (frame, smpte);
3498 secs = smpte.seconds + ((float) smpte.frames / session->smpte_frames_per_second);
3499 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", smpte.hours, smpte.minutes, secs);
3503 snprintf (buf, sizeof(buf), "%u", frame);
3507 if (xpos >= 0 && ypos >=0) {
3508 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
3511 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
3513 show_verbose_canvas_cursor ();
3517 Editor::show_verbose_duration_cursor (jack_nframes_t start, jack_nframes_t end, double offset, double xpos, double ypos)
3524 Meter meter_at_start(session->tempo_map().meter_at(start));
3530 switch (ARDOUR_UI::instance()->secondary_clock.mode ()) {
3531 case AudioClock::BBT:
3532 session->bbt_time (start, sbbt);
3533 session->bbt_time (end, ebbt);
3536 /* XXX this computation won't work well if the
3537 user makes a selection that spans any meter changes.
3540 ebbt.bars -= sbbt.bars;
3541 if (ebbt.beats >= sbbt.beats) {
3542 ebbt.beats -= sbbt.beats;
3545 ebbt.beats = int(meter_at_start.beats_per_bar()) + ebbt.beats - sbbt.beats;
3547 if (ebbt.ticks >= sbbt.ticks) {
3548 ebbt.ticks -= sbbt.ticks;
3551 ebbt.ticks = int(Meter::ticks_per_beat) + ebbt.ticks - sbbt.ticks;
3554 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, ebbt.bars, ebbt.beats, ebbt.ticks);
3557 case AudioClock::SMPTE:
3558 session->smpte_duration (end - start, smpte);
3559 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
3562 case AudioClock::MinSec:
3563 /* XXX fix this to compute min/sec properly */
3564 session->smpte_duration (end - start, smpte);
3565 secs = smpte.seconds + ((float) smpte.frames / session->smpte_frames_per_second);
3566 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", smpte.hours, smpte.minutes, secs);
3570 snprintf (buf, sizeof(buf), "%u", end - start);
3574 if (xpos >= 0 && ypos >=0) {
3575 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
3578 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
3580 show_verbose_canvas_cursor ();
3584 Editor::collect_new_region_view (AudioRegionView* rv)
3586 latest_regionview = rv;
3590 Editor::start_selection_grab (ArdourCanvas::Item* item, GdkEvent* event)
3592 if (clicked_regionview == 0) {
3596 /* lets try to create new Region for the selection */
3598 vector<AudioRegion*> new_regions;
3599 create_region_from_selection (new_regions);
3601 if (new_regions.empty()) {
3605 /* XXX fix me one day to use all new regions */
3607 Region* region = new_regions.front();
3609 /* add it to the current stream/playlist.
3611 tricky: the streamview for the track will add a new regionview. we will
3612 catch the signal it sends when it creates the regionview to
3613 set the regionview we want to then drag.
3616 latest_regionview = 0;
3617 sigc::connection c = clicked_audio_trackview->view->AudioRegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3619 /* A selection grab currently creates two undo/redo operations, one for
3620 creating the new region and another for moving it.
3623 begin_reversible_command (_("selection grab"));
3625 Playlist* playlist = clicked_trackview->playlist();
3627 before = &(playlist->get_state());
3628 clicked_trackview->playlist()->add_region (*region, selection->time[clicked_selection].start);
3629 XMLNode &after = playlist->get_state();
3630 session->add_command(new MementoCommand<Playlist>(*playlist, *before, after));
3632 commit_reversible_command ();
3636 if (latest_regionview == 0) {
3637 /* something went wrong */
3641 /* we need to deselect all other regionviews, and select this one
3642 i'm ignoring undo stuff, because the region creation will take care of it */
3643 selection->set (latest_regionview);
3645 drag_info.item = latest_regionview->get_canvas_group();
3646 drag_info.data = latest_regionview;
3647 drag_info.motion_callback = &Editor::region_drag_motion_callback;
3648 drag_info.finished_callback = &Editor::region_drag_finished_callback;
3652 drag_info.last_trackview = clicked_trackview;
3653 drag_info.last_frame_position = latest_regionview->region.position();
3654 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
3656 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3660 Editor::cancel_selection ()
3662 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
3663 (*i)->hide_selection ();
3665 begin_reversible_command (_("cancel selection"));
3666 selection->clear ();
3667 clicked_selection = 0;
3668 commit_reversible_command ();
3672 Editor::start_selection_op (ArdourCanvas::Item* item, GdkEvent* event, SelectionOp op)
3674 jack_nframes_t start = 0;
3675 jack_nframes_t end = 0;
3681 drag_info.item = item;
3682 drag_info.motion_callback = &Editor::drag_selection;
3683 drag_info.finished_callback = &Editor::end_selection_op;
3688 case CreateSelection:
3689 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
3690 drag_info.copy = true;
3692 drag_info.copy = false;
3694 start_grab (event, selector_cursor);
3697 case SelectionStartTrim:
3698 if (clicked_trackview) {
3699 clicked_trackview->order_selection_trims (item, true);
3701 start_grab (event, trimmer_cursor);
3702 start = selection->time[clicked_selection].start;
3703 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
3706 case SelectionEndTrim:
3707 if (clicked_trackview) {
3708 clicked_trackview->order_selection_trims (item, false);
3710 start_grab (event, trimmer_cursor);
3711 end = selection->time[clicked_selection].end;
3712 drag_info.pointer_frame_offset = drag_info.grab_frame - end;
3716 start = selection->time[clicked_selection].start;
3718 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
3722 if (selection_op == SelectionMove) {
3723 show_verbose_time_cursor(start, 10);
3725 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
3730 Editor::drag_selection (ArdourCanvas::Item* item, GdkEvent* event)
3732 jack_nframes_t start = 0;
3733 jack_nframes_t end = 0;
3734 jack_nframes_t length;
3735 jack_nframes_t pending_position;
3737 if ((int32_t) drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
3738 pending_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
3741 pending_position = 0;
3744 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3745 snap_to (pending_position);
3748 /* only alter selection if the current frame is
3749 different from the last frame position (adjusted)
3752 if (pending_position == drag_info.last_pointer_frame) return;
3754 switch (selection_op) {
3755 case CreateSelection:
3757 if (drag_info.first_move) {
3758 snap_to (drag_info.grab_frame);
3761 if (pending_position < drag_info.grab_frame) {
3762 start = pending_position;
3763 end = drag_info.grab_frame;
3765 end = pending_position;
3766 start = drag_info.grab_frame;
3769 /* first drag: Either add to the selection
3770 or create a new selection->
3773 if (drag_info.first_move) {
3775 begin_reversible_command (_("range selection"));
3777 if (drag_info.copy) {
3778 /* adding to the selection */
3779 clicked_selection = selection->add (start, end);
3780 drag_info.copy = false;
3782 /* new selection-> */
3783 clicked_selection = selection->set (clicked_trackview, start, end);
3788 case SelectionStartTrim:
3790 if (drag_info.first_move) {
3791 begin_reversible_command (_("trim selection start"));
3794 start = selection->time[clicked_selection].start;
3795 end = selection->time[clicked_selection].end;
3797 if (pending_position > end) {
3800 start = pending_position;
3804 case SelectionEndTrim:
3806 if (drag_info.first_move) {
3807 begin_reversible_command (_("trim selection end"));
3810 start = selection->time[clicked_selection].start;
3811 end = selection->time[clicked_selection].end;
3813 if (pending_position < start) {
3816 end = pending_position;
3823 if (drag_info.first_move) {
3824 begin_reversible_command (_("move selection"));
3827 start = selection->time[clicked_selection].start;
3828 end = selection->time[clicked_selection].end;
3830 length = end - start;
3832 start = pending_position;
3835 end = start + length;
3840 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
3841 start_canvas_autoscroll (1);
3845 selection->replace (clicked_selection, start, end);
3848 drag_info.last_pointer_frame = pending_position;
3849 drag_info.first_move = false;
3851 if (selection_op == SelectionMove) {
3852 show_verbose_time_cursor(start, 10);
3854 show_verbose_time_cursor(pending_position, 10);
3859 Editor::end_selection_op (ArdourCanvas::Item* item, GdkEvent* event)
3861 if (!drag_info.first_move) {
3862 drag_selection (item, event);
3863 /* XXX this is not object-oriented programming at all. ick */
3864 if (selection->time.consolidate()) {
3865 selection->TimeChanged ();
3867 commit_reversible_command ();
3869 /* just a click, no pointer movement.*/
3871 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
3873 selection->clear_time();
3878 /* XXX what happens if its a music selection? */
3879 session->set_audio_range (selection->time);
3880 stop_canvas_autoscroll ();
3884 Editor::start_trim (ArdourCanvas::Item* item, GdkEvent* event)
3887 TimeAxisView* tvp = clicked_trackview;
3888 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
3890 if (tv && tv->is_audio_track()) {
3891 speed = tv->get_diskstream()->speed();
3894 jack_nframes_t region_start = (jack_nframes_t) (clicked_regionview->region.position() / speed);
3895 jack_nframes_t region_end = (jack_nframes_t) (clicked_regionview->region.last_frame() / speed);
3896 jack_nframes_t region_length = (jack_nframes_t) (clicked_regionview->region.length() / speed);
3898 motion_frozen_playlists.clear();
3900 //drag_info.item = clicked_regionview->get_name_highlight();
3901 drag_info.item = item;
3902 drag_info.motion_callback = &Editor::trim_motion_callback;
3903 drag_info.finished_callback = &Editor::trim_finished_callback;
3905 start_grab (event, trimmer_cursor);
3907 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
3908 trim_op = ContentsTrim;
3910 /* These will get overridden for a point trim.*/
3911 if (drag_info.current_pointer_frame < (region_start + region_length/2)) {
3912 /* closer to start */
3913 trim_op = StartTrim;
3914 } else if (drag_info.current_pointer_frame > (region_end - region_length/2)) {
3922 show_verbose_time_cursor(region_start, 10);
3925 show_verbose_time_cursor(region_end, 10);
3928 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
3934 Editor::trim_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
3936 AudioRegionView* rv = clicked_regionview;
3937 jack_nframes_t frame_delta = 0;
3938 bool left_direction;
3939 bool obey_snap = !Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier());
3941 /* snap modifier works differently here..
3942 its' current state has to be passed to the
3943 various trim functions in order to work properly
3947 TimeAxisView* tvp = clicked_trackview;
3948 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
3949 pair<set<Playlist*>::iterator,bool> insert_result;
3951 if (tv && tv->is_audio_track()) {
3952 speed = tv->get_diskstream()->speed();
3955 if (drag_info.last_pointer_frame > drag_info.current_pointer_frame) {
3956 left_direction = true;
3958 left_direction = false;
3962 snap_to (drag_info.current_pointer_frame);
3965 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
3969 if (drag_info.first_move) {
3975 trim_type = "Region start trim";
3978 trim_type = "Region end trim";
3981 trim_type = "Region content trim";
3985 begin_reversible_command (trim_type);
3987 for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin(); i != selection->audio_regions.by_layer().end(); ++i) {
3988 (*i)->region.freeze ();
3989 (*i)->temporarily_hide_envelope ();
3991 Playlist * pl = (*i)->region.playlist();
3992 insert_result = motion_frozen_playlists.insert (pl);
3993 if (insert_result.second) {
3994 session->add_command(new MementoUndoCommand<Playlist>(*pl, pl->get_state()));
3999 if (left_direction) {
4000 frame_delta = (drag_info.last_pointer_frame - drag_info.current_pointer_frame);
4002 frame_delta = (drag_info.current_pointer_frame - drag_info.last_pointer_frame);
4007 if ((left_direction == false) && (drag_info.current_pointer_frame <= rv->region.first_frame()/speed)) {
4010 for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin(); i != selection->audio_regions.by_layer().end(); ++i) {
4011 single_start_trim (**i, frame_delta, left_direction, obey_snap);
4017 if ((left_direction == true) && (drag_info.current_pointer_frame > (jack_nframes_t) (rv->region.last_frame()/speed))) {
4020 for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin(); i != selection->audio_regions.by_layer().end(); ++i) {
4021 single_end_trim (**i, frame_delta, left_direction, obey_snap);
4028 bool swap_direction = false;
4030 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
4031 swap_direction = true;
4034 for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin();
4035 i != selection->audio_regions.by_layer().end(); ++i)
4037 single_contents_trim (**i, frame_delta, left_direction, swap_direction, obey_snap);
4045 show_verbose_time_cursor((jack_nframes_t) (rv->region.position()/speed), 10);
4048 show_verbose_time_cursor((jack_nframes_t) (rv->region.last_frame()/speed), 10);
4051 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4055 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4056 drag_info.first_move = false;
4060 Editor::single_contents_trim (AudioRegionView& rv, jack_nframes_t frame_delta, bool left_direction, bool swap_direction, bool obey_snap)
4062 Region& region (rv.region);
4064 if (region.locked()) {
4068 jack_nframes_t new_bound;
4071 TimeAxisView* tvp = clicked_trackview;
4072 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
4074 if (tv && tv->is_audio_track()) {
4075 speed = tv->get_diskstream()->speed();
4078 if (left_direction) {
4079 if (swap_direction) {
4080 new_bound = (jack_nframes_t) (region.position()/speed) + frame_delta;
4082 new_bound = (jack_nframes_t) (region.position()/speed) - frame_delta;
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;
4093 snap_to (new_bound);
4095 region.trim_start ((jack_nframes_t) (new_bound * speed), this);
4096 rv.region_changed (StartChanged);
4100 Editor::single_start_trim (AudioRegionView& rv, jack_nframes_t frame_delta, bool left_direction, bool obey_snap)
4102 Region& region (rv.region);
4104 if (region.locked()) {
4108 jack_nframes_t new_bound;
4111 TimeAxisView* tvp = clicked_trackview;
4112 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
4114 if (tv && tv->is_audio_track()) {
4115 speed = tv->get_diskstream()->speed();
4118 if (left_direction) {
4119 new_bound = (jack_nframes_t) (region.position()/speed) - frame_delta;
4121 new_bound = (jack_nframes_t) (region.position()/speed) + frame_delta;
4125 snap_to (new_bound, (left_direction ? 0 : 1));
4128 region.trim_front ((jack_nframes_t) (new_bound * speed), this);
4130 rv.region_changed (Change (LengthChanged|PositionChanged|StartChanged));
4134 Editor::single_end_trim (AudioRegionView& rv, jack_nframes_t frame_delta, bool left_direction, bool obey_snap)
4136 Region& region (rv.region);
4138 if (region.locked()) {
4142 jack_nframes_t new_bound;
4145 TimeAxisView* tvp = clicked_trackview;
4146 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
4148 if (tv && tv->is_audio_track()) {
4149 speed = tv->get_diskstream()->speed();
4152 if (left_direction) {
4153 new_bound = (jack_nframes_t) ((region.last_frame() + 1)/speed) - frame_delta;
4155 new_bound = (jack_nframes_t) ((region.last_frame() + 1)/speed) + frame_delta;
4159 snap_to (new_bound);
4161 region.trim_end ((jack_nframes_t) (new_bound * speed), this);
4162 rv.region_changed (LengthChanged);
4166 Editor::trim_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
4168 if (!drag_info.first_move) {
4169 trim_motion_callback (item, event);
4171 if (!clicked_regionview->get_selected()) {
4172 thaw_region_after_trim (*clicked_regionview);
4175 for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin();
4176 i != selection->audio_regions.by_layer().end(); ++i)
4178 thaw_region_after_trim (**i);
4182 for (set<Playlist*>::iterator p = motion_frozen_playlists.begin(); p != motion_frozen_playlists.end(); ++p) {
4184 session->add_command (new MementoRedoCommand<Playlist>(*(*p), (*p)->get_state()));
4187 motion_frozen_playlists.clear ();
4189 commit_reversible_command();
4191 /* no mouse movement */
4197 Editor::point_trim (GdkEvent* event)
4199 AudioRegionView* rv = clicked_regionview;
4200 jack_nframes_t new_bound = drag_info.current_pointer_frame;
4202 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4203 snap_to (new_bound);
4206 /* Choose action dependant on which button was pressed */
4207 switch (event->button.button) {
4209 trim_op = StartTrim;
4210 begin_reversible_command (_("Start point trim"));
4212 if (rv->get_selected()) {
4214 for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin();
4215 i != selection->audio_regions.by_layer().end(); ++i)
4217 if (!(*i)->region.locked()) {
4218 Playlist *pl = (*i)->region.playlist();
4219 XMLNode &before = pl->get_state();
4220 (*i)->region.trim_front (new_bound, this);
4221 XMLNode &after = pl->get_state();
4222 session->add_command(new MementoCommand<Playlist>(*pl, before, after));
4228 if (!rv->region.locked()) {
4229 Playlist *pl = rv->region.playlist();
4230 XMLNode &before = pl->get_state();
4231 rv->region.trim_front (new_bound, this);
4232 XMLNode &after = pl->get_state();
4233 session->add_command(new MementoCommand<Playlist>(*pl, before, after));
4237 commit_reversible_command();
4242 begin_reversible_command (_("End point trim"));
4244 if (rv->get_selected()) {
4246 for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin(); i != selection->audio_regions.by_layer().end(); ++i)
4248 if (!(*i)->region.locked()) {
4249 Playlist *pl = (*i)->region.playlist();
4250 XMLNode &before = pl->get_state();
4251 (*i)->region.trim_end (new_bound, this);
4252 XMLNode &after = pl->get_state();
4253 session->add_command(new MementoCommand<Playlist>(*pl, before, after));
4259 if (!rv->region.locked()) {
4260 Playlist *pl = rv->region.playlist();
4261 XMLNode &before = pl->get_state();
4262 rv->region.trim_end (new_bound, this);
4263 XMLNode &after = pl->get_state();
4264 session->add_command (new MementoCommand<Playlist>(*pl, before, after));
4268 commit_reversible_command();
4277 Editor::thaw_region_after_trim (AudioRegionView& rv)
4279 Region& region (rv.region);
4281 if (region.locked()) {
4285 region.thaw (_("trimmed region"));
4286 XMLNode &after = region.playlist()->get_state();
4287 session->add_command (new MementoRedoCommand<Playlist>(*(region.playlist()), after));
4289 rv.unhide_envelope ();
4293 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* event)
4298 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
4299 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
4303 Location* location = find_location_from_marker (marker, is_start);
4304 location->set_hidden (true, this);
4309 Editor::start_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event, RangeMarkerOp op)
4315 drag_info.item = item;
4316 drag_info.motion_callback = &Editor::drag_range_markerbar_op;
4317 drag_info.finished_callback = &Editor::end_range_markerbar_op;
4319 range_marker_op = op;
4321 if (!temp_location) {
4322 temp_location = new Location;
4326 case CreateRangeMarker:
4327 case CreateTransportMarker:
4329 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
4330 drag_info.copy = true;
4332 drag_info.copy = false;
4334 start_grab (event, selector_cursor);
4338 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4343 Editor::drag_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4345 jack_nframes_t start = 0;
4346 jack_nframes_t end = 0;
4347 ArdourCanvas::SimpleRect *crect = (range_marker_op == CreateRangeMarker) ? range_bar_drag_rect: transport_bar_drag_rect;
4349 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4350 snap_to (drag_info.current_pointer_frame);
4353 /* only alter selection if the current frame is
4354 different from the last frame position.
4357 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4359 switch (range_marker_op) {
4360 case CreateRangeMarker:
4361 case CreateTransportMarker:
4362 if (drag_info.first_move) {
4363 snap_to (drag_info.grab_frame);
4366 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4367 start = drag_info.current_pointer_frame;
4368 end = drag_info.grab_frame;
4370 end = drag_info.current_pointer_frame;
4371 start = drag_info.grab_frame;
4374 /* first drag: Either add to the selection
4375 or create a new selection.
4378 if (drag_info.first_move) {
4380 temp_location->set (start, end);
4384 update_marker_drag_item (temp_location);
4385 range_marker_drag_rect->show();
4386 range_marker_drag_rect->raise_to_top();
4392 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
4393 start_canvas_autoscroll (1);
4397 temp_location->set (start, end);
4399 double x1 = frame_to_pixel (start);
4400 double x2 = frame_to_pixel (end);
4401 crect->property_x1() = x1;
4402 crect->property_x2() = x2;
4404 update_marker_drag_item (temp_location);
4407 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4408 drag_info.first_move = false;
4410 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4415 Editor::end_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4417 Location * newloc = 0;
4419 if (!drag_info.first_move) {
4420 drag_range_markerbar_op (item, event);
4422 switch (range_marker_op) {
4423 case CreateRangeMarker:
4425 begin_reversible_command (_("new range marker"));
4426 XMLNode &before = session->locations()->get_state();
4427 newloc = new Location(temp_location->start(), temp_location->end(), "unnamed", Location::IsRangeMarker);
4428 session->locations()->add (newloc, true);
4429 XMLNode &after = session->locations()->get_state();
4430 session->add_command(new MementoCommand<Locations>(*(session->locations()), before, after));
4431 commit_reversible_command ();
4433 range_bar_drag_rect->hide();
4434 range_marker_drag_rect->hide();
4438 case CreateTransportMarker:
4439 // popup menu to pick loop or punch
4440 new_transport_marker_context_menu (&event->button, item);
4445 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
4447 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
4449 jack_nframes_t start;
4452 start = session->locations()->first_mark_before (drag_info.grab_frame);
4453 end = session->locations()->first_mark_after (drag_info.grab_frame);
4455 if (end == max_frames) {
4456 end = session->current_end_frame ();
4460 start = session->current_start_frame ();
4463 switch (mouse_mode) {
4465 /* find the two markers on either side and then make the selection from it */
4466 cerr << "select between " << start << " .. " << end << endl;
4467 select_all_within (start, end, 0.0f, FLT_MAX, Selection::Set);
4471 /* find the two markers on either side of the click and make the range out of it */
4472 selection->set (0, start, end);
4481 stop_canvas_autoscroll ();
4487 Editor::start_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4489 drag_info.item = item;
4490 drag_info.motion_callback = &Editor::drag_mouse_zoom;
4491 drag_info.finished_callback = &Editor::end_mouse_zoom;
4493 start_grab (event, zoom_cursor);
4495 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4499 Editor::drag_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4501 jack_nframes_t start;
4504 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4505 snap_to (drag_info.current_pointer_frame);
4507 if (drag_info.first_move) {
4508 snap_to (drag_info.grab_frame);
4512 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4514 /* base start and end on initial click position */
4515 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4516 start = drag_info.current_pointer_frame;
4517 end = drag_info.grab_frame;
4519 end = drag_info.current_pointer_frame;
4520 start = drag_info.grab_frame;
4525 if (drag_info.first_move) {
4527 zoom_rect->raise_to_top();
4530 reposition_zoom_rect(start, end);
4532 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4533 drag_info.first_move = false;
4535 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4540 Editor::end_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4542 if (!drag_info.first_move) {
4543 drag_mouse_zoom (item, event);
4545 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
4546 temporal_zoom_by_frame (drag_info.grab_frame, drag_info.last_pointer_frame, "mouse zoom");
4548 temporal_zoom_by_frame (drag_info.last_pointer_frame, drag_info.grab_frame, "mouse zoom");
4551 temporal_zoom_to_frame (false, drag_info.grab_frame);
4553 temporal_zoom_step (false);
4554 center_screen (drag_info.grab_frame);
4562 Editor::reposition_zoom_rect (jack_nframes_t start, jack_nframes_t end)
4564 double x1 = frame_to_pixel (start);
4565 double x2 = frame_to_pixel (end);
4566 double y2 = canvas_height - 2;
4568 zoom_rect->property_x1() = x1;
4569 zoom_rect->property_y1() = 1.0;
4570 zoom_rect->property_x2() = x2;
4571 zoom_rect->property_y2() = y2;
4575 Editor::start_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4577 drag_info.item = item;
4578 drag_info.motion_callback = &Editor::drag_rubberband_select;
4579 drag_info.finished_callback = &Editor::end_rubberband_select;
4581 start_grab (event, cross_hair_cursor);
4583 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4587 Editor::drag_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4589 jack_nframes_t start;
4594 /* use a bigger drag threshold than the default */
4596 if (abs ((int) (drag_info.current_pointer_frame - drag_info.grab_frame)) < 8) {
4600 // if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4601 // snap_to (drag_info.current_pointer_frame);
4603 // if (drag_info.first_move) {
4604 // snap_to (drag_info.grab_frame);
4609 /* base start and end on initial click position */
4610 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4611 start = drag_info.current_pointer_frame;
4612 end = drag_info.grab_frame;
4614 end = drag_info.current_pointer_frame;
4615 start = drag_info.grab_frame;
4618 if (drag_info.current_pointer_y < drag_info.grab_y) {
4619 y1 = drag_info.current_pointer_y;
4620 y2 = drag_info.grab_y;
4623 y2 = drag_info.current_pointer_y;
4624 y1 = drag_info.grab_y;
4628 if (start != end || y1 != y2) {
4630 double x1 = frame_to_pixel (start);
4631 double x2 = frame_to_pixel (end);
4633 rubberband_rect->property_x1() = x1;
4634 rubberband_rect->property_y1() = y1;
4635 rubberband_rect->property_x2() = x2;
4636 rubberband_rect->property_y2() = y2;
4638 rubberband_rect->show();
4639 rubberband_rect->raise_to_top();
4641 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4642 drag_info.first_move = false;
4644 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4649 Editor::end_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4651 if (!drag_info.first_move) {
4653 drag_rubberband_select (item, event);
4656 if (drag_info.current_pointer_y < drag_info.grab_y) {
4657 y1 = drag_info.current_pointer_y;
4658 y2 = drag_info.grab_y;
4661 y2 = drag_info.current_pointer_y;
4662 y1 = drag_info.grab_y;
4666 Selection::Operation op = Keyboard::selection_type (event->button.state);
4669 begin_reversible_command (_("select regions"));
4671 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
4672 commit = select_all_within (drag_info.grab_frame, drag_info.last_pointer_frame, y1, y2, op);
4674 commit = select_all_within (drag_info.last_pointer_frame, drag_info.grab_frame, y1, y2, op);
4678 commit_reversible_command ();
4682 selection->clear_audio_regions();
4683 selection->clear_points ();
4684 selection->clear_lines ();
4687 rubberband_rect->hide();
4692 Editor::mouse_rename_region (ArdourCanvas::Item* item, GdkEvent* event)
4694 using namespace Gtkmm2ext;
4696 ArdourPrompter prompter (false);
4698 prompter.set_prompt (_("Name for region:"));
4699 prompter.set_initial_text (clicked_regionview->region.name());
4700 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
4701 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
4702 prompter.show_all ();
4703 switch (prompter.run ()) {
4704 case Gtk::RESPONSE_ACCEPT:
4706 prompter.get_result(str);
4708 clicked_regionview->region.set_name (str);
4716 Editor::start_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
4718 drag_info.item = item;
4719 drag_info.motion_callback = &Editor::time_fx_motion;
4720 drag_info.finished_callback = &Editor::end_time_fx;
4724 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4728 Editor::time_fx_motion (ArdourCanvas::Item *item, GdkEvent* event)
4730 AudioRegionView* rv = clicked_regionview;
4732 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4733 snap_to (drag_info.current_pointer_frame);
4736 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
4740 if (drag_info.current_pointer_frame > rv->region.position()) {
4741 rv->get_time_axis_view().show_timestretch (rv->region.position(), drag_info.current_pointer_frame);
4744 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4745 drag_info.first_move = false;
4747 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4751 Editor::end_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
4753 clicked_regionview->get_time_axis_view().hide_timestretch ();
4755 if (drag_info.first_move) {
4759 jack_nframes_t newlen = drag_info.last_pointer_frame - clicked_regionview->region.position();
4760 float percentage = (float) ((double) newlen - (double) clicked_regionview->region.length()) / ((double) newlen) * 100.0f;
4762 begin_reversible_command (_("timestretch"));
4764 if (run_timestretch (selection->audio_regions, percentage) == 0) {
4765 session->commit_reversible_command ();
4770 Editor::mouse_brush_insert_region (AudioRegionView* rv, jack_nframes_t pos)
4772 /* no brushing without a useful snap setting */
4774 switch (snap_mode) {
4776 return; /* can't work because it allows region to be placed anywhere */
4781 switch (snap_type) {
4784 case SnapToEditCursor:
4791 /* don't brush a copy over the original */
4793 if (pos == rv->region.position()) {
4797 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*>(&rv->get_time_axis_view());
4799 if (atv == 0 || !atv->is_audio_track()) {
4803 Playlist* playlist = atv->playlist();
4804 double speed = atv->get_diskstream()->speed();
4806 XMLNode &before = playlist->get_state();
4807 playlist->add_region (*(new AudioRegion (rv->region)), (jack_nframes_t) (pos * speed));
4808 XMLNode &after = playlist->get_state();
4809 session->add_command(new MementoCommand<Playlist>(*playlist, before, after));
4811 // playlist is frozen, so we have to update manually
4813 playlist->StateChanged (Change (~0)); /* EMIT SIGNAL */
4817 Editor::track_height_step_timeout ()
4820 struct timeval delta;
4822 gettimeofday (&now, 0);
4823 timersub (&now, &last_track_height_step_timestamp, &delta);
4825 if (delta.tv_sec * 1000000 + delta.tv_usec > 250000) { /* milliseconds */
4826 current_stepping_trackview = 0;