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.
29 #include <pbd/error.h>
30 #include <gtkmm2ext/utils.h>
32 #include "ardour_ui.h"
34 #include "time_axis_view.h"
35 #include "audio_time_axis.h"
36 #include "audio_region_view.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 (RegionSelection::iterator i = selection->regions.begin(); i != selection->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 RegionViewNameHighlight:
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 RegionViewNameHighlight:
526 start_trim (item, event);
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 RegionViewNameHighlight:
697 start_trim (item, event);
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);
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 RegionViewNameHighlight:
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 // Gain only makes sense for audio regions
1052 if ( ! dynamic_cast<AudioRegionView*>(clicked_regionview))
1055 switch (item_type) {
1057 dynamic_cast<AudioRegionView*>(clicked_regionview)->add_gain_point_event (item, event);
1061 case AutomationTrackItem:
1062 dynamic_cast<AutomationTimeAxisView*>(clicked_trackview)->
1063 add_automation_event (item, event, where, event->button.y);
1072 switch (item_type) {
1074 audition_selected_region ();
1091 switch (mouse_mode) {
1094 switch (item_type) {
1096 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
1098 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::Shift|Keyboard::Alt))) {
1101 // Button2 click is unused
1114 // x_style_paste (where, 1.0);
1134 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1140 switch (item_type) {
1141 case GainControlPointItem:
1142 if (mouse_mode == MouseGain) {
1143 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1144 cp->set_visible (true);
1148 at_y = cp->get_y ();
1149 cp->item->i2w (at_x, at_y);
1153 fraction = 1.0 - (cp->get_y() / cp->line.height());
1155 set_verbose_canvas_cursor (cp->line.get_verbose_cursor_string (fraction), at_x, at_y);
1156 show_verbose_canvas_cursor ();
1158 if (is_drawable()) {
1159 track_canvas.get_window()->set_cursor (*fader_cursor);
1164 case GainAutomationControlPointItem:
1165 case PanAutomationControlPointItem:
1166 case RedirectAutomationControlPointItem:
1167 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1168 cp->set_visible (true);
1172 at_y = cp->get_y ();
1173 cp->item->i2w (at_x, at_y);
1177 fraction = 1.0 - (cp->get_y() / cp->line.height());
1179 set_verbose_canvas_cursor (cp->line.get_verbose_cursor_string (fraction), at_x, at_y);
1180 show_verbose_canvas_cursor ();
1182 if (is_drawable()) {
1183 track_canvas.get_window()->set_cursor (*fader_cursor);
1188 if (mouse_mode == MouseGain) {
1189 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1191 line->property_fill_color_rgba() = color_map[cEnteredGainLine];
1192 if (is_drawable()) {
1193 track_canvas.get_window()->set_cursor (*fader_cursor);
1198 case GainAutomationLineItem:
1199 case RedirectAutomationLineItem:
1200 case PanAutomationLineItem:
1202 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1204 line->property_fill_color_rgba() = color_map[cEnteredAutomationLine];
1206 if (is_drawable()) {
1207 track_canvas.get_window()->set_cursor (*fader_cursor);
1211 case RegionViewNameHighlight:
1212 if (is_drawable() && mouse_mode == MouseObject) {
1213 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1217 case StartSelectionTrimItem:
1218 case EndSelectionTrimItem:
1219 /* <CMT Additions> */
1220 case ImageFrameHandleStartItem:
1221 case ImageFrameHandleEndItem:
1222 case MarkerViewHandleStartItem:
1223 case MarkerViewHandleEndItem:
1224 /* </CMT Additions> */
1226 if (is_drawable()) {
1227 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1231 case EditCursorItem:
1232 case PlayheadCursorItem:
1233 if (is_drawable()) {
1234 track_canvas.get_window()->set_cursor (*grabber_cursor);
1238 case RegionViewName:
1240 /* when the name is not an active item, the entire name highlight is for trimming */
1242 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1243 if (mouse_mode == MouseObject && is_drawable()) {
1244 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1250 case AutomationTrackItem:
1251 if (is_drawable()) {
1252 Gdk::Cursor *cursor;
1253 switch (mouse_mode) {
1255 cursor = selector_cursor;
1258 cursor = zoom_cursor;
1261 cursor = cross_hair_cursor;
1265 track_canvas.get_window()->set_cursor (*cursor);
1267 AutomationTimeAxisView* atv;
1268 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1269 clear_entered_track = false;
1270 set_entered_track (atv);
1276 case RangeMarkerBarItem:
1277 case TransportMarkerBarItem:
1280 if (is_drawable()) {
1281 time_canvas.get_window()->set_cursor (*timebar_cursor);
1286 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1289 marker->set_color_rgba (color_map[cEnteredMarker]);
1291 case MeterMarkerItem:
1292 case TempoMarkerItem:
1293 if (is_drawable()) {
1294 time_canvas.get_window()->set_cursor (*timebar_cursor);
1297 case FadeInHandleItem:
1298 case FadeOutHandleItem:
1299 if (mouse_mode == MouseObject) {
1300 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1302 rect->property_fill_color_rgba() = 0;
1303 rect->property_outline_pixels() = 1;
1312 /* second pass to handle entered track status in a comprehensible way.
1315 switch (item_type) {
1317 case GainAutomationLineItem:
1318 case RedirectAutomationLineItem:
1319 case PanAutomationLineItem:
1320 case GainControlPointItem:
1321 case GainAutomationControlPointItem:
1322 case PanAutomationControlPointItem:
1323 case RedirectAutomationControlPointItem:
1324 /* these do not affect the current entered track state */
1325 clear_entered_track = false;
1328 case AutomationTrackItem:
1329 /* handled above already */
1333 set_entered_track (0);
1341 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1350 switch (item_type) {
1351 case GainControlPointItem:
1352 case GainAutomationControlPointItem:
1353 case PanAutomationControlPointItem:
1354 case RedirectAutomationControlPointItem:
1355 cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
1356 if (cp->line.npoints() > 1) {
1357 if (!cp->selected) {
1358 cp->set_visible (false);
1362 if (is_drawable()) {
1363 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1366 hide_verbose_canvas_cursor ();
1369 case RegionViewNameHighlight:
1370 case StartSelectionTrimItem:
1371 case EndSelectionTrimItem:
1372 case EditCursorItem:
1373 case PlayheadCursorItem:
1374 /* <CMT Additions> */
1375 case ImageFrameHandleStartItem:
1376 case ImageFrameHandleEndItem:
1377 case MarkerViewHandleStartItem:
1378 case MarkerViewHandleEndItem:
1379 /* </CMT Additions> */
1380 if (is_drawable()) {
1381 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1386 case GainAutomationLineItem:
1387 case RedirectAutomationLineItem:
1388 case PanAutomationLineItem:
1389 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1391 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1393 line->property_fill_color_rgba() = al->get_line_color();
1395 if (is_drawable()) {
1396 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1400 case RegionViewName:
1401 /* see enter_handler() for notes */
1402 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1403 if (is_drawable() && mouse_mode == MouseObject) {
1404 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1409 case RangeMarkerBarItem:
1410 case TransportMarkerBarItem:
1414 if (is_drawable()) {
1415 time_canvas.get_window()->set_cursor (*timebar_cursor);
1420 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1423 loc = find_location_from_marker (marker, is_start);
1424 if (loc) location_flags_changed (loc, this);
1426 case MeterMarkerItem:
1427 case TempoMarkerItem:
1429 if (is_drawable()) {
1430 time_canvas.get_window()->set_cursor (*timebar_cursor);
1435 case FadeInHandleItem:
1436 case FadeOutHandleItem:
1437 rv = static_cast<RegionView*>(item->get_data ("regionview"));
1439 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1441 rect->property_fill_color_rgba() = rv->get_fill_color();
1442 rect->property_outline_pixels() = 0;
1447 case AutomationTrackItem:
1448 if (is_drawable()) {
1449 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1450 clear_entered_track = true;
1451 Glib::signal_idle().connect (mem_fun(*this, &Editor::left_automation_track));
1463 Editor::left_automation_track ()
1465 if (clear_entered_track) {
1466 set_entered_track (0);
1467 clear_entered_track = false;
1473 Editor::motion_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type, bool from_autoscroll)
1477 /* We call this so that MOTION_NOTIFY events continue to be
1478 delivered to the canvas. We need to do this because we set
1479 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
1480 the density of the events, at the expense of a round-trip
1481 to the server. Given that this will mostly occur on cases
1482 where DISPLAY = :0.0, and given the cost of what the motion
1483 event might do, its a good tradeoff.
1486 track_canvas.get_pointer (x, y);
1488 if (current_stepping_trackview) {
1489 /* don't keep the persistent stepped trackview if the mouse moves */
1490 current_stepping_trackview = 0;
1491 step_timeout.disconnect ();
1494 if (session && session->actively_recording()) {
1495 /* Sorry. no dragging stuff around while we record */
1499 drag_info.item_type = item_type;
1500 drag_info.current_pointer_frame = event_frame (event, &drag_info.current_pointer_x,
1501 &drag_info.current_pointer_y);
1503 if (!from_autoscroll && drag_info.item) {
1504 /* item != 0 is the best test i can think of for dragging.
1506 if (!drag_info.move_threshold_passed) {
1508 drag_info.move_threshold_passed = (abs ((int) (drag_info.current_pointer_x - drag_info.grab_x)) > 4);
1510 // and change the initial grab loc/frame if this drag info wants us to
1512 if (drag_info.want_move_threshold && drag_info.move_threshold_passed) {
1513 drag_info.grab_frame = drag_info.current_pointer_frame;
1514 drag_info.grab_x = drag_info.current_pointer_x;
1515 drag_info.grab_y = drag_info.current_pointer_y;
1516 drag_info.last_pointer_frame = drag_info.grab_frame;
1517 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
1522 switch (item_type) {
1523 case PlayheadCursorItem:
1524 case EditCursorItem:
1526 case GainControlPointItem:
1527 case RedirectAutomationControlPointItem:
1528 case GainAutomationControlPointItem:
1529 case PanAutomationControlPointItem:
1530 case TempoMarkerItem:
1531 case MeterMarkerItem:
1532 case RegionViewNameHighlight:
1533 case StartSelectionTrimItem:
1534 case EndSelectionTrimItem:
1537 case RedirectAutomationLineItem:
1538 case GainAutomationLineItem:
1539 case PanAutomationLineItem:
1540 case FadeInHandleItem:
1541 case FadeOutHandleItem:
1542 /* <CMT Additions> */
1543 case ImageFrameHandleStartItem:
1544 case ImageFrameHandleEndItem:
1545 case MarkerViewHandleStartItem:
1546 case MarkerViewHandleEndItem:
1547 /* </CMT Additions> */
1548 if (drag_info.item && (event->motion.state & Gdk::BUTTON1_MASK ||
1549 (event->motion.state & Gdk::BUTTON2_MASK))) {
1550 if (!from_autoscroll) {
1551 maybe_autoscroll (event);
1553 (this->*(drag_info.motion_callback)) (item, event);
1562 switch (mouse_mode) {
1567 if (drag_info.item && (event->motion.state & GDK_BUTTON1_MASK ||
1568 (event->motion.state & GDK_BUTTON2_MASK))) {
1569 if (!from_autoscroll) {
1570 maybe_autoscroll (event);
1572 (this->*(drag_info.motion_callback)) (item, event);
1583 track_canvas_motion (event);
1584 // drag_info.last_pointer_frame = drag_info.current_pointer_frame;
1592 Editor::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
1594 if (drag_info.item == 0) {
1595 fatal << _("programming error: start_grab called without drag item") << endmsg;
1601 cursor = grabber_cursor;
1604 // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
1606 if (event->button.button == 2) {
1607 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Alt)) {
1608 drag_info.y_constrained = true;
1609 drag_info.x_constrained = false;
1611 drag_info.y_constrained = false;
1612 drag_info.x_constrained = true;
1615 drag_info.x_constrained = false;
1616 drag_info.y_constrained = false;
1619 drag_info.grab_frame = event_frame(event, &drag_info.grab_x, &drag_info.grab_y);
1620 drag_info.last_pointer_frame = drag_info.grab_frame;
1621 drag_info.current_pointer_frame = drag_info.grab_frame;
1622 drag_info.current_pointer_x = drag_info.grab_x;
1623 drag_info.current_pointer_y = drag_info.grab_y;
1624 drag_info.cumulative_x_drag = 0;
1625 drag_info.cumulative_y_drag = 0;
1626 drag_info.first_move = true;
1627 drag_info.move_threshold_passed = false;
1628 drag_info.want_move_threshold = false;
1629 drag_info.pointer_frame_offset = 0;
1630 drag_info.brushing = false;
1631 drag_info.copied_location = 0;
1633 drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK,
1635 event->button.time);
1637 if (session && session->transport_rolling()) {
1638 drag_info.was_rolling = true;
1640 drag_info.was_rolling = false;
1643 switch (snap_type) {
1644 case SnapToRegionStart:
1645 case SnapToRegionEnd:
1646 case SnapToRegionSync:
1647 case SnapToRegionBoundary:
1648 build_region_boundary_cache ();
1656 Editor::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t time)
1658 drag_info.item->ungrab (0);
1659 drag_info.item = new_item;
1662 cursor = grabber_cursor;
1665 drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK, *cursor, time);
1669 Editor::end_grab (ArdourCanvas::Item* item, GdkEvent* event)
1671 bool did_drag = false;
1673 stop_canvas_autoscroll ();
1675 if (drag_info.item == 0) {
1679 drag_info.item->ungrab (event->button.time);
1681 if (drag_info.finished_callback) {
1682 (this->*(drag_info.finished_callback)) (item, event);
1685 did_drag = !drag_info.first_move;
1687 hide_verbose_canvas_cursor();
1690 drag_info.copy = false;
1691 drag_info.motion_callback = 0;
1692 drag_info.finished_callback = 0;
1693 drag_info.last_trackview = 0;
1694 drag_info.last_frame_position = 0;
1695 drag_info.grab_frame = 0;
1696 drag_info.last_pointer_frame = 0;
1697 drag_info.current_pointer_frame = 0;
1698 drag_info.brushing = false;
1700 if (drag_info.copied_location) {
1701 delete drag_info.copied_location;
1702 drag_info.copied_location = 0;
1709 Editor::set_edit_cursor (GdkEvent* event)
1711 jack_nframes_t pointer_frame = event_frame (event);
1713 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1714 if (snap_type != SnapToEditCursor) {
1715 snap_to (pointer_frame);
1719 edit_cursor->set_position (pointer_frame);
1720 edit_cursor_clock.set (pointer_frame);
1724 Editor::set_playhead_cursor (GdkEvent* event)
1726 jack_nframes_t pointer_frame = event_frame (event);
1728 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1729 snap_to (pointer_frame);
1733 session->request_locate (pointer_frame, session->transport_rolling());
1738 Editor::start_fade_in_grab (ArdourCanvas::Item* item, GdkEvent* event)
1740 drag_info.item = item;
1741 drag_info.motion_callback = &Editor::fade_in_drag_motion_callback;
1742 drag_info.finished_callback = &Editor::fade_in_drag_finished_callback;
1746 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
1747 fatal << _("programming error: fade in canvas item has no regionview data pointer!") << endmsg;
1751 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1753 drag_info.pointer_frame_offset = drag_info.grab_frame - ((jack_nframes_t) arv->audio_region().fade_in().back()->when + arv->region().position());
1757 Editor::fade_in_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1759 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1761 jack_nframes_t fade_length;
1763 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1764 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1770 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1774 if (pos < (arv->region().position() + 64)) {
1775 fade_length = 64; // this should be a minimum defined somewhere
1776 } else if (pos > arv->region().last_frame()) {
1777 fade_length = arv->region().length();
1779 fade_length = pos - arv->region().position();
1782 arv->reset_fade_in_shape_width (fade_length);
1784 show_verbose_duration_cursor (arv->region().position(), arv->region().position() + fade_length, 10);
1786 drag_info.first_move = false;
1790 Editor::fade_in_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1792 if (drag_info.first_move) return;
1794 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1796 jack_nframes_t fade_length;
1798 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1799 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1805 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1809 if (pos < (arv->region().position() + 64)) {
1810 fade_length = 64; // this should be a minimum defined somewhere
1812 else if (pos > arv->region().last_frame()) {
1813 fade_length = arv->region().length();
1816 fade_length = pos - arv->region().position();
1819 begin_reversible_command (_("change fade in length"));
1820 session->add_undo (arv->region().get_memento());
1821 arv->audio_region().set_fade_in_length (fade_length);
1822 session->add_redo_no_execute (arv->region().get_memento());
1823 commit_reversible_command ();
1824 fade_in_drag_motion_callback (item, event);
1828 Editor::start_fade_out_grab (ArdourCanvas::Item* item, GdkEvent* event)
1830 drag_info.item = item;
1831 drag_info.motion_callback = &Editor::fade_out_drag_motion_callback;
1832 drag_info.finished_callback = &Editor::fade_out_drag_finished_callback;
1836 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
1837 fatal << _("programming error: fade out canvas item has no regionview data pointer!") << endmsg;
1841 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1843 drag_info.pointer_frame_offset = drag_info.grab_frame - (arv->region().length() - (jack_nframes_t) arv->audio_region().fade_out().back()->when + arv->region().position());
1847 Editor::fade_out_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1849 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1851 jack_nframes_t fade_length;
1853 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1854 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1860 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1864 if (pos > (arv->region().last_frame() - 64)) {
1865 fade_length = 64; // this should really be a minimum fade defined somewhere
1867 else if (pos < arv->region().position()) {
1868 fade_length = arv->region().length();
1871 fade_length = arv->region().last_frame() - pos;
1874 arv->reset_fade_out_shape_width (fade_length);
1876 show_verbose_duration_cursor (arv->region().last_frame() - fade_length, arv->region().last_frame(), 10);
1878 drag_info.first_move = false;
1882 Editor::fade_out_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1884 if (drag_info.first_move) return;
1886 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1888 jack_nframes_t fade_length;
1890 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1891 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1897 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1901 if (pos > (arv->region().last_frame() - 64)) {
1902 fade_length = 64; // this should really be a minimum fade defined somewhere
1904 else if (pos < arv->region().position()) {
1905 fade_length = arv->region().length();
1908 fade_length = arv->region().last_frame() - pos;
1911 begin_reversible_command (_("change fade out length"));
1912 session->add_undo (arv->region().get_memento());
1913 arv->audio_region().set_fade_out_length (fade_length);
1914 session->add_redo_no_execute (arv->region().get_memento());
1915 commit_reversible_command ();
1917 fade_out_drag_motion_callback (item, event);
1921 Editor::start_cursor_grab (ArdourCanvas::Item* item, GdkEvent* event)
1923 drag_info.item = item;
1924 drag_info.motion_callback = &Editor::cursor_drag_motion_callback;
1925 drag_info.finished_callback = &Editor::cursor_drag_finished_callback;
1929 if ((drag_info.data = (item->get_data ("cursor"))) == 0) {
1930 fatal << _("programming error: cursor canvas item has no cursor data pointer!") << endmsg;
1934 Cursor* cursor = (Cursor *) drag_info.data;
1936 if (session && cursor == playhead_cursor) {
1937 if (drag_info.was_rolling) {
1938 session->request_stop ();
1942 drag_info.pointer_frame_offset = drag_info.grab_frame - cursor->current_frame;
1944 show_verbose_time_cursor (cursor->current_frame, 10);
1948 Editor::cursor_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1950 Cursor* cursor = (Cursor *) drag_info.data;
1951 jack_nframes_t adjusted_frame;
1953 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1954 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1960 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1961 if (cursor != edit_cursor || snap_type != SnapToEditCursor) {
1962 snap_to (adjusted_frame);
1966 if (adjusted_frame == drag_info.last_pointer_frame) return;
1968 cursor->set_position (adjusted_frame);
1970 if (cursor == edit_cursor) {
1971 edit_cursor_clock.set (cursor->current_frame);
1974 show_verbose_time_cursor (cursor->current_frame, 10);
1976 drag_info.last_pointer_frame = adjusted_frame;
1977 drag_info.first_move = false;
1981 Editor::cursor_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1983 if (drag_info.first_move) return;
1985 cursor_drag_motion_callback (item, event);
1987 if (item == &playhead_cursor->canvas_item) {
1989 session->request_locate (playhead_cursor->current_frame, drag_info.was_rolling);
1991 } else if (item == &edit_cursor->canvas_item) {
1992 edit_cursor->set_position (edit_cursor->current_frame);
1993 edit_cursor_clock.set (edit_cursor->current_frame);
1998 Editor::update_marker_drag_item (Location *location)
2000 double x1 = frame_to_pixel (location->start());
2001 double x2 = frame_to_pixel (location->end());
2003 if (location->is_mark()) {
2004 marker_drag_line_points.front().set_x(x1);
2005 marker_drag_line_points.back().set_x(x1);
2006 marker_drag_line->property_points() = marker_drag_line_points;
2009 range_marker_drag_rect->property_x1() = x1;
2010 range_marker_drag_rect->property_x2() = x2;
2015 Editor::start_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2019 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2020 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2026 Location *location = find_location_from_marker (marker, is_start);
2028 drag_info.item = item;
2029 drag_info.data = marker;
2030 drag_info.motion_callback = &Editor::marker_drag_motion_callback;
2031 drag_info.finished_callback = &Editor::marker_drag_finished_callback;
2035 drag_info.copied_location = new Location (*location);
2036 drag_info.pointer_frame_offset = drag_info.grab_frame - (is_start ? location->start() : location->end());
2038 update_marker_drag_item (location);
2040 if (location->is_mark()) {
2041 marker_drag_line->show();
2042 marker_drag_line->raise_to_top();
2045 range_marker_drag_rect->show();
2046 range_marker_drag_rect->raise_to_top();
2049 if (is_start) show_verbose_time_cursor (location->start(), 10);
2050 else show_verbose_time_cursor (location->end(), 10);
2054 Editor::marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2056 jack_nframes_t f_delta;
2057 Marker* marker = (Marker *) drag_info.data;
2058 Location *real_location;
2059 Location *copy_location;
2061 bool move_both = false;
2064 jack_nframes_t newframe;
2065 if (drag_info.pointer_frame_offset <= (long) drag_info.current_pointer_frame) {
2066 newframe = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2072 jack_nframes_t next = newframe;
2074 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2075 snap_to (newframe, 0, true);
2078 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
2082 /* call this to find out if its the start or end */
2084 real_location = find_location_from_marker (marker, is_start);
2086 /* use the copy that we're "dragging" around */
2088 copy_location = drag_info.copied_location;
2090 f_delta = copy_location->end() - copy_location->start();
2092 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
2096 if (copy_location->is_mark()) {
2099 copy_location->set_start (newframe);
2103 if (is_start) { // start-of-range marker
2106 copy_location->set_start (newframe);
2107 copy_location->set_end (newframe + f_delta);
2108 } else if (newframe < copy_location->end()) {
2109 copy_location->set_start (newframe);
2111 snap_to (next, 1, true);
2112 copy_location->set_end (next);
2113 copy_location->set_start (newframe);
2116 } else { // end marker
2119 copy_location->set_end (newframe);
2120 copy_location->set_start (newframe - f_delta);
2121 } else if (newframe > copy_location->start()) {
2122 copy_location->set_end (newframe);
2124 } else if (newframe > 0) {
2125 snap_to (next, -1, true);
2126 copy_location->set_start (next);
2127 copy_location->set_end (newframe);
2132 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
2133 drag_info.first_move = false;
2135 update_marker_drag_item (copy_location);
2137 LocationMarkers* lm = find_location_markers (real_location);
2138 lm->set_position (copy_location->start(), copy_location->end());
2140 show_verbose_time_cursor (newframe, 10);
2144 Editor::marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2146 if (drag_info.first_move) {
2147 marker_drag_motion_callback (item, event);
2151 Marker* marker = (Marker *) drag_info.data;
2155 begin_reversible_command ( _("move marker") );
2156 session->add_undo( session->locations()->get_memento() );
2158 Location * location = find_location_from_marker (marker, is_start);
2161 if (location->is_mark()) {
2162 location->set_start (drag_info.copied_location->start());
2164 location->set (drag_info.copied_location->start(), drag_info.copied_location->end());
2168 session->add_redo_no_execute( session->locations()->get_memento() );
2169 commit_reversible_command ();
2171 marker_drag_line->hide();
2172 range_marker_drag_rect->hide();
2176 Editor::start_meter_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2179 MeterMarker* meter_marker;
2181 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2182 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2186 meter_marker = dynamic_cast<MeterMarker*> (marker);
2188 MetricSection& section (meter_marker->meter());
2190 if (!section.movable()) {
2194 drag_info.item = item;
2195 drag_info.data = marker;
2196 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2197 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2201 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2203 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2207 Editor::start_meter_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2210 MeterMarker* meter_marker;
2212 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2213 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2217 meter_marker = dynamic_cast<MeterMarker*> (marker);
2219 // create a dummy marker for visual representation of moving the copy.
2220 // The actual copying is not done before we reach the finish callback.
2222 snprintf (name, sizeof(name), "%g/%g", meter_marker->meter().beats_per_bar(), meter_marker->meter().note_divisor ());
2223 MeterMarker* new_marker = new MeterMarker(*this, *meter_group, color_map[cMeterMarker], name,
2224 *new MeterSection(meter_marker->meter()));
2226 drag_info.item = &new_marker->the_item();
2227 drag_info.copy = true;
2228 drag_info.data = new_marker;
2229 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2230 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2234 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2236 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2240 Editor::meter_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2242 MeterMarker* marker = (MeterMarker *) drag_info.data;
2243 jack_nframes_t adjusted_frame;
2245 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2246 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2252 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2253 snap_to (adjusted_frame);
2256 if (adjusted_frame == drag_info.last_pointer_frame) return;
2258 marker->set_position (adjusted_frame);
2261 drag_info.last_pointer_frame = adjusted_frame;
2262 drag_info.first_move = false;
2264 show_verbose_time_cursor (adjusted_frame, 10);
2268 Editor::meter_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2270 if (drag_info.first_move) return;
2272 meter_marker_drag_motion_callback (drag_info.item, event);
2274 MeterMarker* marker = (MeterMarker *) drag_info.data;
2277 TempoMap& map (session->tempo_map());
2278 map.bbt_time (drag_info.last_pointer_frame, when);
2280 if (drag_info.copy == true) {
2281 begin_reversible_command (_("copy meter mark"));
2282 session->add_undo (map.get_memento());
2283 map.add_meter (marker->meter(), when);
2284 session->add_redo_no_execute (map.get_memento());
2285 commit_reversible_command ();
2287 // delete the dummy marker we used for visual representation of copying.
2288 // a new visual marker will show up automatically.
2291 begin_reversible_command (_("move meter mark"));
2292 session->add_undo (map.get_memento());
2293 map.move_meter (marker->meter(), when);
2294 session->add_redo_no_execute (map.get_memento());
2295 commit_reversible_command ();
2300 Editor::start_tempo_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2303 TempoMarker* tempo_marker;
2305 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2306 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2310 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2311 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2315 MetricSection& section (tempo_marker->tempo());
2317 if (!section.movable()) {
2321 drag_info.item = item;
2322 drag_info.data = marker;
2323 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2324 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2328 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2329 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2333 Editor::start_tempo_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2336 TempoMarker* tempo_marker;
2338 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2339 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2343 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2344 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2348 // create a dummy marker for visual representation of moving the copy.
2349 // The actual copying is not done before we reach the finish callback.
2351 snprintf (name, sizeof (name), "%.2f", tempo_marker->tempo().beats_per_minute());
2352 TempoMarker* new_marker = new TempoMarker(*this, *tempo_group, color_map[cTempoMarker], name,
2353 *new TempoSection(tempo_marker->tempo()));
2355 drag_info.item = &new_marker->the_item();
2356 drag_info.copy = true;
2357 drag_info.data = new_marker;
2358 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2359 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2363 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2365 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2369 Editor::tempo_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2371 TempoMarker* marker = (TempoMarker *) drag_info.data;
2372 jack_nframes_t adjusted_frame;
2374 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2375 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2381 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2382 snap_to (adjusted_frame);
2385 if (adjusted_frame == drag_info.last_pointer_frame) return;
2387 /* OK, we've moved far enough to make it worth actually move the thing. */
2389 marker->set_position (adjusted_frame);
2391 show_verbose_time_cursor (adjusted_frame, 10);
2393 drag_info.last_pointer_frame = adjusted_frame;
2394 drag_info.first_move = false;
2398 Editor::tempo_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2400 if (drag_info.first_move) return;
2402 tempo_marker_drag_motion_callback (drag_info.item, event);
2404 TempoMarker* marker = (TempoMarker *) drag_info.data;
2407 TempoMap& map (session->tempo_map());
2408 map.bbt_time (drag_info.last_pointer_frame, when);
2410 if (drag_info.copy == true) {
2411 begin_reversible_command (_("copy tempo mark"));
2412 session->add_undo (map.get_memento());
2413 map.add_tempo (marker->tempo(), when);
2414 session->add_redo_no_execute (map.get_memento());
2415 commit_reversible_command ();
2417 // delete the dummy marker we used for visual representation of copying.
2418 // a new visual marker will show up automatically.
2421 begin_reversible_command (_("move tempo mark"));
2422 session->add_undo (map.get_memento());
2423 map.move_tempo (marker->tempo(), when);
2424 session->add_redo_no_execute (map.get_memento());
2425 commit_reversible_command ();
2430 Editor::remove_gain_control_point (ArdourCanvas::Item*item, GdkEvent* event)
2432 ControlPoint* control_point;
2434 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2435 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2439 // We shouldn't remove the first or last gain point
2440 if (control_point->line.is_last_point(*control_point) ||
2441 control_point->line.is_first_point(*control_point)) {
2445 control_point->line.remove_point (*control_point);
2449 Editor::remove_control_point (ArdourCanvas::Item*item, GdkEvent* event)
2451 ControlPoint* control_point;
2453 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2454 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2458 control_point->line.remove_point (*control_point);
2462 Editor::start_control_point_grab (ArdourCanvas::Item* item, GdkEvent* event)
2464 ControlPoint* control_point;
2466 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2467 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2471 drag_info.item = item;
2472 drag_info.data = control_point;
2473 drag_info.motion_callback = &Editor::control_point_drag_motion_callback;
2474 drag_info.finished_callback = &Editor::control_point_drag_finished_callback;
2476 start_grab (event, fader_cursor);
2478 control_point->line.start_drag (control_point, 0);
2480 float fraction = 1.0 - (control_point->get_y() / control_point->line.height());
2481 set_verbose_canvas_cursor (control_point->line.get_verbose_cursor_string (fraction),
2482 drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
2484 show_verbose_canvas_cursor ();
2488 Editor::control_point_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2490 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2492 double cx = drag_info.current_pointer_x;
2493 double cy = drag_info.current_pointer_y;
2495 drag_info.cumulative_x_drag = cx - drag_info.grab_x ;
2496 drag_info.cumulative_y_drag = cy - drag_info.grab_y ;
2498 if (drag_info.x_constrained) {
2499 cx = drag_info.grab_x;
2501 if (drag_info.y_constrained) {
2502 cy = drag_info.grab_y;
2505 cp->line.parent_group().w2i (cx, cy);
2509 cy = min ((double) cp->line.height(), cy);
2511 //translate cx to frames
2512 jack_nframes_t cx_frames = unit_to_frame (cx);
2514 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier()) && !drag_info.x_constrained) {
2515 snap_to (cx_frames);
2518 float fraction = 1.0 - (cy / cp->line.height());
2522 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
2528 cp->line.point_drag (*cp, cx_frames , fraction, push);
2530 set_verbose_canvas_cursor_text (cp->line.get_verbose_cursor_string (fraction));
2532 drag_info.first_move = false;
2536 Editor::control_point_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2538 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2540 if (drag_info.first_move) {
2544 if ((event->type == GDK_BUTTON_RELEASE) && (event->button.button == 1) && Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
2545 reset_point_selection ();
2549 control_point_drag_motion_callback (item, event);
2551 cp->line.end_drag (cp);
2555 Editor::start_line_grab_from_regionview (ArdourCanvas::Item* item, GdkEvent* event)
2557 switch (mouse_mode) {
2559 assert(dynamic_cast<AudioRegionView*>(clicked_regionview));
2560 start_line_grab (dynamic_cast<AudioRegionView*>(clicked_regionview)->get_gain_line(), event);
2568 Editor::start_line_grab_from_line (ArdourCanvas::Item* item, GdkEvent* event)
2572 if ((al = reinterpret_cast<AutomationLine*> (item->get_data ("line"))) == 0) {
2573 fatal << _("programming error: line canvas item has no line pointer!") << endmsg;
2577 start_line_grab (al, event);
2581 Editor::start_line_grab (AutomationLine* line, GdkEvent* event)
2585 jack_nframes_t frame_within_region;
2587 /* need to get x coordinate in terms of parent (TimeAxisItemView)
2591 cx = event->button.x;
2592 cy = event->button.y;
2593 line->parent_group().w2i (cx, cy);
2594 frame_within_region = (jack_nframes_t) floor (cx * frames_per_unit);
2596 if (!line->control_points_adjacent (frame_within_region, current_line_drag_info.before,
2597 current_line_drag_info.after)) {
2598 /* no adjacent points */
2602 drag_info.item = &line->grab_item();
2603 drag_info.data = line;
2604 drag_info.motion_callback = &Editor::line_drag_motion_callback;
2605 drag_info.finished_callback = &Editor::line_drag_finished_callback;
2607 start_grab (event, fader_cursor);
2609 double fraction = 1.0 - (cy / line->height());
2611 line->start_drag (0, fraction);
2613 set_verbose_canvas_cursor (line->get_verbose_cursor_string (fraction),
2614 drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
2615 show_verbose_canvas_cursor ();
2619 Editor::line_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2621 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
2622 double cx = drag_info.current_pointer_x;
2623 double cy = drag_info.current_pointer_y;
2625 line->parent_group().w2i (cx, cy);
2628 fraction = 1.0 - (cy / line->height());
2632 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
2638 line->line_drag (current_line_drag_info.before, current_line_drag_info.after, fraction, push);
2640 set_verbose_canvas_cursor_text (line->get_verbose_cursor_string (fraction));
2644 Editor::line_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2646 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
2647 line_drag_motion_callback (item, event);
2652 Editor::start_region_grab (ArdourCanvas::Item* item, GdkEvent* event)
2654 if (selection->regions.empty() || clicked_regionview == 0) {
2658 drag_info.copy = false;
2659 drag_info.item = item;
2660 drag_info.data = clicked_regionview;
2661 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2662 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2667 TimeAxisView* tvp = clicked_trackview;
2668 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2670 if (tv && tv->is_audio_track()) {
2671 speed = tv->get_diskstream()->speed();
2674 drag_info.last_frame_position = (jack_nframes_t) (clicked_regionview->region().position() / speed);
2675 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2676 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2677 // we want a move threshold
2678 drag_info.want_move_threshold = true;
2680 show_verbose_time_cursor (drag_info.last_frame_position, 10);
2682 begin_reversible_command (_("move region(s)"));
2686 Editor::start_region_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2688 if (selection->regions.empty() || clicked_regionview == 0) {
2692 drag_info.copy = true;
2693 drag_info.item = item;
2694 drag_info.data = clicked_regionview;
2698 TimeAxisView* tv = &clicked_regionview->get_time_axis_view();
2699 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*>(tv);
2702 if (atv && atv->is_audio_track()) {
2703 speed = atv->get_diskstream()->speed();
2706 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2707 drag_info.last_frame_position = (jack_nframes_t) (clicked_regionview->region().position() / speed);
2708 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2709 // we want a move threshold
2710 drag_info.want_move_threshold = true;
2711 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2712 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2716 Editor::start_region_brush_grab (ArdourCanvas::Item* item, GdkEvent* event)
2718 if (selection->regions.empty() || clicked_regionview == 0) {
2722 drag_info.copy = false;
2723 drag_info.item = item;
2724 drag_info.data = clicked_regionview;
2725 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2726 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2731 TimeAxisView* tvp = clicked_trackview;
2732 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2734 if (tv && tv->is_audio_track()) {
2735 speed = tv->get_diskstream()->speed();
2738 drag_info.last_frame_position = (jack_nframes_t) (clicked_regionview->region().position() / speed);
2739 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2740 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2741 // we want a move threshold
2742 drag_info.want_move_threshold = true;
2743 drag_info.brushing = true;
2745 begin_reversible_command (_("Drag region brush"));
2749 Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2753 RegionView* rv = reinterpret_cast<RegionView*> (drag_info.data);
2754 jack_nframes_t pending_region_position = 0;
2755 int32_t pointer_y_span = 0, canvas_pointer_y_span = 0, original_pointer_order;
2756 int32_t visible_y_high = 0, visible_y_low = 512; //high meaning higher numbered.. not the height on the screen
2757 bool clamp_y_axis = false;
2758 vector<int32_t> height_list(512) ;
2759 vector<int32_t>::iterator j;
2761 show_verbose_time_cursor (drag_info.last_frame_position, 10);
2763 if (drag_info.copy && drag_info.move_threshold_passed && drag_info.want_move_threshold) {
2765 drag_info.want_move_threshold = false; // don't copy again
2767 /* this is committed in the grab finished callback. */
2769 begin_reversible_command (_("Drag region copy"));
2771 /* duplicate the region(s) */
2773 vector<RegionView*> new_regionviews;
2775 set<Playlist*> affected_playlists;
2776 pair<set<Playlist*>::iterator,bool> insert_result;
2778 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
2783 Playlist* to_playlist = rv->region().playlist();
2784 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
2786 insert_result = affected_playlists.insert (to_playlist);
2787 if (insert_result.second) {
2788 session->add_undo (to_playlist->get_memento ());
2791 latest_regionview = 0;
2793 sigc::connection c = atv->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
2795 /* create a new region with the same name. */
2797 // FIXME: ew. need a (virtual) Region::duplicate() or something?
2798 Region* newregion = NULL;
2799 if (dynamic_cast<AudioRegion*>(&rv->region()))
2800 newregion = new AudioRegion (dynamic_cast<AudioRegion&>(rv->region()));
2803 /* if the original region was locked, we don't care */
2805 newregion->set_locked (false);
2807 to_playlist->add_region (*newregion, (jack_nframes_t) (rv->region().position() * atv->get_diskstream()->speed()));
2811 if (latest_regionview) {
2812 new_regionviews.push_back (latest_regionview);
2816 if (new_regionviews.empty()) {
2820 /* reset selection to new regionviews */
2822 selection->set (new_regionviews);
2824 /* reset drag_info data to reflect the fact that we are dragging the copies */
2826 drag_info.data = new_regionviews.front();
2827 swap_grab (new_regionviews.front()->get_canvas_group (), 0, event->motion.time);
2830 /* Which trackview is this ? */
2832 TimeAxisView* tvp = trackview_by_y_position (drag_info.current_pointer_y);
2833 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
2835 /* The region motion is only processed if the pointer is over
2839 if (!tv || !tv->is_audio_track()) {
2840 /* To make sure we hide the verbose canvas cursor when the mouse is
2841 not held over and audiotrack.
2843 hide_verbose_canvas_cursor ();
2847 original_pointer_order = drag_info.last_trackview->order;
2849 /************************************************************
2851 ************************************************************/
2853 if (drag_info.brushing) {
2854 clamp_y_axis = true;
2859 if ((pointer_y_span = (drag_info.last_trackview->order - tv->order)) != 0) {
2861 int32_t children = 0, numtracks = 0;
2862 // XXX hard coding track limit, oh my, so very very bad
2863 bitset <1024> tracks (0x00);
2864 /* get a bitmask representing the visible tracks */
2866 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2867 TimeAxisView *tracklist_timeview;
2868 tracklist_timeview = (*i);
2869 AudioTimeAxisView* atv2 = dynamic_cast<AudioTimeAxisView*>(tracklist_timeview);
2870 list<TimeAxisView*> children_list;
2872 /* zeroes are audio tracks. ones are other types. */
2874 if (!atv2->hidden()) {
2876 if (visible_y_high < atv2->order) {
2877 visible_y_high = atv2->order;
2879 if (visible_y_low > atv2->order) {
2880 visible_y_low = atv2->order;
2883 if (!atv2->is_audio_track()) {
2884 tracks = tracks |= (0x01 << atv2->order);
2887 height_list[atv2->order] = (*i)->height;
2889 if ((children_list = atv2->get_child_list()).size() > 0) {
2890 for (list<TimeAxisView*>::iterator j = children_list.begin(); j != children_list.end(); ++j) {
2891 tracks = tracks |= (0x01 << (atv2->order + children));
2892 height_list[atv2->order + children] = (*j)->height;
2900 /* find the actual span according to the canvas */
2902 canvas_pointer_y_span = pointer_y_span;
2903 if (drag_info.last_trackview->order >= tv->order) {
2905 for (y = tv->order; y < drag_info.last_trackview->order; y++) {
2906 if (height_list[y] == 0 ) {
2907 canvas_pointer_y_span--;
2912 for (y = drag_info.last_trackview->order;y <= tv->order; y++) {
2913 if ( height_list[y] == 0 ) {
2914 canvas_pointer_y_span++;
2919 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
2920 RegionView* rv2 = (*i);
2921 double ix1, ix2, iy1, iy2;
2924 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
2925 rv2->get_canvas_group()->i2w (ix1, iy1);
2926 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
2927 RouteTimeAxisView* atv2 = dynamic_cast<RouteTimeAxisView*>(tvp2);
2929 if (atv2->order != original_pointer_order) {
2930 /* this isn't the pointer track */
2932 if (canvas_pointer_y_span > 0) {
2934 /* moving up the canvas */
2935 if ((atv2->order - canvas_pointer_y_span) >= visible_y_low) {
2937 int32_t visible_tracks = 0;
2938 while (visible_tracks < canvas_pointer_y_span ) {
2941 while (height_list[atv2->order - (visible_tracks - n)] == 0) {
2942 /* we're passing through a hidden track */
2947 if (tracks[atv2->order - (canvas_pointer_y_span - n)] != 0x00) {
2948 clamp_y_axis = true;
2952 clamp_y_axis = true;
2955 } else if (canvas_pointer_y_span < 0) {
2957 /*moving down the canvas*/
2959 if ((atv2->order - (canvas_pointer_y_span - n)) <= visible_y_high) { // we will overflow
2962 int32_t visible_tracks = 0;
2964 while (visible_tracks > canvas_pointer_y_span ) {
2967 while (height_list[atv2->order - (visible_tracks - n)] == 0) {
2971 if ( tracks[atv2->order - ( canvas_pointer_y_span - n)] != 0x00) {
2972 clamp_y_axis = true;
2977 clamp_y_axis = true;
2983 /* this is the pointer's track */
2984 if ((atv2->order - pointer_y_span) > visible_y_high) { // we will overflow
2985 clamp_y_axis = true;
2986 } else if ((atv2->order - pointer_y_span) < visible_y_low) { // we will underflow
2987 clamp_y_axis = true;
2995 } else if (drag_info.last_trackview == tv) {
2996 clamp_y_axis = true;
3000 if (!clamp_y_axis) {
3001 drag_info.last_trackview = tv;
3004 /************************************************************
3006 ************************************************************/
3008 /* compute the amount of pointer motion in frames, and where
3009 the region would be if we moved it by that much.
3012 if (drag_info.move_threshold_passed) {
3014 if ((int32_t)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
3016 jack_nframes_t sync_frame;
3017 jack_nframes_t sync_offset;
3020 pending_region_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
3022 sync_offset = rv->region().sync_offset (sync_dir);
3023 sync_frame = rv->region().adjust_to_sync (pending_region_position);
3025 /* we snap if the snap modifier is not enabled.
3028 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3029 snap_to (sync_frame);
3032 if (sync_frame - sync_offset <= sync_frame) {
3033 pending_region_position = sync_frame - (sync_dir*sync_offset);
3035 pending_region_position = 0;
3039 pending_region_position = 0;
3042 if (pending_region_position > max_frames - rv->region().length()) {
3043 pending_region_position = drag_info.last_frame_position;
3046 // printf ("3: pending_region_position= %lu %lu\n", pending_region_position, drag_info.last_frame_position );
3048 if (pending_region_position != drag_info.last_frame_position && !drag_info.x_constrained) {
3050 /* now compute the canvas unit distance we need to move the regiondrag_info.last_trackview->order
3051 to make it appear at the new location.
3054 if (pending_region_position > drag_info.last_frame_position) {
3055 x_delta = ((double) (pending_region_position - drag_info.last_frame_position) / frames_per_unit);
3057 x_delta = -((double) (drag_info.last_frame_position - pending_region_position) / frames_per_unit);
3060 drag_info.last_frame_position = pending_region_position;
3067 /* threshold not passed */
3072 /*************************************************************
3074 ************************************************************/
3076 if (x_delta == 0 && (pointer_y_span == 0)) {
3077 /* haven't reached next snap point, and we're not switching
3078 trackviews. nothing to do.
3084 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3086 RegionView* rv2 = (*i);
3088 // If any regionview is at zero, we need to know so we can stop further leftward motion.
3090 double ix1, ix2, iy1, iy2;
3091 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3092 rv2->get_canvas_group()->i2w (ix1, iy1);
3101 /*************************************************************
3103 ************************************************************/
3105 pair<set<Playlist*>::iterator,bool> insert_result;
3106 const list<RegionView*>& layered_regions = selection->regions.by_layer();
3108 for (list<RegionView*>::const_iterator i = layered_regions.begin(); i != layered_regions.end(); ++i) {
3110 RegionView* rv = (*i);
3111 double ix1, ix2, iy1, iy2;
3112 int32_t temp_pointer_y_span = pointer_y_span;
3114 /* get item BBox, which will be relative to parent. so we have
3115 to query on a child, then convert to world coordinates using
3119 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3120 rv->get_canvas_group()->i2w (ix1, iy1);
3121 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3122 AudioTimeAxisView* canvas_atv = dynamic_cast<AudioTimeAxisView*>(tvp2);
3123 AudioTimeAxisView* temp_atv;
3125 if ((pointer_y_span != 0) && !clamp_y_axis) {
3128 for (j = height_list.begin(); j!= height_list.end(); j++) {
3129 if (x == canvas_atv->order) {
3130 /* we found the track the region is on */
3131 if (x != original_pointer_order) {
3132 /*this isn't from the same track we're dragging from */
3133 temp_pointer_y_span = canvas_pointer_y_span;
3135 while (temp_pointer_y_span > 0) {
3136 /* we're moving up canvas-wise,
3137 so we need to find the next track height
3139 if (j != height_list.begin()) {
3142 if (x != original_pointer_order) {
3143 /* we're not from the dragged track, so ignore hidden tracks. */
3145 temp_pointer_y_span++;
3149 temp_pointer_y_span--;
3151 while (temp_pointer_y_span < 0) {
3153 if (x != original_pointer_order) {
3155 temp_pointer_y_span--;
3159 if (j != height_list.end()) {
3162 temp_pointer_y_span++;
3164 /* find out where we'll be when we move and set height accordingly */
3166 tvp2 = trackview_by_y_position (iy1 + y_delta);
3167 temp_atv = dynamic_cast<AudioTimeAxisView*>(tvp2);
3168 rv->set_height (temp_atv->height);
3170 /* if you un-comment the following, the region colours will follow the track colours whilst dragging,
3171 personally, i think this can confuse things, but never mind.
3174 //const GdkColor& col (temp_atv->view->get_region_color());
3175 //rv->set_color (const_cast<GdkColor&>(col));
3182 /* prevent the regionview from being moved to before
3183 the zero position on the canvas.
3188 if (-x_delta > ix1) {
3191 } else if ((x_delta > 0) &&(rv->region().last_frame() > max_frames - x_delta)) {
3192 x_delta = max_frames - rv->region().last_frame();
3195 if (drag_info.first_move) {
3197 /* hide any dependent views */
3199 // rv->get_time_axis_view().hide_dependent_views (*rv);
3201 /* this is subtle. raising the regionview itself won't help,
3202 because raise_to_top() just puts the item on the top of
3203 its parent's stack. so, we need to put the trackview canvas_display group
3204 on the top, since its parent is the whole canvas.
3207 rv->get_canvas_group()->raise_to_top();
3208 rv->get_time_axis_view().canvas_display->raise_to_top();
3209 cursor_group->raise_to_top();
3211 /* freeze the playlists from notifying till
3215 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*> (&rv->get_time_axis_view());
3216 if (atv && atv->is_audio_track()) {
3217 AudioPlaylist* pl = dynamic_cast<AudioPlaylist*>(atv->get_diskstream()->playlist());
3219 /* only freeze and capture state once */
3221 insert_result = motion_frozen_playlists.insert (pl);
3222 if (insert_result.second) {
3224 session->add_undo(pl->get_memento());
3230 if (drag_info.brushing) {
3231 mouse_brush_insert_region (rv, pending_region_position);
3233 rv->move (x_delta, y_delta);
3237 if (drag_info.first_move) {
3238 cursor_group->raise_to_top();
3241 drag_info.first_move = false;
3243 if (x_delta != 0 && !drag_info.brushing) {
3244 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3250 Editor::region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3252 jack_nframes_t where;
3253 RegionView* rv = reinterpret_cast<RegionView *> (drag_info.data);
3254 pair<set<Playlist*>::iterator,bool> insert_result;
3255 bool nocommit = true;
3257 RouteTimeAxisView* atv;
3258 bool regionview_y_movement;
3259 bool regionview_x_movement;
3261 /* first_move is set to false if the regionview has been moved in the
3265 if (drag_info.first_move) {
3272 /* The regionview has been moved at some stage during the grab so we need
3273 to account for any mouse movement between this event and the last one.
3276 region_drag_motion_callback (item, event);
3278 if (drag_info.brushing) {
3279 /* all changes were made during motion event handlers */
3283 /* adjust for track speed */
3286 atv = dynamic_cast<AudioTimeAxisView*> (drag_info.last_trackview);
3287 if (atv && atv->get_diskstream()) {
3288 speed = atv->get_diskstream()->speed();
3291 regionview_x_movement = (drag_info.last_frame_position != (jack_nframes_t) (rv->region().position()/speed));
3292 regionview_y_movement = (drag_info.last_trackview != &rv->get_time_axis_view());
3294 //printf ("last_frame: %s position is %lu %g\n", rv->get_time_axis_view().name().c_str(), drag_info.last_frame_position, speed);
3295 //printf ("last_rackview: %s \n", drag_info.last_trackview->name().c_str());
3297 if (regionview_y_movement) {
3299 /* motion between tracks */
3301 list<RegionView*> new_selection;
3303 /* moved to a different audio track. */
3305 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ) {
3307 RegionView* rv2 = (*i);
3309 /* the region that used to be in the old playlist is not
3310 moved to the new one - we make a copy of it. as a result,
3311 any existing editor for the region should no longer be
3315 if (!drag_info.copy) {
3316 rv2->hide_region_editor();
3318 new_selection.push_back (rv2);
3322 /* first, freeze the target tracks */
3324 for (list<RegionView*>::const_iterator i = new_selection.begin(); i != new_selection.end();i++ ) {
3326 Playlist* from_playlist;
3327 Playlist* to_playlist;
3329 double ix1, ix2, iy1, iy2;
3331 (*i)->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3332 (*i)->get_canvas_group()->i2w (ix1, iy1);
3333 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3334 AudioTimeAxisView* atv2 = dynamic_cast<AudioTimeAxisView*>(tvp2);
3336 from_playlist = (*i)->region().playlist();
3337 to_playlist = atv2->playlist();
3339 /* the from_playlist was frozen in the "first_move" case
3340 of the motion handler. the insert can fail,
3341 but that doesn't matter. it just means
3342 we already have the playlist in the list.
3345 motion_frozen_playlists.insert (from_playlist);
3347 /* only freeze the to_playlist once */
3349 insert_result = motion_frozen_playlists.insert(to_playlist);
3350 if (insert_result.second) {
3351 to_playlist->freeze();
3352 session->add_undo(to_playlist->get_memento());
3357 /* now do it again with the actual operations */
3359 for (list<RegionView*>::const_iterator i = new_selection.begin(); i != new_selection.end();i++ ) {
3361 Playlist* from_playlist;
3362 Playlist* to_playlist;
3364 double ix1, ix2, iy1, iy2;
3366 (*i)->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3367 (*i)->get_canvas_group()->i2w (ix1, iy1);
3368 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3369 AudioTimeAxisView* atv2 = dynamic_cast<AudioTimeAxisView*>(tvp2);
3371 from_playlist = (*i)->region().playlist();
3372 to_playlist = atv2->playlist();
3374 latest_regionview = 0;
3376 where = (jack_nframes_t) (unit_to_frame (ix1) * speed);
3377 Region* new_region = createRegion ((*i)->region());
3379 from_playlist->remove_region (&((*i)->region()));
3381 sigc::connection c = atv2->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3382 to_playlist->add_region (*new_region, where);
3385 if (latest_regionview) {
3386 selection->add (latest_regionview);
3392 /* motion within a single track */
3394 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3398 if (rv->region().locked()) {
3402 if (regionview_x_movement) {
3403 double ownspeed = 1.0;
3404 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*> (&(rv->get_time_axis_view()));
3406 if (atv && atv->get_diskstream()) {
3407 ownspeed = atv->get_diskstream()->speed();
3410 /* base the new region position on the current position of the regionview.*/
3412 double ix1, ix2, iy1, iy2;
3414 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3415 rv->get_canvas_group()->i2w (ix1, iy1);
3416 where = (jack_nframes_t) (unit_to_frame (ix1) * ownspeed);
3420 where = rv->region().position();
3423 rv->get_time_axis_view().reveal_dependent_views (*rv);
3425 /* no need to add an undo here, we did that when we added this playlist to motion_frozen playlists */
3427 rv->region().set_position (where, (void *) this);
3432 for (set<Playlist*>::iterator p = motion_frozen_playlists.begin(); p != motion_frozen_playlists.end(); ++p) {
3434 session->add_redo_no_execute ((*p)->get_memento());
3437 motion_frozen_playlists.clear ();
3440 commit_reversible_command ();
3445 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
3447 /* Either add to or set the set the region selection, unless
3448 this is an alignment click (control used)
3451 if (Keyboard::modifier_state_contains (event->state, Keyboard::Control)) {
3452 TimeAxisView* tv = &rv.get_time_axis_view();
3453 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*>(tv);
3455 if (atv && atv->is_audio_track()) {
3456 speed = atv->get_diskstream()->speed();
3459 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Alt))) {
3461 align_region (rv.region(), SyncPoint, (jack_nframes_t) (edit_cursor->current_frame * speed));
3463 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Shift))) {
3465 align_region (rv.region(), End, (jack_nframes_t) (edit_cursor->current_frame * speed));
3469 align_region (rv.region(), Start, (jack_nframes_t) (edit_cursor->current_frame * speed));
3475 Editor::show_verbose_time_cursor (jack_nframes_t frame, double offset, double xpos, double ypos)
3486 switch (ARDOUR_UI::instance()->secondary_clock.mode ()) {
3487 case AudioClock::BBT:
3488 session->bbt_time (frame, bbt);
3489 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, bbt.bars, bbt.beats, bbt.ticks);
3492 case AudioClock::SMPTE:
3493 session->smpte_time (frame, smpte);
3494 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
3497 case AudioClock::MinSec:
3498 /* XXX fix this to compute min/sec properly */
3499 session->smpte_time (frame, smpte);
3500 secs = smpte.seconds + ((float) smpte.frames / session->smpte_frames_per_second);
3501 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", smpte.hours, smpte.minutes, secs);
3505 snprintf (buf, sizeof(buf), "%u", frame);
3509 if (xpos >= 0 && ypos >=0) {
3510 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
3513 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
3515 show_verbose_canvas_cursor ();
3519 Editor::show_verbose_duration_cursor (jack_nframes_t start, jack_nframes_t end, double offset, double xpos, double ypos)
3526 Meter meter_at_start(session->tempo_map().meter_at(start));
3532 switch (ARDOUR_UI::instance()->secondary_clock.mode ()) {
3533 case AudioClock::BBT:
3534 session->bbt_time (start, sbbt);
3535 session->bbt_time (end, ebbt);
3538 /* XXX this computation won't work well if the
3539 user makes a selection that spans any meter changes.
3542 ebbt.bars -= sbbt.bars;
3543 if (ebbt.beats >= sbbt.beats) {
3544 ebbt.beats -= sbbt.beats;
3547 ebbt.beats = int(meter_at_start.beats_per_bar()) + ebbt.beats - sbbt.beats;
3549 if (ebbt.ticks >= sbbt.ticks) {
3550 ebbt.ticks -= sbbt.ticks;
3553 ebbt.ticks = int(Meter::ticks_per_beat) + ebbt.ticks - sbbt.ticks;
3556 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, ebbt.bars, ebbt.beats, ebbt.ticks);
3559 case AudioClock::SMPTE:
3560 session->smpte_duration (end - start, smpte);
3561 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
3564 case AudioClock::MinSec:
3565 /* XXX fix this to compute min/sec properly */
3566 session->smpte_duration (end - start, smpte);
3567 secs = smpte.seconds + ((float) smpte.frames / session->smpte_frames_per_second);
3568 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", smpte.hours, smpte.minutes, secs);
3572 snprintf (buf, sizeof(buf), "%u", end - start);
3576 if (xpos >= 0 && ypos >=0) {
3577 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
3580 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
3582 show_verbose_canvas_cursor ();
3586 Editor::collect_new_region_view (RegionView* rv)
3588 latest_regionview = rv;
3592 Editor::start_selection_grab (ArdourCanvas::Item* item, GdkEvent* event)
3594 if (clicked_regionview == 0) {
3598 /* lets try to create new Region for the selection */
3600 vector<AudioRegion*> new_regions;
3601 create_region_from_selection (new_regions);
3603 if (new_regions.empty()) {
3607 /* XXX fix me one day to use all new regions */
3609 Region* region = new_regions.front();
3611 /* add it to the current stream/playlist.
3613 tricky: the streamview for the track will add a new regionview. we will
3614 catch the signal it sends when it creates the regionview to
3615 set the regionview we want to then drag.
3618 latest_regionview = 0;
3619 sigc::connection c = clicked_audio_trackview->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3621 /* A selection grab currently creates two undo/redo operations, one for
3622 creating the new region and another for moving it.
3625 begin_reversible_command (_("selection grab"));
3627 Playlist* playlist = clicked_trackview->playlist();
3629 session->add_undo (playlist->get_memento ());
3630 clicked_trackview->playlist()->add_region (*region, selection->time[clicked_selection].start);
3631 session->add_redo_no_execute (playlist->get_memento ());
3633 commit_reversible_command ();
3637 if (latest_regionview == 0) {
3638 /* something went wrong */
3642 /* we need to deselect all other regionviews, and select this one
3643 i'm ignoring undo stuff, because the region creation will take care of it */
3644 selection->set (latest_regionview);
3646 drag_info.item = latest_regionview->get_canvas_group();
3647 drag_info.data = latest_regionview;
3648 drag_info.motion_callback = &Editor::region_drag_motion_callback;
3649 drag_info.finished_callback = &Editor::region_drag_finished_callback;
3653 drag_info.last_trackview = clicked_trackview;
3654 drag_info.last_frame_position = latest_regionview->region().position();
3655 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
3657 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3661 Editor::cancel_selection ()
3663 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
3664 (*i)->hide_selection ();
3666 begin_reversible_command (_("cancel selection"));
3667 selection->clear ();
3668 clicked_selection = 0;
3669 commit_reversible_command ();
3673 Editor::start_selection_op (ArdourCanvas::Item* item, GdkEvent* event, SelectionOp op)
3675 jack_nframes_t start = 0;
3676 jack_nframes_t end = 0;
3682 drag_info.item = item;
3683 drag_info.motion_callback = &Editor::drag_selection;
3684 drag_info.finished_callback = &Editor::end_selection_op;
3689 case CreateSelection:
3690 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
3691 drag_info.copy = true;
3693 drag_info.copy = false;
3695 start_grab (event, selector_cursor);
3698 case SelectionStartTrim:
3699 if (clicked_trackview) {
3700 clicked_trackview->order_selection_trims (item, true);
3702 start_grab (event, trimmer_cursor);
3703 start = selection->time[clicked_selection].start;
3704 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
3707 case SelectionEndTrim:
3708 if (clicked_trackview) {
3709 clicked_trackview->order_selection_trims (item, false);
3711 start_grab (event, trimmer_cursor);
3712 end = selection->time[clicked_selection].end;
3713 drag_info.pointer_frame_offset = drag_info.grab_frame - end;
3717 start = selection->time[clicked_selection].start;
3719 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
3723 if (selection_op == SelectionMove) {
3724 show_verbose_time_cursor(start, 10);
3726 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
3731 Editor::drag_selection (ArdourCanvas::Item* item, GdkEvent* event)
3733 jack_nframes_t start = 0;
3734 jack_nframes_t end = 0;
3735 jack_nframes_t length;
3736 jack_nframes_t pending_position;
3738 if ((int32_t) drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
3739 pending_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
3742 pending_position = 0;
3745 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3746 snap_to (pending_position);
3749 /* only alter selection if the current frame is
3750 different from the last frame position (adjusted)
3753 if (pending_position == drag_info.last_pointer_frame) return;
3755 switch (selection_op) {
3756 case CreateSelection:
3758 if (drag_info.first_move) {
3759 snap_to (drag_info.grab_frame);
3762 if (pending_position < drag_info.grab_frame) {
3763 start = pending_position;
3764 end = drag_info.grab_frame;
3766 end = pending_position;
3767 start = drag_info.grab_frame;
3770 /* first drag: Either add to the selection
3771 or create a new selection->
3774 if (drag_info.first_move) {
3776 begin_reversible_command (_("range selection"));
3778 if (drag_info.copy) {
3779 /* adding to the selection */
3780 clicked_selection = selection->add (start, end);
3781 drag_info.copy = false;
3783 /* new selection-> */
3784 clicked_selection = selection->set (clicked_trackview, start, end);
3789 case SelectionStartTrim:
3791 if (drag_info.first_move) {
3792 begin_reversible_command (_("trim selection start"));
3795 start = selection->time[clicked_selection].start;
3796 end = selection->time[clicked_selection].end;
3798 if (pending_position > end) {
3801 start = pending_position;
3805 case SelectionEndTrim:
3807 if (drag_info.first_move) {
3808 begin_reversible_command (_("trim selection end"));
3811 start = selection->time[clicked_selection].start;
3812 end = selection->time[clicked_selection].end;
3814 if (pending_position < start) {
3817 end = pending_position;
3824 if (drag_info.first_move) {
3825 begin_reversible_command (_("move selection"));
3828 start = selection->time[clicked_selection].start;
3829 end = selection->time[clicked_selection].end;
3831 length = end - start;
3833 start = pending_position;
3836 end = start + length;
3841 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
3842 start_canvas_autoscroll (1);
3846 selection->replace (clicked_selection, start, end);
3849 drag_info.last_pointer_frame = pending_position;
3850 drag_info.first_move = false;
3852 if (selection_op == SelectionMove) {
3853 show_verbose_time_cursor(start, 10);
3855 show_verbose_time_cursor(pending_position, 10);
3860 Editor::end_selection_op (ArdourCanvas::Item* item, GdkEvent* event)
3862 if (!drag_info.first_move) {
3863 drag_selection (item, event);
3864 /* XXX this is not object-oriented programming at all. ick */
3865 if (selection->time.consolidate()) {
3866 selection->TimeChanged ();
3868 commit_reversible_command ();
3870 /* just a click, no pointer movement.*/
3872 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
3874 selection->clear_time();
3879 /* XXX what happens if its a music selection? */
3880 session->set_audio_range (selection->time);
3881 stop_canvas_autoscroll ();
3885 Editor::start_trim (ArdourCanvas::Item* item, GdkEvent* event)
3888 TimeAxisView* tvp = clicked_trackview;
3889 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
3891 if (tv && tv->is_audio_track()) {
3892 speed = tv->get_diskstream()->speed();
3895 jack_nframes_t region_start = (jack_nframes_t) (clicked_regionview->region().position() / speed);
3896 jack_nframes_t region_end = (jack_nframes_t) (clicked_regionview->region().last_frame() / speed);
3897 jack_nframes_t region_length = (jack_nframes_t) (clicked_regionview->region().length() / speed);
3899 motion_frozen_playlists.clear();
3901 //drag_info.item = clicked_regionview->get_name_highlight();
3902 drag_info.item = item;
3903 drag_info.motion_callback = &Editor::trim_motion_callback;
3904 drag_info.finished_callback = &Editor::trim_finished_callback;
3906 start_grab (event, trimmer_cursor);
3908 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
3909 trim_op = ContentsTrim;
3911 /* These will get overridden for a point trim.*/
3912 if (drag_info.current_pointer_frame < (region_start + region_length/2)) {
3913 /* closer to start */
3914 trim_op = StartTrim;
3915 } else if (drag_info.current_pointer_frame > (region_end - region_length/2)) {
3923 show_verbose_time_cursor(region_start, 10);
3926 show_verbose_time_cursor(region_end, 10);
3929 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
3935 Editor::trim_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
3937 RegionView* rv = clicked_regionview;
3938 jack_nframes_t frame_delta = 0;
3939 bool left_direction;
3940 bool obey_snap = !Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier());
3942 /* snap modifier works differently here..
3943 its' current state has to be passed to the
3944 various trim functions in order to work properly
3948 TimeAxisView* tvp = clicked_trackview;
3949 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
3950 pair<set<Playlist*>::iterator,bool> insert_result;
3952 if (tv && tv->is_audio_track()) {
3953 speed = tv->get_diskstream()->speed();
3956 if (drag_info.last_pointer_frame > drag_info.current_pointer_frame) {
3957 left_direction = true;
3959 left_direction = false;
3963 snap_to (drag_info.current_pointer_frame);
3966 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
3970 if (drag_info.first_move) {
3976 trim_type = "Region start trim";
3979 trim_type = "Region end trim";
3982 trim_type = "Region content trim";
3986 begin_reversible_command (trim_type);
3988 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3989 (*i)->region().freeze ();
3991 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
3993 arv->temporarily_hide_envelope ();
3995 Playlist * pl = (*i)->region().playlist();
3996 insert_result = motion_frozen_playlists.insert (pl);
3997 if (insert_result.second) {
3998 session->add_undo (pl->get_memento());
4003 if (left_direction) {
4004 frame_delta = (drag_info.last_pointer_frame - drag_info.current_pointer_frame);
4006 frame_delta = (drag_info.current_pointer_frame - drag_info.last_pointer_frame);
4011 if ((left_direction == false) && (drag_info.current_pointer_frame <= rv->region().first_frame()/speed)) {
4014 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4015 single_start_trim (**i, frame_delta, left_direction, obey_snap);
4021 if ((left_direction == true) && (drag_info.current_pointer_frame > (jack_nframes_t) (rv->region().last_frame()/speed))) {
4024 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4025 single_end_trim (**i, frame_delta, left_direction, obey_snap);
4032 bool swap_direction = false;
4034 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
4035 swap_direction = true;
4038 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4039 i != selection->regions.by_layer().end(); ++i)
4041 single_contents_trim (**i, frame_delta, left_direction, swap_direction, obey_snap);
4049 show_verbose_time_cursor((jack_nframes_t) (rv->region().position()/speed), 10);
4052 show_verbose_time_cursor((jack_nframes_t) (rv->region().last_frame()/speed), 10);
4055 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4059 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4060 drag_info.first_move = false;
4064 Editor::single_contents_trim (RegionView& rv, jack_nframes_t frame_delta, bool left_direction, bool swap_direction, bool obey_snap)
4066 Region& region (rv.region());
4068 if (region.locked()) {
4072 jack_nframes_t new_bound;
4075 TimeAxisView* tvp = clicked_trackview;
4076 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4078 if (tv && tv->is_audio_track()) {
4079 speed = tv->get_diskstream()->speed();
4082 if (left_direction) {
4083 if (swap_direction) {
4084 new_bound = (jack_nframes_t) (region.position()/speed) + frame_delta;
4086 new_bound = (jack_nframes_t) (region.position()/speed) - frame_delta;
4089 if (swap_direction) {
4090 new_bound = (jack_nframes_t) (region.position()/speed) - frame_delta;
4092 new_bound = (jack_nframes_t) (region.position()/speed) + frame_delta;
4097 snap_to (new_bound);
4099 region.trim_start ((jack_nframes_t) (new_bound * speed), this);
4100 rv.region_changed (StartChanged);
4104 Editor::single_start_trim (RegionView& rv, jack_nframes_t frame_delta, bool left_direction, bool obey_snap)
4106 Region& region (rv.region());
4108 if (region.locked()) {
4112 jack_nframes_t new_bound;
4115 TimeAxisView* tvp = clicked_trackview;
4116 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
4118 if (tv && tv->is_audio_track()) {
4119 speed = tv->get_diskstream()->speed();
4122 if (left_direction) {
4123 new_bound = (jack_nframes_t) (region.position()/speed) - frame_delta;
4125 new_bound = (jack_nframes_t) (region.position()/speed) + frame_delta;
4129 snap_to (new_bound, (left_direction ? 0 : 1));
4132 region.trim_front ((jack_nframes_t) (new_bound * speed), this);
4134 rv.region_changed (Change (LengthChanged|PositionChanged|StartChanged));
4138 Editor::single_end_trim (RegionView& rv, jack_nframes_t frame_delta, bool left_direction, bool obey_snap)
4140 Region& region (rv.region());
4142 if (region.locked()) {
4146 jack_nframes_t new_bound;
4149 TimeAxisView* tvp = clicked_trackview;
4150 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
4152 if (tv && tv->is_audio_track()) {
4153 speed = tv->get_diskstream()->speed();
4156 if (left_direction) {
4157 new_bound = (jack_nframes_t) ((region.last_frame() + 1)/speed) - frame_delta;
4159 new_bound = (jack_nframes_t) ((region.last_frame() + 1)/speed) + frame_delta;
4163 snap_to (new_bound);
4165 region.trim_end ((jack_nframes_t) (new_bound * speed), this);
4166 rv.region_changed (LengthChanged);
4170 Editor::trim_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
4172 if (!drag_info.first_move) {
4173 trim_motion_callback (item, event);
4175 if (!clicked_regionview->get_selected()) {
4176 thaw_region_after_trim (*clicked_regionview);
4179 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4180 i != selection->regions.by_layer().end(); ++i)
4182 thaw_region_after_trim (**i);
4186 for (set<Playlist*>::iterator p = motion_frozen_playlists.begin(); p != motion_frozen_playlists.end(); ++p) {
4188 session->add_redo_no_execute ((*p)->get_memento());
4191 motion_frozen_playlists.clear ();
4193 commit_reversible_command();
4195 /* no mouse movement */
4201 Editor::point_trim (GdkEvent* event)
4203 RegionView* rv = clicked_regionview;
4204 jack_nframes_t new_bound = drag_info.current_pointer_frame;
4206 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4207 snap_to (new_bound);
4210 /* Choose action dependant on which button was pressed */
4211 switch (event->button.button) {
4213 trim_op = StartTrim;
4214 begin_reversible_command (_("Start point trim"));
4216 if (rv->get_selected()) {
4218 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4219 i != selection->regions.by_layer().end(); ++i)
4221 if (!(*i)->region().locked()) {
4222 session->add_undo ((*i)->region().playlist()->get_memento());
4223 (*i)->region().trim_front (new_bound, this);
4224 session->add_redo_no_execute ((*i)->region().playlist()->get_memento());
4230 if (!rv->region().locked()) {
4231 session->add_undo (rv->region().playlist()->get_memento());
4232 rv->region().trim_front (new_bound, this);
4233 session->add_redo_no_execute (rv->region().playlist()->get_memento());
4237 commit_reversible_command();
4242 begin_reversible_command (_("End point trim"));
4244 if (rv->get_selected()) {
4246 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
4248 if (!(*i)->region().locked()) {
4249 session->add_undo ((*i)->region().playlist()->get_memento());
4250 (*i)->region().trim_end (new_bound, this);
4251 session->add_redo_no_execute ((*i)->region().playlist()->get_memento());
4257 if (!rv->region().locked()) {
4258 session->add_undo (rv->region().playlist()->get_memento());
4259 rv->region().trim_end (new_bound, this);
4260 session->add_redo_no_execute (rv->region().playlist()->get_memento());
4264 commit_reversible_command();
4273 Editor::thaw_region_after_trim (RegionView& rv)
4275 Region& region (rv.region());
4277 if (region.locked()) {
4281 region.thaw (_("trimmed region"));
4282 session->add_redo_no_execute (region.playlist()->get_memento());
4284 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(&rv);
4286 arv->unhide_envelope ();
4290 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* event)
4295 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
4296 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
4300 Location* location = find_location_from_marker (marker, is_start);
4301 location->set_hidden (true, this);
4306 Editor::start_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event, RangeMarkerOp op)
4312 drag_info.item = item;
4313 drag_info.motion_callback = &Editor::drag_range_markerbar_op;
4314 drag_info.finished_callback = &Editor::end_range_markerbar_op;
4316 range_marker_op = op;
4318 if (!temp_location) {
4319 temp_location = new Location;
4323 case CreateRangeMarker:
4324 case CreateTransportMarker:
4326 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
4327 drag_info.copy = true;
4329 drag_info.copy = false;
4331 start_grab (event, selector_cursor);
4335 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4340 Editor::drag_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4342 jack_nframes_t start = 0;
4343 jack_nframes_t end = 0;
4344 ArdourCanvas::SimpleRect *crect = (range_marker_op == CreateRangeMarker) ? range_bar_drag_rect: transport_bar_drag_rect;
4346 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4347 snap_to (drag_info.current_pointer_frame);
4350 /* only alter selection if the current frame is
4351 different from the last frame position.
4354 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4356 switch (range_marker_op) {
4357 case CreateRangeMarker:
4358 case CreateTransportMarker:
4359 if (drag_info.first_move) {
4360 snap_to (drag_info.grab_frame);
4363 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4364 start = drag_info.current_pointer_frame;
4365 end = drag_info.grab_frame;
4367 end = drag_info.current_pointer_frame;
4368 start = drag_info.grab_frame;
4371 /* first drag: Either add to the selection
4372 or create a new selection.
4375 if (drag_info.first_move) {
4377 temp_location->set (start, end);
4381 update_marker_drag_item (temp_location);
4382 range_marker_drag_rect->show();
4383 range_marker_drag_rect->raise_to_top();
4389 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
4390 start_canvas_autoscroll (1);
4394 temp_location->set (start, end);
4396 double x1 = frame_to_pixel (start);
4397 double x2 = frame_to_pixel (end);
4398 crect->property_x1() = x1;
4399 crect->property_x2() = x2;
4401 update_marker_drag_item (temp_location);
4404 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4405 drag_info.first_move = false;
4407 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4412 Editor::end_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4414 Location * newloc = 0;
4416 if (!drag_info.first_move) {
4417 drag_range_markerbar_op (item, event);
4419 switch (range_marker_op) {
4420 case CreateRangeMarker:
4421 begin_reversible_command (_("new range marker"));
4422 session->add_undo (session->locations()->get_memento());
4423 newloc = new Location(temp_location->start(), temp_location->end(), "unnamed", Location::IsRangeMarker);
4424 session->locations()->add (newloc, true);
4425 session->add_redo_no_execute (session->locations()->get_memento());
4426 commit_reversible_command ();
4428 range_bar_drag_rect->hide();
4429 range_marker_drag_rect->hide();
4432 case CreateTransportMarker:
4433 // popup menu to pick loop or punch
4434 new_transport_marker_context_menu (&event->button, item);
4439 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
4441 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
4443 jack_nframes_t start;
4446 start = session->locations()->first_mark_before (drag_info.grab_frame);
4447 end = session->locations()->first_mark_after (drag_info.grab_frame);
4449 if (end == max_frames) {
4450 end = session->current_end_frame ();
4454 start = session->current_start_frame ();
4457 switch (mouse_mode) {
4459 /* find the two markers on either side and then make the selection from it */
4460 cerr << "select between " << start << " .. " << end << endl;
4461 select_all_within (start, end, 0.0f, FLT_MAX, Selection::Set);
4465 /* find the two markers on either side of the click and make the range out of it */
4466 selection->set (0, start, end);
4475 stop_canvas_autoscroll ();
4481 Editor::start_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4483 drag_info.item = item;
4484 drag_info.motion_callback = &Editor::drag_mouse_zoom;
4485 drag_info.finished_callback = &Editor::end_mouse_zoom;
4487 start_grab (event, zoom_cursor);
4489 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4493 Editor::drag_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4495 jack_nframes_t start;
4498 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4499 snap_to (drag_info.current_pointer_frame);
4501 if (drag_info.first_move) {
4502 snap_to (drag_info.grab_frame);
4506 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4508 /* base start and end on initial click position */
4509 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4510 start = drag_info.current_pointer_frame;
4511 end = drag_info.grab_frame;
4513 end = drag_info.current_pointer_frame;
4514 start = drag_info.grab_frame;
4519 if (drag_info.first_move) {
4521 zoom_rect->raise_to_top();
4524 reposition_zoom_rect(start, end);
4526 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4527 drag_info.first_move = false;
4529 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4534 Editor::end_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4536 if (!drag_info.first_move) {
4537 drag_mouse_zoom (item, event);
4539 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
4540 temporal_zoom_by_frame (drag_info.grab_frame, drag_info.last_pointer_frame, "mouse zoom");
4542 temporal_zoom_by_frame (drag_info.last_pointer_frame, drag_info.grab_frame, "mouse zoom");
4545 temporal_zoom_to_frame (false, drag_info.grab_frame);
4547 temporal_zoom_step (false);
4548 center_screen (drag_info.grab_frame);
4556 Editor::reposition_zoom_rect (jack_nframes_t start, jack_nframes_t end)
4558 double x1 = frame_to_pixel (start);
4559 double x2 = frame_to_pixel (end);
4560 double y2 = canvas_height - 2;
4562 zoom_rect->property_x1() = x1;
4563 zoom_rect->property_y1() = 1.0;
4564 zoom_rect->property_x2() = x2;
4565 zoom_rect->property_y2() = y2;
4569 Editor::start_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4571 drag_info.item = item;
4572 drag_info.motion_callback = &Editor::drag_rubberband_select;
4573 drag_info.finished_callback = &Editor::end_rubberband_select;
4575 start_grab (event, cross_hair_cursor);
4577 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4581 Editor::drag_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4583 jack_nframes_t start;
4588 /* use a bigger drag threshold than the default */
4590 if (abs ((int) (drag_info.current_pointer_frame - drag_info.grab_frame)) < 8) {
4594 // if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4595 // snap_to (drag_info.current_pointer_frame);
4597 // if (drag_info.first_move) {
4598 // snap_to (drag_info.grab_frame);
4603 /* base start and end on initial click position */
4604 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4605 start = drag_info.current_pointer_frame;
4606 end = drag_info.grab_frame;
4608 end = drag_info.current_pointer_frame;
4609 start = drag_info.grab_frame;
4612 if (drag_info.current_pointer_y < drag_info.grab_y) {
4613 y1 = drag_info.current_pointer_y;
4614 y2 = drag_info.grab_y;
4617 y2 = drag_info.current_pointer_y;
4618 y1 = drag_info.grab_y;
4622 if (start != end || y1 != y2) {
4624 double x1 = frame_to_pixel (start);
4625 double x2 = frame_to_pixel (end);
4627 rubberband_rect->property_x1() = x1;
4628 rubberband_rect->property_y1() = y1;
4629 rubberband_rect->property_x2() = x2;
4630 rubberband_rect->property_y2() = y2;
4632 rubberband_rect->show();
4633 rubberband_rect->raise_to_top();
4635 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4636 drag_info.first_move = false;
4638 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4643 Editor::end_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4645 if (!drag_info.first_move) {
4647 drag_rubberband_select (item, event);
4650 if (drag_info.current_pointer_y < drag_info.grab_y) {
4651 y1 = drag_info.current_pointer_y;
4652 y2 = drag_info.grab_y;
4655 y2 = drag_info.current_pointer_y;
4656 y1 = drag_info.grab_y;
4660 Selection::Operation op = Keyboard::selection_type (event->button.state);
4663 begin_reversible_command (_("select regions"));
4665 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
4666 commit = select_all_within (drag_info.grab_frame, drag_info.last_pointer_frame, y1, y2, op);
4668 commit = select_all_within (drag_info.last_pointer_frame, drag_info.grab_frame, y1, y2, op);
4672 commit_reversible_command ();
4676 selection->clear_regions();
4677 selection->clear_points ();
4678 selection->clear_lines ();
4681 rubberband_rect->hide();
4686 Editor::mouse_rename_region (ArdourCanvas::Item* item, GdkEvent* event)
4688 using namespace Gtkmm2ext;
4690 ArdourPrompter prompter (false);
4692 prompter.set_prompt (_("Name for region:"));
4693 prompter.set_initial_text (clicked_regionview->region().name());
4694 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
4695 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
4696 prompter.show_all ();
4697 switch (prompter.run ()) {
4698 case Gtk::RESPONSE_ACCEPT:
4700 prompter.get_result(str);
4702 clicked_regionview->region().set_name (str);
4710 Editor::start_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
4712 drag_info.item = item;
4713 drag_info.motion_callback = &Editor::time_fx_motion;
4714 drag_info.finished_callback = &Editor::end_time_fx;
4718 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4722 Editor::time_fx_motion (ArdourCanvas::Item *item, GdkEvent* event)
4724 RegionView* rv = clicked_regionview;
4726 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4727 snap_to (drag_info.current_pointer_frame);
4730 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
4734 if (drag_info.current_pointer_frame > rv->region().position()) {
4735 rv->get_time_axis_view().show_timestretch (rv->region().position(), drag_info.current_pointer_frame);
4738 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4739 drag_info.first_move = false;
4741 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4745 Editor::end_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
4747 clicked_regionview->get_time_axis_view().hide_timestretch ();
4749 if (drag_info.first_move) {
4753 jack_nframes_t newlen = drag_info.last_pointer_frame - clicked_regionview->region().position();
4754 float percentage = (float) ((double) newlen - (double) clicked_regionview->region().length()) / ((double) newlen) * 100.0f;
4756 begin_reversible_command (_("timestretch"));
4758 if (run_timestretch (selection->regions, percentage) == 0) {
4759 session->commit_reversible_command ();
4764 Editor::mouse_brush_insert_region (RegionView* rv, jack_nframes_t pos)
4766 /* no brushing without a useful snap setting */
4769 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
4772 switch (snap_mode) {
4774 return; /* can't work because it allows region to be placed anywhere */
4779 switch (snap_type) {
4782 case SnapToEditCursor:
4789 /* don't brush a copy over the original */
4791 if (pos == rv->region().position()) {
4795 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*>(&arv->get_time_axis_view());
4797 if (atv == 0 || !atv->is_audio_track()) {
4801 Playlist* playlist = atv->playlist();
4802 double speed = atv->get_diskstream()->speed();
4804 session->add_undo (playlist->get_memento());
4805 playlist->add_region (*(new AudioRegion (arv->audio_region())), (jack_nframes_t) (pos * speed));
4806 session->add_redo_no_execute (playlist->get_memento());
4808 // playlist is frozen, so we have to update manually
4810 playlist->StateChanged (Change (~0)); /* EMIT SIGNAL */
4814 Editor::track_height_step_timeout ()
4817 struct timeval delta;
4819 gettimeofday (&now, 0);
4820 timersub (&now, &last_track_height_step_timestamp, &delta);
4822 if (delta.tv_sec * 1000000 + delta.tv_usec > 250000) { /* milliseconds */
4823 current_stepping_trackview = 0;