2 Copyright (C) 2000-2001 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
28 #include <pbd/error.h>
29 #include <gtkmm2ext/utils.h>
30 #include <pbd/memento_command.h>
32 #include "ardour_ui.h"
34 #include "time_axis_view.h"
35 #include "audio_time_axis.h"
36 #include "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/profile.h>
50 #include <ardour/route.h>
51 #include <ardour/audio_track.h>
52 #include <ardour/audio_diskstream.h>
53 #include <ardour/playlist.h>
54 #include <ardour/audioplaylist.h>
55 #include <ardour/audioregion.h>
56 #include <ardour/dB.h>
57 #include <ardour/utils.h>
58 #include <ardour/region_factory.h>
65 using namespace ARDOUR;
69 using namespace Editing;
72 Editor::event_frame (GdkEvent* event, double* pcx, double* pcy)
86 switch (event->type) {
87 case GDK_BUTTON_RELEASE:
88 case GDK_BUTTON_PRESS:
89 case GDK_2BUTTON_PRESS:
90 case GDK_3BUTTON_PRESS:
91 track_canvas.w2c(event->button.x, event->button.y, *pcx, *pcy);
93 case GDK_MOTION_NOTIFY:
94 track_canvas.w2c(event->motion.x, event->motion.y, *pcx, *pcy);
96 case GDK_ENTER_NOTIFY:
97 case GDK_LEAVE_NOTIFY:
98 track_canvas.w2c(event->crossing.x, event->crossing.y, *pcx, *pcy);
101 case GDK_KEY_RELEASE:
102 // track_canvas.w2c(event->key.x, event->key.y, *pcx, *pcy);
105 warning << string_compose (_("Editor::event_frame() used on unhandled event type %1"), event->type) << endmsg;
109 /* note that pixel_to_frame() never returns less than zero, so even if the pixel
110 position is negative (as can be the case with motion events in particular),
111 the frame location is always positive.
114 return pixel_to_frame (*pcx);
118 Editor::mouse_mode_toggled (MouseMode m)
120 if (ignore_mouse_mode_toggle) {
126 if (mouse_select_button.get_active()) {
132 if (mouse_move_button.get_active()) {
138 if (mouse_gain_button.get_active()) {
144 if (mouse_zoom_button.get_active()) {
150 if (mouse_timefx_button.get_active()) {
156 if (mouse_audition_button.get_active()) {
167 Editor::set_mouse_mode (MouseMode m, bool force)
169 if (drag_info.item) {
173 if (!force && m == mouse_mode) {
181 if (mouse_mode != MouseRange) {
183 /* in all modes except range, hide the range selection,
184 show the object (region) selection.
187 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
188 (*i)->set_should_show_selection (true);
190 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
191 (*i)->hide_selection ();
197 in range mode,show the range selection.
200 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
201 if ((*i)->get_selected()) {
202 (*i)->show_selection (selection->time);
207 /* XXX the hack of unsetting all other buttongs should go
208 away once GTK2 allows us to use regular radio buttons drawn like
209 normal buttons, rather than my silly GroupedButton hack.
212 ignore_mouse_mode_toggle = true;
214 switch (mouse_mode) {
216 mouse_select_button.set_active (true);
217 current_canvas_cursor = selector_cursor;
221 mouse_move_button.set_active (true);
222 current_canvas_cursor = grabber_cursor;
226 mouse_gain_button.set_active (true);
227 current_canvas_cursor = cross_hair_cursor;
231 mouse_zoom_button.set_active (true);
232 current_canvas_cursor = zoom_cursor;
236 mouse_timefx_button.set_active (true);
237 current_canvas_cursor = time_fx_cursor; // just use playhead
241 mouse_audition_button.set_active (true);
242 current_canvas_cursor = speaker_cursor;
246 ignore_mouse_mode_toggle = false;
249 track_canvas.get_window()->set_cursor(*current_canvas_cursor);
254 Editor::step_mouse_mode (bool next)
256 switch (current_mouse_mode()) {
258 if (next) set_mouse_mode (MouseRange);
259 else set_mouse_mode (MouseTimeFX);
263 if (next) set_mouse_mode (MouseZoom);
264 else set_mouse_mode (MouseObject);
268 if (next) set_mouse_mode (MouseGain);
269 else set_mouse_mode (MouseRange);
273 if (next) set_mouse_mode (MouseTimeFX);
274 else set_mouse_mode (MouseZoom);
278 if (next) set_mouse_mode (MouseAudition);
279 else set_mouse_mode (MouseGain);
283 if (next) set_mouse_mode (MouseObject);
284 else set_mouse_mode (MouseTimeFX);
290 Editor::button_selection (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
294 /* in object/audition/timefx mode, any button press sets
295 the selection if the object can be selected. this is a
296 bit of hack, because we want to avoid this if the
297 mouse operation is a region alignment.
299 note: not dbl-click or triple-click
302 if (((mouse_mode != MouseObject) &&
303 (mouse_mode != MouseAudition || item_type != RegionItem) &&
304 (mouse_mode != MouseTimeFX || item_type != RegionItem) &&
305 (mouse_mode != MouseRange)) ||
307 (event->type != GDK_BUTTON_PRESS && event->type != GDK_BUTTON_RELEASE || event->button.button > 3)) {
312 if (event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) {
314 if ((event->button.state & Keyboard::RelevantModifierKeyMask) && event->button.button != 1) {
316 /* almost no selection action on modified button-2 or button-3 events */
318 if (item_type != RegionItem && event->button.button != 2) {
324 Selection::Operation op = Keyboard::selection_type (event->button.state);
325 bool press = (event->type == GDK_BUTTON_PRESS);
327 // begin_reversible_command (_("select on click"));
331 if (mouse_mode != MouseRange) {
332 commit = set_selected_regionview_from_click (press, op, true);
333 } else if (event->type == GDK_BUTTON_PRESS) {
334 commit = set_selected_track_from_click (press, op, false);
338 case RegionViewNameHighlight:
340 if (mouse_mode != MouseRange) {
341 commit = set_selected_regionview_from_click (press, op, true);
342 } else if (event->type == GDK_BUTTON_PRESS) {
343 commit = set_selected_track_from_click (press, op, false);
347 case FadeInHandleItem:
349 case FadeOutHandleItem:
351 if (mouse_mode != MouseRange) {
352 commit = set_selected_regionview_from_click (press, op, true);
353 } else if (event->type == GDK_BUTTON_PRESS) {
354 commit = set_selected_track_from_click (press, op, false);
358 case GainAutomationControlPointItem:
359 case PanAutomationControlPointItem:
360 case RedirectAutomationControlPointItem:
361 if (mouse_mode != MouseRange) {
362 commit = set_selected_control_point_from_click (op, false);
367 /* for context click or range selection, select track */
368 if (event->button.button == 3) {
369 commit = set_selected_track_from_click (press, op, true);
370 } else if (event->type == GDK_BUTTON_PRESS && mouse_mode == MouseRange) {
371 commit = set_selected_track_from_click (press, op, false);
375 case AutomationTrackItem:
376 commit = set_selected_track_from_click (press, op, true);
384 // commit_reversible_command ();
389 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
391 track_canvas.grab_focus();
393 if (session && session->actively_recording()) {
397 button_selection (item, event, item_type);
399 if (drag_info.item == 0 &&
400 (Keyboard::is_delete_event (&event->button) ||
401 Keyboard::is_context_menu_event (&event->button) ||
402 Keyboard::is_edit_event (&event->button))) {
404 /* handled by button release */
408 switch (event->button.button) {
411 if (event->type == GDK_BUTTON_PRESS) {
413 if (drag_info.item) {
414 drag_info.item->ungrab (event->button.time);
417 /* single mouse clicks on any of these item types operate
418 independent of mouse mode, mostly because they are
419 not on the main track canvas or because we want
425 case PlayheadCursorItem:
426 start_cursor_grab (item, event);
430 if (Keyboard::modifier_state_equals (event->button.state,
431 Keyboard::ModifierMask(Keyboard::Control|Keyboard::Shift))) {
432 hide_marker (item, event);
434 start_marker_grab (item, event);
438 case TempoMarkerItem:
439 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
440 start_tempo_marker_copy_grab (item, event);
442 start_tempo_marker_grab (item, event);
446 case MeterMarkerItem:
447 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
448 start_meter_marker_copy_grab (item, event);
450 start_meter_marker_grab (item, event);
460 case RangeMarkerBarItem:
461 start_range_markerbar_op (item, event, CreateRangeMarker);
465 case TransportMarkerBarItem:
466 start_range_markerbar_op (item, event, CreateTransportMarker);
475 switch (mouse_mode) {
478 case StartSelectionTrimItem:
479 start_selection_op (item, event, SelectionStartTrim);
482 case EndSelectionTrimItem:
483 start_selection_op (item, event, SelectionEndTrim);
487 if (Keyboard::modifier_state_contains
488 (event->button.state, Keyboard::ModifierMask(Keyboard::Alt))) {
489 // contains and not equals because I can't use alt as a modifier alone.
490 start_selection_grab (item, event);
491 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
492 /* grab selection for moving */
493 start_selection_op (item, event, SelectionMove);
496 /* this was debated, but decided the more common action was to
497 make a new selection */
498 start_selection_op (item, event, CreateSelection);
503 start_selection_op (item, event, CreateSelection);
509 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::Control|Keyboard::Alt)) &&
510 event->type == GDK_BUTTON_PRESS) {
512 start_rubberband_select (item, event);
514 } else if (event->type == GDK_BUTTON_PRESS) {
517 case FadeInHandleItem:
518 start_fade_in_grab (item, event);
521 case FadeOutHandleItem:
522 start_fade_out_grab (item, event);
526 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
527 start_region_copy_grab (item, event);
528 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
529 start_region_brush_grab (item, event);
531 start_region_grab (item, event);
535 case RegionViewNameHighlight:
536 start_trim (item, event);
541 /* rename happens on edit clicks */
542 start_trim (clicked_regionview->get_name_highlight(), event);
546 case GainAutomationControlPointItem:
547 case PanAutomationControlPointItem:
548 case RedirectAutomationControlPointItem:
549 start_control_point_grab (item, event);
553 case GainAutomationLineItem:
554 case PanAutomationLineItem:
555 case RedirectAutomationLineItem:
556 start_line_grab_from_line (item, event);
561 case AutomationTrackItem:
562 start_rubberband_select (item, event);
565 /* <CMT Additions> */
566 case ImageFrameHandleStartItem:
567 imageframe_start_handle_op(item, event) ;
570 case ImageFrameHandleEndItem:
571 imageframe_end_handle_op(item, event) ;
574 case MarkerViewHandleStartItem:
575 markerview_item_start_handle_op(item, event) ;
578 case MarkerViewHandleEndItem:
579 markerview_item_end_handle_op(item, event) ;
582 /* </CMT Additions> */
584 /* <CMT Additions> */
586 start_markerview_grab(item, event) ;
589 start_imageframe_grab(item, event) ;
591 /* </CMT Additions> */
607 // start_line_grab_from_regionview (item, event);
610 case GainControlPointItem:
611 start_control_point_grab (item, event);
615 start_line_grab_from_line (item, event);
618 case GainAutomationControlPointItem:
619 case PanAutomationControlPointItem:
620 case RedirectAutomationControlPointItem:
621 start_control_point_grab (item, event);
632 case GainAutomationControlPointItem:
633 case PanAutomationControlPointItem:
634 case RedirectAutomationControlPointItem:
635 start_control_point_grab (item, event);
638 case GainAutomationLineItem:
639 case PanAutomationLineItem:
640 case RedirectAutomationLineItem:
641 start_line_grab_from_line (item, event);
645 // XXX need automation mode to identify which
647 // start_line_grab_from_regionview (item, event);
657 if (event->type == GDK_BUTTON_PRESS) {
658 start_mouse_zoom (item, event);
665 if (item_type == RegionItem) {
666 start_time_fx (item, event);
671 /* handled in release */
680 switch (mouse_mode) {
682 if (event->type == GDK_BUTTON_PRESS) {
685 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
686 start_region_copy_grab (item, event);
688 start_region_grab (item, event);
692 case GainAutomationControlPointItem:
693 case PanAutomationControlPointItem:
694 case RedirectAutomationControlPointItem:
695 start_control_point_grab (item, event);
706 case RegionViewNameHighlight:
707 start_trim (item, event);
712 start_trim (clicked_regionview->get_name_highlight(), event);
723 if (event->type == GDK_BUTTON_PRESS) {
724 /* relax till release */
731 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
732 temporal_zoom_session();
734 temporal_zoom_to_frame (true, event_frame(event));
757 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
759 nframes_t where = event_frame (event, 0, 0);
761 /* no action if we're recording */
763 if (session && session->actively_recording()) {
767 /* first, see if we're finishing a drag ... */
769 if (drag_info.item) {
770 if (end_grab (item, event)) {
771 /* grab dragged, so do nothing else */
776 button_selection (item, event, item_type);
778 /* edit events get handled here */
780 if (drag_info.item == 0 && Keyboard::is_edit_event (&event->button)) {
786 case TempoMarkerItem:
787 edit_tempo_marker (item);
790 case MeterMarkerItem:
791 edit_meter_marker (item);
795 if (clicked_regionview->name_active()) {
796 return mouse_rename_region (item, event);
806 /* context menu events get handled here */
808 if (Keyboard::is_context_menu_event (&event->button)) {
810 if (drag_info.item == 0) {
812 /* no matter which button pops up the context menu, tell the menu
813 widget to use button 1 to drive menu selection.
818 case FadeInHandleItem:
820 case FadeOutHandleItem:
821 popup_fade_context_menu (1, event->button.time, item, item_type);
825 popup_track_context_menu (1, event->button.time, item_type, false, where);
829 case RegionViewNameHighlight:
831 popup_track_context_menu (1, event->button.time, item_type, false, where);
835 popup_track_context_menu (1, event->button.time, item_type, true, where);
838 case AutomationTrackItem:
839 popup_track_context_menu (1, event->button.time, item_type, false, where);
843 case RangeMarkerBarItem:
844 case TransportMarkerBarItem:
847 popup_ruler_menu (pixel_to_frame(event->button.x), item_type);
851 marker_context_menu (&event->button, item);
854 case TempoMarkerItem:
855 tm_marker_context_menu (&event->button, item);
858 case MeterMarkerItem:
859 tm_marker_context_menu (&event->button, item);
862 case CrossfadeViewItem:
863 popup_track_context_menu (1, event->button.time, item_type, false, where);
866 /* <CMT Additions> */
868 popup_imageframe_edit_menu(1, event->button.time, item, true) ;
870 case ImageFrameTimeAxisItem:
871 popup_imageframe_edit_menu(1, event->button.time, item, false) ;
874 popup_marker_time_axis_edit_menu(1, event->button.time, item, true) ;
876 case MarkerTimeAxisItem:
877 popup_marker_time_axis_edit_menu(1, event->button.time, item, false) ;
879 /* <CMT Additions> */
890 /* delete events get handled here */
892 if (drag_info.item == 0 && Keyboard::is_delete_event (&event->button)) {
895 case TempoMarkerItem:
896 remove_tempo_marker (item);
899 case MeterMarkerItem:
900 remove_meter_marker (item);
904 remove_marker (*item, event);
908 if (mouse_mode == MouseObject) {
909 remove_clicked_region ();
913 case GainControlPointItem:
914 if (mouse_mode == MouseGain) {
915 remove_gain_control_point (item, event);
919 case GainAutomationControlPointItem:
920 case PanAutomationControlPointItem:
921 case RedirectAutomationControlPointItem:
922 remove_control_point (item, event);
931 switch (event->button.button) {
935 /* see comments in button_press_handler */
937 case PlayheadCursorItem:
940 case GainAutomationLineItem:
941 case PanAutomationLineItem:
942 case RedirectAutomationLineItem:
943 case StartSelectionTrimItem:
944 case EndSelectionTrimItem:
948 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
949 snap_to (where, 0, true);
951 mouse_add_new_marker (where);
955 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
958 mouse_add_new_tempo_event (where);
962 mouse_add_new_meter_event (pixel_to_frame (event->button.x));
970 switch (mouse_mode) {
973 case AutomationTrackItem:
974 dynamic_cast<AutomationTimeAxisView*>(clicked_trackview)->add_automation_event
988 // Gain only makes sense for audio regions
989 if ( ! dynamic_cast<AudioRegionView*>(clicked_regionview))
994 dynamic_cast<AudioRegionView*>(clicked_regionview)->add_gain_point_event (item, event);
998 case AutomationTrackItem:
999 dynamic_cast<AutomationTimeAxisView*>(clicked_trackview)->
1000 add_automation_event (item, event, where, event->button.y);
1009 switch (item_type) {
1011 audition_selected_region ();
1028 switch (mouse_mode) {
1031 switch (item_type) {
1033 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
1035 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::Shift|Keyboard::Alt))) {
1038 // Button2 click is unused
1051 // x_style_paste (where, 1.0);
1071 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1077 switch (item_type) {
1078 case GainControlPointItem:
1079 if (mouse_mode == MouseGain) {
1080 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1081 cp->set_visible (true);
1085 at_y = cp->get_y ();
1086 cp->item->i2w (at_x, at_y);
1090 fraction = 1.0 - (cp->get_y() / cp->line.height());
1092 set_verbose_canvas_cursor (cp->line.get_verbose_cursor_string (fraction), at_x, at_y);
1093 show_verbose_canvas_cursor ();
1095 if (is_drawable()) {
1096 track_canvas.get_window()->set_cursor (*fader_cursor);
1101 case GainAutomationControlPointItem:
1102 case PanAutomationControlPointItem:
1103 case RedirectAutomationControlPointItem:
1104 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1105 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1106 cp->set_visible (true);
1110 at_y = cp->get_y ();
1111 cp->item->i2w (at_x, at_y);
1115 fraction = 1.0 - (cp->get_y() / cp->line.height());
1117 set_verbose_canvas_cursor (cp->line.get_verbose_cursor_string (fraction), at_x, at_y);
1118 show_verbose_canvas_cursor ();
1120 if (is_drawable()) {
1121 track_canvas.get_window()->set_cursor (*fader_cursor);
1127 if (mouse_mode == MouseGain) {
1128 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1130 line->property_fill_color_rgba() = color_map[cEnteredGainLine];
1131 if (is_drawable()) {
1132 track_canvas.get_window()->set_cursor (*fader_cursor);
1137 case GainAutomationLineItem:
1138 case RedirectAutomationLineItem:
1139 case PanAutomationLineItem:
1140 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1142 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1144 line->property_fill_color_rgba() = color_map[cEnteredAutomationLine];
1146 if (is_drawable()) {
1147 track_canvas.get_window()->set_cursor (*fader_cursor);
1152 case RegionViewNameHighlight:
1153 if (is_drawable() && mouse_mode == MouseObject) {
1154 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1158 case StartSelectionTrimItem:
1159 case EndSelectionTrimItem:
1160 /* <CMT Additions> */
1161 case ImageFrameHandleStartItem:
1162 case ImageFrameHandleEndItem:
1163 case MarkerViewHandleStartItem:
1164 case MarkerViewHandleEndItem:
1165 /* </CMT Additions> */
1167 if (is_drawable()) {
1168 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1172 case EditCursorItem:
1173 case PlayheadCursorItem:
1174 if (is_drawable()) {
1175 track_canvas.get_window()->set_cursor (*grabber_cursor);
1179 case RegionViewName:
1181 /* when the name is not an active item, the entire name highlight is for trimming */
1183 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1184 if (mouse_mode == MouseObject && is_drawable()) {
1185 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1191 case AutomationTrackItem:
1192 if (is_drawable()) {
1193 Gdk::Cursor *cursor;
1194 switch (mouse_mode) {
1196 cursor = selector_cursor;
1199 cursor = zoom_cursor;
1202 cursor = cross_hair_cursor;
1206 track_canvas.get_window()->set_cursor (*cursor);
1208 AutomationTimeAxisView* atv;
1209 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1210 clear_entered_track = false;
1211 set_entered_track (atv);
1217 case RangeMarkerBarItem:
1218 case TransportMarkerBarItem:
1221 if (is_drawable()) {
1222 time_canvas.get_window()->set_cursor (*timebar_cursor);
1227 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1230 marker->set_color_rgba (color_map[cEnteredMarker]);
1232 case MeterMarkerItem:
1233 case TempoMarkerItem:
1234 if (is_drawable()) {
1235 time_canvas.get_window()->set_cursor (*timebar_cursor);
1238 case FadeInHandleItem:
1239 case FadeOutHandleItem:
1240 if (mouse_mode == MouseObject) {
1241 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1243 rect->property_fill_color_rgba() = 0;
1244 rect->property_outline_pixels() = 1;
1253 /* second pass to handle entered track status in a comprehensible way.
1256 switch (item_type) {
1258 case GainAutomationLineItem:
1259 case RedirectAutomationLineItem:
1260 case PanAutomationLineItem:
1261 case GainControlPointItem:
1262 case GainAutomationControlPointItem:
1263 case PanAutomationControlPointItem:
1264 case RedirectAutomationControlPointItem:
1265 /* these do not affect the current entered track state */
1266 clear_entered_track = false;
1269 case AutomationTrackItem:
1270 /* handled above already */
1274 set_entered_track (0);
1282 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1291 switch (item_type) {
1292 case GainControlPointItem:
1293 case GainAutomationControlPointItem:
1294 case PanAutomationControlPointItem:
1295 case RedirectAutomationControlPointItem:
1296 cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
1297 if (cp->line.npoints() > 1) {
1298 if (!cp->selected) {
1299 cp->set_visible (false);
1303 if (is_drawable()) {
1304 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1307 hide_verbose_canvas_cursor ();
1310 case RegionViewNameHighlight:
1311 case StartSelectionTrimItem:
1312 case EndSelectionTrimItem:
1313 case EditCursorItem:
1314 case PlayheadCursorItem:
1315 /* <CMT Additions> */
1316 case ImageFrameHandleStartItem:
1317 case ImageFrameHandleEndItem:
1318 case MarkerViewHandleStartItem:
1319 case MarkerViewHandleEndItem:
1320 /* </CMT Additions> */
1321 if (is_drawable()) {
1322 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1327 case GainAutomationLineItem:
1328 case RedirectAutomationLineItem:
1329 case PanAutomationLineItem:
1330 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1332 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1334 line->property_fill_color_rgba() = al->get_line_color();
1336 if (is_drawable()) {
1337 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1341 case RegionViewName:
1342 /* see enter_handler() for notes */
1343 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1344 if (is_drawable() && mouse_mode == MouseObject) {
1345 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1350 case RangeMarkerBarItem:
1351 case TransportMarkerBarItem:
1355 if (is_drawable()) {
1356 time_canvas.get_window()->set_cursor (*timebar_cursor);
1361 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1364 loc = find_location_from_marker (marker, is_start);
1365 if (loc) location_flags_changed (loc, this);
1367 case MeterMarkerItem:
1368 case TempoMarkerItem:
1370 if (is_drawable()) {
1371 time_canvas.get_window()->set_cursor (*timebar_cursor);
1376 case FadeInHandleItem:
1377 case FadeOutHandleItem:
1378 rv = static_cast<RegionView*>(item->get_data ("regionview"));
1380 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1382 rect->property_fill_color_rgba() = rv->get_fill_color();
1383 rect->property_outline_pixels() = 0;
1388 case AutomationTrackItem:
1389 if (is_drawable()) {
1390 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1391 clear_entered_track = true;
1392 Glib::signal_idle().connect (mem_fun(*this, &Editor::left_automation_track));
1404 Editor::left_automation_track ()
1406 if (clear_entered_track) {
1407 set_entered_track (0);
1408 clear_entered_track = false;
1414 Editor::motion_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type, bool from_autoscroll)
1418 /* We call this so that MOTION_NOTIFY events continue to be
1419 delivered to the canvas. We need to do this because we set
1420 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
1421 the density of the events, at the expense of a round-trip
1422 to the server. Given that this will mostly occur on cases
1423 where DISPLAY = :0.0, and given the cost of what the motion
1424 event might do, its a good tradeoff.
1427 track_canvas.get_pointer (x, y);
1429 if (current_stepping_trackview) {
1430 /* don't keep the persistent stepped trackview if the mouse moves */
1431 current_stepping_trackview = 0;
1432 step_timeout.disconnect ();
1435 if (session && session->actively_recording()) {
1436 /* Sorry. no dragging stuff around while we record */
1440 drag_info.item_type = item_type;
1441 drag_info.current_pointer_frame = event_frame (event, &drag_info.current_pointer_x,
1442 &drag_info.current_pointer_y);
1444 if (!from_autoscroll && drag_info.item) {
1445 /* item != 0 is the best test i can think of for dragging.
1447 if (!drag_info.move_threshold_passed) {
1449 bool x_threshold_passed = (abs ((int) (drag_info.current_pointer_x - drag_info.grab_x)) > 4);
1450 bool y_threshold_passed = (abs ((int) (drag_info.current_pointer_y - drag_info.grab_y)) > 4);
1452 drag_info.move_threshold_passed = (x_threshold_passed || y_threshold_passed);
1454 // and change the initial grab loc/frame if this drag info wants us to
1456 if (drag_info.want_move_threshold && drag_info.move_threshold_passed) {
1457 drag_info.grab_frame = drag_info.current_pointer_frame;
1458 drag_info.grab_x = drag_info.current_pointer_x;
1459 drag_info.grab_y = drag_info.current_pointer_y;
1460 drag_info.last_pointer_frame = drag_info.grab_frame;
1461 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
1466 switch (item_type) {
1467 case PlayheadCursorItem:
1468 case EditCursorItem:
1470 case GainControlPointItem:
1471 case RedirectAutomationControlPointItem:
1472 case GainAutomationControlPointItem:
1473 case PanAutomationControlPointItem:
1474 case TempoMarkerItem:
1475 case MeterMarkerItem:
1476 case RegionViewNameHighlight:
1477 case StartSelectionTrimItem:
1478 case EndSelectionTrimItem:
1481 case RedirectAutomationLineItem:
1482 case GainAutomationLineItem:
1483 case PanAutomationLineItem:
1484 case FadeInHandleItem:
1485 case FadeOutHandleItem:
1486 /* <CMT Additions> */
1487 case ImageFrameHandleStartItem:
1488 case ImageFrameHandleEndItem:
1489 case MarkerViewHandleStartItem:
1490 case MarkerViewHandleEndItem:
1491 /* </CMT Additions> */
1492 if (drag_info.item && (event->motion.state & Gdk::BUTTON1_MASK ||
1493 (event->motion.state & Gdk::BUTTON2_MASK))) {
1494 if (!from_autoscroll) {
1495 maybe_autoscroll (event);
1497 (this->*(drag_info.motion_callback)) (item, event);
1506 switch (mouse_mode) {
1511 if (drag_info.item && (event->motion.state & GDK_BUTTON1_MASK ||
1512 (event->motion.state & GDK_BUTTON2_MASK))) {
1513 if (!from_autoscroll) {
1514 maybe_autoscroll (event);
1516 (this->*(drag_info.motion_callback)) (item, event);
1527 track_canvas_motion (event);
1528 // drag_info.last_pointer_frame = drag_info.current_pointer_frame;
1536 Editor::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
1538 if (drag_info.item == 0) {
1539 fatal << _("programming error: start_grab called without drag item") << endmsg;
1545 cursor = grabber_cursor;
1548 // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
1550 if (event->button.button == 2) {
1551 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Alt)) {
1552 drag_info.y_constrained = true;
1553 drag_info.x_constrained = false;
1555 drag_info.y_constrained = false;
1556 drag_info.x_constrained = true;
1559 drag_info.x_constrained = false;
1560 drag_info.y_constrained = false;
1563 drag_info.grab_frame = event_frame(event, &drag_info.grab_x, &drag_info.grab_y);
1564 drag_info.last_pointer_frame = drag_info.grab_frame;
1565 drag_info.current_pointer_frame = drag_info.grab_frame;
1566 drag_info.current_pointer_x = drag_info.grab_x;
1567 drag_info.current_pointer_y = drag_info.grab_y;
1568 drag_info.cumulative_x_drag = 0;
1569 drag_info.cumulative_y_drag = 0;
1570 drag_info.first_move = true;
1571 drag_info.move_threshold_passed = false;
1572 drag_info.want_move_threshold = false;
1573 drag_info.pointer_frame_offset = 0;
1574 drag_info.brushing = false;
1575 drag_info.copied_location = 0;
1577 drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK,
1579 event->button.time);
1581 if (session && session->transport_rolling()) {
1582 drag_info.was_rolling = true;
1584 drag_info.was_rolling = false;
1587 switch (snap_type) {
1588 case SnapToRegionStart:
1589 case SnapToRegionEnd:
1590 case SnapToRegionSync:
1591 case SnapToRegionBoundary:
1592 build_region_boundary_cache ();
1600 Editor::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t time)
1602 drag_info.item->ungrab (0);
1603 drag_info.item = new_item;
1606 cursor = grabber_cursor;
1609 drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK, *cursor, time);
1613 Editor::end_grab (ArdourCanvas::Item* item, GdkEvent* event)
1615 bool did_drag = false;
1617 stop_canvas_autoscroll ();
1619 if (drag_info.item == 0) {
1623 drag_info.item->ungrab (event->button.time);
1625 if (drag_info.finished_callback) {
1626 (this->*(drag_info.finished_callback)) (item, event);
1629 did_drag = !drag_info.first_move;
1631 hide_verbose_canvas_cursor();
1634 drag_info.copy = false;
1635 drag_info.motion_callback = 0;
1636 drag_info.finished_callback = 0;
1637 drag_info.last_trackview = 0;
1638 drag_info.last_frame_position = 0;
1639 drag_info.grab_frame = 0;
1640 drag_info.last_pointer_frame = 0;
1641 drag_info.current_pointer_frame = 0;
1642 drag_info.brushing = false;
1644 if (drag_info.copied_location) {
1645 delete drag_info.copied_location;
1646 drag_info.copied_location = 0;
1653 Editor::set_edit_cursor (GdkEvent* event)
1655 nframes_t pointer_frame = event_frame (event);
1657 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1658 if (snap_type != SnapToEditCursor) {
1659 snap_to (pointer_frame);
1663 edit_cursor->set_position (pointer_frame);
1664 edit_cursor_clock.set (pointer_frame);
1668 Editor::set_playhead_cursor (GdkEvent* event)
1670 nframes_t pointer_frame = event_frame (event);
1672 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1673 snap_to (pointer_frame);
1677 session->request_locate (pointer_frame, session->transport_rolling());
1682 Editor::start_fade_in_grab (ArdourCanvas::Item* item, GdkEvent* event)
1684 drag_info.item = item;
1685 drag_info.motion_callback = &Editor::fade_in_drag_motion_callback;
1686 drag_info.finished_callback = &Editor::fade_in_drag_finished_callback;
1690 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
1691 fatal << _("programming error: fade in canvas item has no regionview data pointer!") << endmsg;
1695 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1697 drag_info.pointer_frame_offset = drag_info.grab_frame - ((nframes_t) arv->audio_region()->fade_in().back()->when + arv->region()->position());
1701 Editor::fade_in_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1703 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1705 nframes_t fade_length;
1707 if ((int32_t)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1708 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1714 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1718 if (pos < (arv->region()->position() + 64)) {
1719 fade_length = 64; // this should be a minimum defined somewhere
1720 } else if (pos > arv->region()->last_frame()) {
1721 fade_length = arv->region()->length();
1723 fade_length = pos - arv->region()->position();
1725 /* mapover the region selection */
1727 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1729 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1735 tmp->reset_fade_in_shape_width (fade_length);
1738 show_verbose_duration_cursor (arv->region()->position(), arv->region()->position() + fade_length, 10);
1740 drag_info.first_move = false;
1744 Editor::fade_in_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1746 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1748 nframes_t fade_length;
1750 if (drag_info.first_move) return;
1752 if ((int32_t)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1753 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1758 if (pos < (arv->region()->position() + 64)) {
1759 fade_length = 64; // this should be a minimum defined somewhere
1760 } else if (pos > arv->region()->last_frame()) {
1761 fade_length = arv->region()->length();
1763 fade_length = pos - arv->region()->position();
1766 begin_reversible_command (_("change fade in length"));
1768 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1770 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1776 AutomationList& alist = tmp->audio_region()->fade_in();
1777 XMLNode &before = alist.get_state();
1779 tmp->audio_region()->set_fade_in_length (fade_length);
1781 XMLNode &after = alist.get_state();
1782 session->add_command(new MementoCommand<AutomationList>(alist, &before, &after));
1785 commit_reversible_command ();
1789 Editor::start_fade_out_grab (ArdourCanvas::Item* item, GdkEvent* event)
1791 drag_info.item = item;
1792 drag_info.motion_callback = &Editor::fade_out_drag_motion_callback;
1793 drag_info.finished_callback = &Editor::fade_out_drag_finished_callback;
1797 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
1798 fatal << _("programming error: fade out canvas item has no regionview data pointer!") << endmsg;
1802 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1804 drag_info.pointer_frame_offset = drag_info.grab_frame - (arv->region()->length() - (nframes_t) arv->audio_region()->fade_out().back()->when + arv->region()->position());
1808 Editor::fade_out_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1810 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1812 nframes_t fade_length;
1814 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1815 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1821 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1825 if (pos > (arv->region()->last_frame() - 64)) {
1826 fade_length = 64; // this should really be a minimum fade defined somewhere
1828 else if (pos < arv->region()->position()) {
1829 fade_length = arv->region()->length();
1832 fade_length = arv->region()->last_frame() - pos;
1835 /* mapover the region selection */
1837 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1839 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1845 tmp->reset_fade_out_shape_width (fade_length);
1848 show_verbose_duration_cursor (arv->region()->last_frame() - fade_length, arv->region()->last_frame(), 10);
1850 drag_info.first_move = false;
1854 Editor::fade_out_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1856 if (drag_info.first_move) return;
1858 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1860 nframes_t fade_length;
1862 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1863 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1869 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1873 if (pos > (arv->region()->last_frame() - 64)) {
1874 fade_length = 64; // this should really be a minimum fade defined somewhere
1876 else if (pos < arv->region()->position()) {
1877 fade_length = arv->region()->length();
1880 fade_length = arv->region()->last_frame() - pos;
1883 begin_reversible_command (_("change fade out length"));
1885 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1887 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1893 AutomationList& alist = tmp->audio_region()->fade_out();
1894 XMLNode &before = alist.get_state();
1896 tmp->audio_region()->set_fade_out_length (fade_length);
1898 XMLNode &after = alist.get_state();
1899 session->add_command(new MementoCommand<AutomationList>(alist, &before, &after));
1902 commit_reversible_command ();
1906 Editor::start_cursor_grab (ArdourCanvas::Item* item, GdkEvent* event)
1908 drag_info.item = item;
1909 drag_info.motion_callback = &Editor::cursor_drag_motion_callback;
1910 drag_info.finished_callback = &Editor::cursor_drag_finished_callback;
1914 if ((drag_info.data = (item->get_data ("cursor"))) == 0) {
1915 fatal << _("programming error: cursor canvas item has no cursor data pointer!") << endmsg;
1919 Cursor* cursor = (Cursor *) drag_info.data;
1921 if (cursor == playhead_cursor) {
1922 _dragging_playhead = true;
1924 if (session && drag_info.was_rolling) {
1925 session->request_stop ();
1928 if (session && session->is_auditioning()) {
1929 session->cancel_audition ();
1933 drag_info.pointer_frame_offset = drag_info.grab_frame - cursor->current_frame;
1935 show_verbose_time_cursor (cursor->current_frame, 10);
1939 Editor::cursor_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1941 Cursor* cursor = (Cursor *) drag_info.data;
1942 nframes_t adjusted_frame;
1944 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1945 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1951 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1952 if (cursor != edit_cursor || snap_type != SnapToEditCursor) {
1953 snap_to (adjusted_frame);
1957 if (adjusted_frame == drag_info.last_pointer_frame) return;
1959 cursor->set_position (adjusted_frame);
1961 if (cursor == edit_cursor) {
1962 edit_cursor_clock.set (cursor->current_frame);
1964 UpdateAllTransportClocks (cursor->current_frame);
1967 show_verbose_time_cursor (cursor->current_frame, 10);
1969 drag_info.last_pointer_frame = adjusted_frame;
1970 drag_info.first_move = false;
1974 Editor::cursor_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1976 if (drag_info.first_move) return;
1978 cursor_drag_motion_callback (item, event);
1980 _dragging_playhead = false;
1982 if (item == &playhead_cursor->canvas_item) {
1984 session->request_locate (playhead_cursor->current_frame, drag_info.was_rolling);
1986 } else if (item == &edit_cursor->canvas_item) {
1987 edit_cursor->set_position (edit_cursor->current_frame);
1988 edit_cursor_clock.set (edit_cursor->current_frame);
1993 Editor::update_marker_drag_item (Location *location)
1995 double x1 = frame_to_pixel (location->start());
1996 double x2 = frame_to_pixel (location->end());
1998 if (location->is_mark()) {
1999 marker_drag_line_points.front().set_x(x1);
2000 marker_drag_line_points.back().set_x(x1);
2001 marker_drag_line->property_points() = marker_drag_line_points;
2004 range_marker_drag_rect->property_x1() = x1;
2005 range_marker_drag_rect->property_x2() = x2;
2010 Editor::start_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2014 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2015 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2021 Location *location = find_location_from_marker (marker, is_start);
2023 drag_info.item = item;
2024 drag_info.data = marker;
2025 drag_info.motion_callback = &Editor::marker_drag_motion_callback;
2026 drag_info.finished_callback = &Editor::marker_drag_finished_callback;
2030 drag_info.copied_location = new Location (*location);
2031 drag_info.pointer_frame_offset = drag_info.grab_frame - (is_start ? location->start() : location->end());
2033 update_marker_drag_item (location);
2035 if (location->is_mark()) {
2036 marker_drag_line->show();
2037 marker_drag_line->raise_to_top();
2040 range_marker_drag_rect->show();
2041 range_marker_drag_rect->raise_to_top();
2044 if (is_start) show_verbose_time_cursor (location->start(), 10);
2045 else show_verbose_time_cursor (location->end(), 10);
2049 Editor::marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2052 Marker* marker = (Marker *) drag_info.data;
2053 Location *real_location;
2054 Location *copy_location;
2056 bool move_both = false;
2060 if (drag_info.pointer_frame_offset <= (long) drag_info.current_pointer_frame) {
2061 newframe = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2067 nframes_t next = newframe;
2069 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2070 snap_to (newframe, 0, true);
2073 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
2077 /* call this to find out if its the start or end */
2079 real_location = find_location_from_marker (marker, is_start);
2081 /* use the copy that we're "dragging" around */
2083 copy_location = drag_info.copied_location;
2085 f_delta = copy_location->end() - copy_location->start();
2087 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
2091 if (copy_location->is_mark()) {
2094 copy_location->set_start (newframe);
2098 if (is_start) { // start-of-range marker
2101 copy_location->set_start (newframe);
2102 copy_location->set_end (newframe + f_delta);
2103 } else if (newframe < copy_location->end()) {
2104 copy_location->set_start (newframe);
2106 snap_to (next, 1, true);
2107 copy_location->set_end (next);
2108 copy_location->set_start (newframe);
2111 } else { // end marker
2114 copy_location->set_end (newframe);
2115 copy_location->set_start (newframe - f_delta);
2116 } else if (newframe > copy_location->start()) {
2117 copy_location->set_end (newframe);
2119 } else if (newframe > 0) {
2120 snap_to (next, -1, true);
2121 copy_location->set_start (next);
2122 copy_location->set_end (newframe);
2127 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
2128 drag_info.first_move = false;
2130 update_marker_drag_item (copy_location);
2132 LocationMarkers* lm = find_location_markers (real_location);
2133 lm->set_position (copy_location->start(), copy_location->end());
2135 show_verbose_time_cursor (newframe, 10);
2139 Editor::marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2141 if (drag_info.first_move) {
2142 marker_drag_motion_callback (item, event);
2146 Marker* marker = (Marker *) drag_info.data;
2150 begin_reversible_command ( _("move marker") );
2151 XMLNode &before = session->locations()->get_state();
2153 Location * location = find_location_from_marker (marker, is_start);
2156 if (location->is_mark()) {
2157 location->set_start (drag_info.copied_location->start());
2159 location->set (drag_info.copied_location->start(), drag_info.copied_location->end());
2163 XMLNode &after = session->locations()->get_state();
2164 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
2165 commit_reversible_command ();
2167 marker_drag_line->hide();
2168 range_marker_drag_rect->hide();
2172 Editor::start_meter_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2175 MeterMarker* meter_marker;
2177 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2178 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2182 meter_marker = dynamic_cast<MeterMarker*> (marker);
2184 MetricSection& section (meter_marker->meter());
2186 if (!section.movable()) {
2190 drag_info.item = item;
2191 drag_info.data = marker;
2192 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2193 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2197 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2199 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2203 Editor::start_meter_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2206 MeterMarker* meter_marker;
2208 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2209 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2213 meter_marker = dynamic_cast<MeterMarker*> (marker);
2215 // create a dummy marker for visual representation of moving the copy.
2216 // The actual copying is not done before we reach the finish callback.
2218 snprintf (name, sizeof(name), "%g/%g", meter_marker->meter().beats_per_bar(), meter_marker->meter().note_divisor ());
2219 MeterMarker* new_marker = new MeterMarker(*this, *meter_group, color_map[cMeterMarker], name,
2220 *new MeterSection(meter_marker->meter()));
2222 drag_info.item = &new_marker->the_item();
2223 drag_info.copy = true;
2224 drag_info.data = new_marker;
2225 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2226 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2230 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2232 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2236 Editor::meter_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2238 MeterMarker* marker = (MeterMarker *) drag_info.data;
2239 nframes_t adjusted_frame;
2241 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2242 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2248 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2249 snap_to (adjusted_frame);
2252 if (adjusted_frame == drag_info.last_pointer_frame) return;
2254 marker->set_position (adjusted_frame);
2257 drag_info.last_pointer_frame = adjusted_frame;
2258 drag_info.first_move = false;
2260 show_verbose_time_cursor (adjusted_frame, 10);
2264 Editor::meter_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2266 if (drag_info.first_move) return;
2268 meter_marker_drag_motion_callback (drag_info.item, event);
2270 MeterMarker* marker = (MeterMarker *) drag_info.data;
2273 TempoMap& map (session->tempo_map());
2274 map.bbt_time (drag_info.last_pointer_frame, when);
2276 if (drag_info.copy == true) {
2277 begin_reversible_command (_("copy meter mark"));
2278 XMLNode &before = map.get_state();
2279 map.add_meter (marker->meter(), when);
2280 XMLNode &after = map.get_state();
2281 session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2282 commit_reversible_command ();
2284 // delete the dummy marker we used for visual representation of copying.
2285 // a new visual marker will show up automatically.
2288 begin_reversible_command (_("move meter mark"));
2289 XMLNode &before = map.get_state();
2290 map.move_meter (marker->meter(), when);
2291 XMLNode &after = map.get_state();
2292 session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2293 commit_reversible_command ();
2298 Editor::start_tempo_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2301 TempoMarker* tempo_marker;
2303 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2304 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2308 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2309 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2313 MetricSection& section (tempo_marker->tempo());
2315 if (!section.movable()) {
2319 drag_info.item = item;
2320 drag_info.data = marker;
2321 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2322 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2326 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2327 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2331 Editor::start_tempo_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2334 TempoMarker* tempo_marker;
2336 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2337 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2341 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2342 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2346 // create a dummy marker for visual representation of moving the copy.
2347 // The actual copying is not done before we reach the finish callback.
2349 snprintf (name, sizeof (name), "%.2f", tempo_marker->tempo().beats_per_minute());
2350 TempoMarker* new_marker = new TempoMarker(*this, *tempo_group, color_map[cTempoMarker], name,
2351 *new TempoSection(tempo_marker->tempo()));
2353 drag_info.item = &new_marker->the_item();
2354 drag_info.copy = true;
2355 drag_info.data = new_marker;
2356 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2357 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2361 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2363 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2367 Editor::tempo_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2369 TempoMarker* marker = (TempoMarker *) drag_info.data;
2370 nframes_t adjusted_frame;
2372 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2373 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2379 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2380 snap_to (adjusted_frame);
2383 if (adjusted_frame == drag_info.last_pointer_frame) return;
2385 /* OK, we've moved far enough to make it worth actually move the thing. */
2387 marker->set_position (adjusted_frame);
2389 show_verbose_time_cursor (adjusted_frame, 10);
2391 drag_info.last_pointer_frame = adjusted_frame;
2392 drag_info.first_move = false;
2396 Editor::tempo_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2398 if (drag_info.first_move) return;
2400 tempo_marker_drag_motion_callback (drag_info.item, event);
2402 TempoMarker* marker = (TempoMarker *) drag_info.data;
2405 TempoMap& map (session->tempo_map());
2406 map.bbt_time (drag_info.last_pointer_frame, when);
2408 if (drag_info.copy == true) {
2409 begin_reversible_command (_("copy tempo mark"));
2410 XMLNode &before = map.get_state();
2411 map.add_tempo (marker->tempo(), when);
2412 XMLNode &after = map.get_state();
2413 session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2414 commit_reversible_command ();
2416 // delete the dummy marker we used for visual representation of copying.
2417 // a new visual marker will show up automatically.
2420 begin_reversible_command (_("move tempo mark"));
2421 XMLNode &before = map.get_state();
2422 map.move_tempo (marker->tempo(), when);
2423 XMLNode &after = map.get_state();
2424 session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
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, drag_info.grab_frame, 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 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 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 = (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, drag_info.grab_frame, 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 = (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 = (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;
2713 show_verbose_time_cursor (drag_info.last_frame_position, 10);
2717 Editor::start_region_brush_grab (ArdourCanvas::Item* item, GdkEvent* event)
2719 if (selection->regions.empty() || clicked_regionview == 0) {
2723 drag_info.copy = false;
2724 drag_info.item = item;
2725 drag_info.data = clicked_regionview;
2726 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2727 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2732 TimeAxisView* tvp = clicked_trackview;
2733 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2735 if (tv && tv->is_audio_track()) {
2736 speed = tv->get_diskstream()->speed();
2739 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2740 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2741 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2742 // we want a move threshold
2743 drag_info.want_move_threshold = true;
2744 drag_info.brushing = true;
2746 begin_reversible_command (_("Drag region brush"));
2750 Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2754 RegionView* rv = reinterpret_cast<RegionView*> (drag_info.data);
2755 nframes_t pending_region_position = 0;
2756 int32_t pointer_y_span = 0, canvas_pointer_y_span = 0, original_pointer_order;
2757 int32_t visible_y_high = 0, visible_y_low = 512; //high meaning higher numbered.. not the height on the screen
2758 bool clamp_y_axis = false;
2759 vector<int32_t> height_list(512) ;
2760 vector<int32_t>::iterator j;
2762 if (drag_info.copy && drag_info.move_threshold_passed && drag_info.want_move_threshold) {
2764 drag_info.want_move_threshold = false; // don't copy again
2766 /* duplicate the region(s) */
2768 vector<RegionView*> new_regionviews;
2770 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
2773 AudioRegionView* arv;
2778 if ((arv = dynamic_cast<AudioRegionView*>(rv)) == 0) {
2779 /* XXX handle MIDI here */
2783 nrv = new AudioRegionView (*arv);
2784 nrv->get_canvas_group()->show ();
2786 new_regionviews.push_back (nrv);
2789 if (new_regionviews.empty()) {
2793 /* reset selection to new regionviews */
2795 selection->set (new_regionviews);
2797 /* reset drag_info data to reflect the fact that we are dragging the copies */
2799 drag_info.data = new_regionviews.front();
2801 swap_grab (new_regionviews.front()->get_canvas_group (), 0, event->motion.time);
2804 /* Which trackview is this ? */
2806 TimeAxisView* tvp = trackview_by_y_position (drag_info.current_pointer_y);
2807 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
2809 /* The region motion is only processed if the pointer is over
2813 if (!tv || !tv->is_audio_track()) {
2814 /* To make sure we hide the verbose canvas cursor when the mouse is
2815 not held over and audiotrack.
2817 hide_verbose_canvas_cursor ();
2821 original_pointer_order = drag_info.last_trackview->order;
2823 /************************************************************
2825 ************************************************************/
2827 if (drag_info.brushing) {
2828 clamp_y_axis = true;
2833 if ((pointer_y_span = (drag_info.last_trackview->order - tv->order)) != 0) {
2835 int32_t children = 0, numtracks = 0;
2836 // XXX hard coding track limit, oh my, so very very bad
2837 bitset <1024> tracks (0x00);
2838 /* get a bitmask representing the visible tracks */
2840 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2841 TimeAxisView *tracklist_timeview;
2842 tracklist_timeview = (*i);
2843 AudioTimeAxisView* atv2 = dynamic_cast<AudioTimeAxisView*>(tracklist_timeview);
2844 list<TimeAxisView*> children_list;
2846 /* zeroes are audio tracks. ones are other types. */
2848 if (!atv2->hidden()) {
2850 if (visible_y_high < atv2->order) {
2851 visible_y_high = atv2->order;
2853 if (visible_y_low > atv2->order) {
2854 visible_y_low = atv2->order;
2857 if (!atv2->is_audio_track()) {
2858 tracks = tracks |= (0x01 << atv2->order);
2861 height_list[atv2->order] = (*i)->height;
2863 if ((children_list = atv2->get_child_list()).size() > 0) {
2864 for (list<TimeAxisView*>::iterator j = children_list.begin(); j != children_list.end(); ++j) {
2865 tracks = tracks |= (0x01 << (atv2->order + children));
2866 height_list[atv2->order + children] = (*j)->height;
2874 /* find the actual span according to the canvas */
2876 canvas_pointer_y_span = pointer_y_span;
2877 if (drag_info.last_trackview->order >= tv->order) {
2879 for (y = tv->order; y < drag_info.last_trackview->order; y++) {
2880 if (height_list[y] == 0 ) {
2881 canvas_pointer_y_span--;
2886 for (y = drag_info.last_trackview->order;y <= tv->order; y++) {
2887 if ( height_list[y] == 0 ) {
2888 canvas_pointer_y_span++;
2893 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
2894 RegionView* rv2 = (*i);
2895 double ix1, ix2, iy1, iy2;
2898 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
2899 rv2->get_canvas_group()->i2w (ix1, iy1);
2900 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
2901 RouteTimeAxisView* atv2 = dynamic_cast<RouteTimeAxisView*>(tvp2);
2903 if (atv2->order != original_pointer_order) {
2904 /* this isn't the pointer track */
2906 if (canvas_pointer_y_span > 0) {
2908 /* moving up the canvas */
2909 if ((atv2->order - canvas_pointer_y_span) >= visible_y_low) {
2911 int32_t visible_tracks = 0;
2912 while (visible_tracks < canvas_pointer_y_span ) {
2915 while (height_list[atv2->order - (visible_tracks - n)] == 0) {
2916 /* we're passing through a hidden track */
2921 if (tracks[atv2->order - (canvas_pointer_y_span - n)] != 0x00) {
2922 clamp_y_axis = true;
2926 clamp_y_axis = true;
2929 } else if (canvas_pointer_y_span < 0) {
2931 /*moving down the canvas*/
2933 if ((atv2->order - (canvas_pointer_y_span - n)) <= visible_y_high) { // we will overflow
2936 int32_t visible_tracks = 0;
2938 while (visible_tracks > canvas_pointer_y_span ) {
2941 while (height_list[atv2->order - (visible_tracks - n)] == 0) {
2945 if ( tracks[atv2->order - ( canvas_pointer_y_span - n)] != 0x00) {
2946 clamp_y_axis = true;
2951 clamp_y_axis = true;
2957 /* this is the pointer's track */
2958 if ((atv2->order - pointer_y_span) > visible_y_high) { // we will overflow
2959 clamp_y_axis = true;
2960 } else if ((atv2->order - pointer_y_span) < visible_y_low) { // we will underflow
2961 clamp_y_axis = true;
2969 } else if (drag_info.last_trackview == tv) {
2970 clamp_y_axis = true;
2974 if (!clamp_y_axis) {
2975 drag_info.last_trackview = tv;
2978 /************************************************************
2980 ************************************************************/
2982 /* compute the amount of pointer motion in frames, and where
2983 the region would be if we moved it by that much.
2986 if (drag_info.move_threshold_passed) {
2988 if ((int32_t)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2990 nframes_t sync_frame;
2991 nframes_t sync_offset;
2994 pending_region_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2996 sync_offset = rv->region()->sync_offset (sync_dir);
2997 sync_frame = rv->region()->adjust_to_sync (pending_region_position);
2999 /* we snap if the snap modifier is not enabled.
3002 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3003 snap_to (sync_frame);
3006 if (sync_frame - sync_offset <= sync_frame) {
3007 pending_region_position = sync_frame - (sync_dir*sync_offset);
3009 pending_region_position = 0;
3013 pending_region_position = 0;
3016 if (pending_region_position > max_frames - rv->region()->length()) {
3017 pending_region_position = drag_info.last_frame_position;
3020 // printf ("3: pending_region_position= %lu %lu\n", pending_region_position, drag_info.last_frame_position );
3022 if (pending_region_position != drag_info.last_frame_position && !drag_info.x_constrained) {
3024 /* now compute the canvas unit distance we need to move the regiondrag_info.last_trackview->order
3025 to make it appear at the new location.
3028 if (pending_region_position > drag_info.last_frame_position) {
3029 x_delta = ((double) (pending_region_position - drag_info.last_frame_position) / frames_per_unit);
3031 x_delta = -((double) (drag_info.last_frame_position - pending_region_position) / frames_per_unit);
3034 drag_info.last_frame_position = pending_region_position;
3041 /* threshold not passed */
3046 /*************************************************************
3048 ************************************************************/
3050 if (x_delta == 0 && (pointer_y_span == 0)) {
3051 /* haven't reached next snap point, and we're not switching
3052 trackviews. nothing to do.
3058 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3060 RegionView* rv2 = (*i);
3062 // If any regionview is at zero, we need to know so we can stop further leftward motion.
3064 double ix1, ix2, iy1, iy2;
3065 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3066 rv2->get_canvas_group()->i2w (ix1, iy1);
3075 /*************************************************************
3077 ************************************************************/
3081 if (drag_info.first_move) {
3082 if (drag_info.move_threshold_passed) {
3093 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
3094 const list<RegionView*>& layered_regions = selection->regions.by_layer();
3096 for (list<RegionView*>::const_iterator i = layered_regions.begin(); i != layered_regions.end(); ++i) {
3098 RegionView* rv = (*i);
3099 double ix1, ix2, iy1, iy2;
3100 int32_t temp_pointer_y_span = pointer_y_span;
3102 /* get item BBox, which will be relative to parent. so we have
3103 to query on a child, then convert to world coordinates using
3107 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3108 rv->get_canvas_group()->i2w (ix1, iy1);
3109 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3110 AudioTimeAxisView* canvas_atv = dynamic_cast<AudioTimeAxisView*>(tvp2);
3111 AudioTimeAxisView* temp_atv;
3113 if ((pointer_y_span != 0) && !clamp_y_axis) {
3116 for (j = height_list.begin(); j!= height_list.end(); j++) {
3117 if (x == canvas_atv->order) {
3118 /* we found the track the region is on */
3119 if (x != original_pointer_order) {
3120 /*this isn't from the same track we're dragging from */
3121 temp_pointer_y_span = canvas_pointer_y_span;
3123 while (temp_pointer_y_span > 0) {
3124 /* we're moving up canvas-wise,
3125 so we need to find the next track height
3127 if (j != height_list.begin()) {
3130 if (x != original_pointer_order) {
3131 /* we're not from the dragged track, so ignore hidden tracks. */
3133 temp_pointer_y_span++;
3137 temp_pointer_y_span--;
3139 while (temp_pointer_y_span < 0) {
3141 if (x != original_pointer_order) {
3143 temp_pointer_y_span--;
3147 if (j != height_list.end()) {
3150 temp_pointer_y_span++;
3152 /* find out where we'll be when we move and set height accordingly */
3154 tvp2 = trackview_by_y_position (iy1 + y_delta);
3155 temp_atv = dynamic_cast<AudioTimeAxisView*>(tvp2);
3156 rv->set_height (temp_atv->height);
3158 /* if you un-comment the following, the region colours will follow the track colours whilst dragging,
3159 personally, i think this can confuse things, but never mind.
3162 //const GdkColor& col (temp_atv->view->get_region_color());
3163 //rv->set_color (const_cast<GdkColor&>(col));
3170 /* prevent the regionview from being moved to before
3171 the zero position on the canvas.
3176 if (-x_delta > ix1) {
3179 } else if ((x_delta > 0) &&(rv->region()->last_frame() > max_frames - x_delta)) {
3180 x_delta = max_frames - rv->region()->last_frame();
3183 if (drag_info.first_move) {
3185 /* hide any dependent views */
3187 rv->get_time_axis_view().hide_dependent_views (*rv);
3189 /* this is subtle. raising the regionview itself won't help,
3190 because raise_to_top() just puts the item on the top of
3191 its parent's stack. so, we need to put the trackview canvas_display group
3192 on the top, since its parent is the whole canvas.
3195 rv->get_canvas_group()->raise_to_top();
3196 rv->get_time_axis_view().canvas_display->raise_to_top();
3197 cursor_group->raise_to_top();
3199 rv->fake_set_opaque (true);
3202 if (drag_info.brushing) {
3203 mouse_brush_insert_region (rv, pending_region_position);
3205 rv->move (x_delta, y_delta);
3208 } /* foreach region */
3212 if (drag_info.first_move && drag_info.move_threshold_passed) {
3213 cursor_group->raise_to_top();
3214 drag_info.first_move = false;
3217 if (x_delta != 0 && !drag_info.brushing) {
3218 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3223 Editor::region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3226 RegionView* rv = reinterpret_cast<RegionView *> (drag_info.data);
3227 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
3228 bool nocommit = true;
3230 RouteTimeAxisView* atv;
3231 bool regionview_y_movement;
3232 bool regionview_x_movement;
3234 /* first_move is set to false if the regionview has been moved in the
3238 if (drag_info.first_move) {
3245 /* The regionview has been moved at some stage during the grab so we need
3246 to account for any mouse movement between this event and the last one.
3249 region_drag_motion_callback (item, event);
3251 if (drag_info.brushing) {
3252 /* all changes were made during motion event handlers */
3256 /* adjust for track speed */
3259 atv = dynamic_cast<AudioTimeAxisView*> (drag_info.last_trackview);
3260 if (atv && atv->get_diskstream()) {
3261 speed = atv->get_diskstream()->speed();
3264 regionview_x_movement = (drag_info.last_frame_position != (nframes_t) (rv->region()->position()/speed));
3265 regionview_y_movement = (drag_info.last_trackview != &rv->get_time_axis_view());
3267 //printf ("last_frame: %s position is %lu %g\n", rv->get_time_axis_view().name().c_str(), drag_info.last_frame_position, speed);
3268 //printf ("last_rackview: %s \n", drag_info.last_trackview->name().c_str());
3272 if (drag_info.copy) {
3273 if (drag_info.x_constrained) {
3274 op_string = _("fixed time region copy");
3276 op_string = _("region copy");
3279 if (drag_info.x_constrained) {
3280 op_string = _("fixed time region drag");
3282 op_string = _("region drag");
3286 begin_reversible_command (op_string);
3288 if (regionview_y_movement) {
3290 /* moved to a different audio track. */
3292 vector<RegionView*> new_selection;
3294 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ) {
3296 RegionView* rv = (*i);
3298 double ix1, ix2, iy1, iy2;
3300 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3301 rv->get_canvas_group()->i2w (ix1, iy1);
3302 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3303 AudioTimeAxisView* atv2 = dynamic_cast<AudioTimeAxisView*>(tvp2);
3305 boost::shared_ptr<Playlist> from_playlist = rv->region()->playlist();
3306 boost::shared_ptr<Playlist> to_playlist = atv2->playlist();
3308 where = (nframes_t) (unit_to_frame (ix1) * speed);
3309 boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region()));
3311 /* undo the previous hide_dependent_views so that xfades don't
3312 disappear on copying regions
3315 rv->get_time_axis_view().reveal_dependent_views (*rv);
3317 if (!drag_info.copy) {
3319 /* the region that used to be in the old playlist is not
3320 moved to the new one - we make a copy of it. as a result,
3321 any existing editor for the region should no longer be
3325 rv->hide_region_editor();
3326 rv->fake_set_opaque (false);
3328 session->add_command (new MementoCommand<Playlist>(*from_playlist, &from_playlist->get_state(), 0));
3329 from_playlist->remove_region ((rv->region()));
3330 session->add_command (new MementoCommand<Playlist>(*from_playlist, 0, &from_playlist->get_state()));
3333 latest_regionview = 0;
3335 sigc::connection c = atv2->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3336 session->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
3337 to_playlist->add_region (new_region, where);
3338 session->add_command (new MementoCommand<Playlist>(*to_playlist, 0, &to_playlist->get_state()));
3341 if (latest_regionview) {
3342 new_selection.push_back (latest_regionview);
3345 if (drag_info.copy) {
3346 // get rid of the copy
3350 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
3351 was selected in all of them, then removing it from the playlist will have removed all
3352 trace of it from the selection (i.e. there were N regions selected, we removed 1,
3353 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
3354 corresponding regionview, and the selection is now empty).
3356 this could have invalidated any and all iterators into the region selection.
3358 the heuristic we use here is: if the region selection is empty, break out of the loop
3359 here. if the region selection is not empty, then restart the loop because we know that
3360 we must have removed at least the region(view) we've just been working on as well as any
3361 that we processed on previous iterations.
3364 if (selection->regions.empty()) {
3367 i = selection->regions.by_layer().begin();
3371 selection->set (new_selection);
3375 /* motion within a single track */
3377 list<RegionView*> regions = selection->regions.by_layer();
3379 if (drag_info.copy) {
3380 selection->clear_regions();
3383 for (list<RegionView*>::iterator i = regions.begin(); i != regions.end(); ++i) {
3387 if (rv->region()->locked()) {
3392 if (regionview_x_movement) {
3393 double ownspeed = 1.0;
3394 atv = dynamic_cast<AudioTimeAxisView*> (&(rv->get_time_axis_view()));
3396 if (atv && atv->get_diskstream()) {
3397 ownspeed = atv->get_diskstream()->speed();
3400 /* base the new region position on the current position of the regionview.*/
3402 double ix1, ix2, iy1, iy2;
3404 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3405 rv->get_canvas_group()->i2w (ix1, iy1);
3406 where = (nframes_t) (unit_to_frame (ix1) * ownspeed);
3410 where = rv->region()->position();
3413 boost::shared_ptr<Playlist> to_playlist = rv->region()->playlist();
3415 assert (to_playlist);
3419 session->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
3421 if (drag_info.copy) {
3423 boost::shared_ptr<Region> newregion;
3424 boost::shared_ptr<Region> ar;
3426 if ((ar = boost::dynamic_pointer_cast<AudioRegion>(rv->region())) != 0) {
3427 newregion = RegionFactory::create (ar);
3429 /* XXX MIDI HERE drobilla */
3435 latest_regionview = 0;
3436 sigc::connection c = atv->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3437 to_playlist->add_region (newregion, (nframes_t) (where * atv->get_diskstream()->speed()));
3440 if (latest_regionview) {
3441 atv->reveal_dependent_views (*latest_regionview);
3442 selection->add (latest_regionview);
3445 /* if the original region was locked, we don't care for the new one */
3447 newregion->set_locked (false);
3451 /* just change the model */
3453 rv->region()->set_position (where, (void*) this);
3459 session->add_command (new MementoCommand<Playlist>(*to_playlist, 0, &to_playlist->get_state()));
3461 /* get rid of the copy */
3463 if (drag_info.copy) {
3472 commit_reversible_command ();
3477 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
3479 /* Either add to or set the set the region selection, unless
3480 this is an alignment click (control used)
3483 if (Keyboard::modifier_state_contains (event->state, Keyboard::Control)) {
3484 TimeAxisView* tv = &rv.get_time_axis_view();
3485 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*>(tv);
3487 if (atv && atv->is_audio_track()) {
3488 speed = atv->get_diskstream()->speed();
3491 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Alt))) {
3493 align_region (rv.region(), SyncPoint, (nframes_t) (edit_cursor->current_frame * speed));
3495 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Shift))) {
3497 align_region (rv.region(), End, (nframes_t) (edit_cursor->current_frame * speed));
3501 align_region (rv.region(), Start, (nframes_t) (edit_cursor->current_frame * speed));
3507 Editor::show_verbose_time_cursor (nframes_t frame, double offset, double xpos, double ypos)
3513 nframes_t frame_rate;
3520 switch (Profile->get_small_screen() ? ARDOUR_UI::instance()->primary_clock.mode () : ARDOUR_UI::instance()->secondary_clock.mode ()) {
3521 case AudioClock::BBT:
3522 session->bbt_time (frame, bbt);
3523 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, bbt.bars, bbt.beats, bbt.ticks);
3526 case AudioClock::SMPTE:
3527 session->smpte_time (frame, smpte);
3528 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
3531 case AudioClock::MinSec:
3532 /* XXX this is copied from show_verbose_duration_cursor() */
3533 frame_rate = session->frame_rate();
3534 hours = frame / (frame_rate * 3600);
3535 frame = frame % (frame_rate * 3600);
3536 mins = frame / (frame_rate * 60);
3537 frame = frame % (frame_rate * 60);
3538 secs = (float) frame / (float) frame_rate;
3539 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
3543 snprintf (buf, sizeof(buf), "%u", frame);
3547 if (xpos >= 0 && ypos >=0) {
3548 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
3551 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
3553 show_verbose_canvas_cursor ();
3557 Editor::show_verbose_duration_cursor (nframes_t start, nframes_t end, double offset, double xpos, double ypos)
3564 nframes_t distance, frame_rate;
3566 Meter meter_at_start(session->tempo_map().meter_at(start));
3572 switch (ARDOUR_UI::instance()->secondary_clock.mode ()) {
3573 case AudioClock::BBT:
3574 session->bbt_time (start, sbbt);
3575 session->bbt_time (end, ebbt);
3578 /* XXX this computation won't work well if the
3579 user makes a selection that spans any meter changes.
3582 ebbt.bars -= sbbt.bars;
3583 if (ebbt.beats >= sbbt.beats) {
3584 ebbt.beats -= sbbt.beats;
3587 ebbt.beats = int(meter_at_start.beats_per_bar()) + ebbt.beats - sbbt.beats;
3589 if (ebbt.ticks >= sbbt.ticks) {
3590 ebbt.ticks -= sbbt.ticks;
3593 ebbt.ticks = int(Meter::ticks_per_beat) + ebbt.ticks - sbbt.ticks;
3596 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, ebbt.bars, ebbt.beats, ebbt.ticks);
3599 case AudioClock::SMPTE:
3600 session->smpte_duration (end - start, smpte);
3601 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
3604 case AudioClock::MinSec:
3605 /* XXX this stuff should be elsewhere.. */
3606 distance = end - start;
3607 frame_rate = session->frame_rate();
3608 hours = distance / (frame_rate * 3600);
3609 distance = distance % (frame_rate * 3600);
3610 mins = distance / (frame_rate * 60);
3611 distance = distance % (frame_rate * 60);
3612 secs = (float) distance / (float) frame_rate;
3613 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
3617 snprintf (buf, sizeof(buf), "%u", end - start);
3621 if (xpos >= 0 && ypos >=0) {
3622 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
3625 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
3627 show_verbose_canvas_cursor ();
3631 Editor::collect_new_region_view (RegionView* rv)
3633 latest_regionview = rv;
3637 Editor::start_selection_grab (ArdourCanvas::Item* item, GdkEvent* event)
3639 if (clicked_regionview == 0) {
3643 /* lets try to create new Region for the selection */
3645 vector<boost::shared_ptr<AudioRegion> > new_regions;
3646 create_region_from_selection (new_regions);
3648 if (new_regions.empty()) {
3652 /* XXX fix me one day to use all new regions */
3654 boost::shared_ptr<Region> region (new_regions.front());
3656 /* add it to the current stream/playlist.
3658 tricky: the streamview for the track will add a new regionview. we will
3659 catch the signal it sends when it creates the regionview to
3660 set the regionview we want to then drag.
3663 latest_regionview = 0;
3664 sigc::connection c = clicked_audio_trackview->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3666 /* A selection grab currently creates two undo/redo operations, one for
3667 creating the new region and another for moving it.
3670 begin_reversible_command (_("selection grab"));
3672 boost::shared_ptr<Playlist> playlist = clicked_trackview->playlist();
3674 XMLNode *before = &(playlist->get_state());
3675 clicked_trackview->playlist()->add_region (region, selection->time[clicked_selection].start);
3676 XMLNode *after = &(playlist->get_state());
3677 session->add_command(new MementoCommand<Playlist>(*playlist, before, after));
3679 commit_reversible_command ();
3683 if (latest_regionview == 0) {
3684 /* something went wrong */
3688 /* we need to deselect all other regionviews, and select this one
3689 i'm ignoring undo stuff, because the region creation will take care of it */
3690 selection->set (latest_regionview);
3692 drag_info.item = latest_regionview->get_canvas_group();
3693 drag_info.data = latest_regionview;
3694 drag_info.motion_callback = &Editor::region_drag_motion_callback;
3695 drag_info.finished_callback = &Editor::region_drag_finished_callback;
3699 drag_info.last_trackview = clicked_trackview;
3700 drag_info.last_frame_position = latest_regionview->region()->position();
3701 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
3703 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3707 Editor::cancel_selection ()
3709 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
3710 (*i)->hide_selection ();
3712 begin_reversible_command (_("cancel selection"));
3713 selection->clear ();
3714 clicked_selection = 0;
3715 commit_reversible_command ();
3719 Editor::start_selection_op (ArdourCanvas::Item* item, GdkEvent* event, SelectionOp op)
3721 nframes_t start = 0;
3728 drag_info.item = item;
3729 drag_info.motion_callback = &Editor::drag_selection;
3730 drag_info.finished_callback = &Editor::end_selection_op;
3735 case CreateSelection:
3736 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
3737 drag_info.copy = true;
3739 drag_info.copy = false;
3741 start_grab (event, selector_cursor);
3744 case SelectionStartTrim:
3745 if (clicked_trackview) {
3746 clicked_trackview->order_selection_trims (item, true);
3748 start_grab (event, trimmer_cursor);
3749 start = selection->time[clicked_selection].start;
3750 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
3753 case SelectionEndTrim:
3754 if (clicked_trackview) {
3755 clicked_trackview->order_selection_trims (item, false);
3757 start_grab (event, trimmer_cursor);
3758 end = selection->time[clicked_selection].end;
3759 drag_info.pointer_frame_offset = drag_info.grab_frame - end;
3763 start = selection->time[clicked_selection].start;
3765 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
3769 if (selection_op == SelectionMove) {
3770 show_verbose_time_cursor(start, 10);
3772 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
3777 Editor::drag_selection (ArdourCanvas::Item* item, GdkEvent* event)
3779 nframes_t start = 0;
3782 nframes_t pending_position;
3784 if ((int32_t) drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
3785 pending_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
3788 pending_position = 0;
3791 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3792 snap_to (pending_position);
3795 /* only alter selection if the current frame is
3796 different from the last frame position (adjusted)
3799 if (pending_position == drag_info.last_pointer_frame) return;
3801 switch (selection_op) {
3802 case CreateSelection:
3804 if (drag_info.first_move) {
3805 snap_to (drag_info.grab_frame);
3808 if (pending_position < drag_info.grab_frame) {
3809 start = pending_position;
3810 end = drag_info.grab_frame;
3812 end = pending_position;
3813 start = drag_info.grab_frame;
3816 /* first drag: Either add to the selection
3817 or create a new selection->
3820 if (drag_info.first_move) {
3822 begin_reversible_command (_("range selection"));
3824 if (drag_info.copy) {
3825 /* adding to the selection */
3826 clicked_selection = selection->add (start, end);
3827 drag_info.copy = false;
3829 /* new selection-> */
3830 clicked_selection = selection->set (clicked_trackview, start, end);
3835 case SelectionStartTrim:
3837 if (drag_info.first_move) {
3838 begin_reversible_command (_("trim selection start"));
3841 start = selection->time[clicked_selection].start;
3842 end = selection->time[clicked_selection].end;
3844 if (pending_position > end) {
3847 start = pending_position;
3851 case SelectionEndTrim:
3853 if (drag_info.first_move) {
3854 begin_reversible_command (_("trim selection end"));
3857 start = selection->time[clicked_selection].start;
3858 end = selection->time[clicked_selection].end;
3860 if (pending_position < start) {
3863 end = pending_position;
3870 if (drag_info.first_move) {
3871 begin_reversible_command (_("move selection"));
3874 start = selection->time[clicked_selection].start;
3875 end = selection->time[clicked_selection].end;
3877 length = end - start;
3879 start = pending_position;
3882 end = start + length;
3887 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
3888 start_canvas_autoscroll (1);
3892 selection->replace (clicked_selection, start, end);
3895 drag_info.last_pointer_frame = pending_position;
3896 drag_info.first_move = false;
3898 if (selection_op == SelectionMove) {
3899 show_verbose_time_cursor(start, 10);
3901 show_verbose_time_cursor(pending_position, 10);
3906 Editor::end_selection_op (ArdourCanvas::Item* item, GdkEvent* event)
3908 if (!drag_info.first_move) {
3909 drag_selection (item, event);
3910 /* XXX this is not object-oriented programming at all. ick */
3911 if (selection->time.consolidate()) {
3912 selection->TimeChanged ();
3914 commit_reversible_command ();
3916 /* just a click, no pointer movement.*/
3918 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
3920 selection->clear_time();
3925 /* XXX what happens if its a music selection? */
3926 session->set_audio_range (selection->time);
3927 stop_canvas_autoscroll ();
3931 Editor::start_trim (ArdourCanvas::Item* item, GdkEvent* event)
3934 TimeAxisView* tvp = clicked_trackview;
3935 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
3937 if (tv && tv->is_audio_track()) {
3938 speed = tv->get_diskstream()->speed();
3941 nframes_t region_start = (nframes_t) (clicked_regionview->region()->position() / speed);
3942 nframes_t region_end = (nframes_t) (clicked_regionview->region()->last_frame() / speed);
3943 nframes_t region_length = (nframes_t) (clicked_regionview->region()->length() / speed);
3945 //drag_info.item = clicked_regionview->get_name_highlight();
3946 drag_info.item = item;
3947 drag_info.motion_callback = &Editor::trim_motion_callback;
3948 drag_info.finished_callback = &Editor::trim_finished_callback;
3950 start_grab (event, trimmer_cursor);
3952 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
3953 trim_op = ContentsTrim;
3955 /* These will get overridden for a point trim.*/
3956 if (drag_info.current_pointer_frame < (region_start + region_length/2)) {
3957 /* closer to start */
3958 trim_op = StartTrim;
3959 } else if (drag_info.current_pointer_frame > (region_end - region_length/2)) {
3967 show_verbose_time_cursor(region_start, 10);
3970 show_verbose_time_cursor(region_end, 10);
3973 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
3979 Editor::trim_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
3981 RegionView* rv = clicked_regionview;
3982 nframes_t frame_delta = 0;
3983 bool left_direction;
3984 bool obey_snap = !Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier());
3986 /* snap modifier works differently here..
3987 its' current state has to be passed to the
3988 various trim functions in order to work properly
3992 TimeAxisView* tvp = clicked_trackview;
3993 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
3994 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
3996 if (tv && tv->is_audio_track()) {
3997 speed = tv->get_diskstream()->speed();
4000 if (drag_info.last_pointer_frame > drag_info.current_pointer_frame) {
4001 left_direction = true;
4003 left_direction = false;
4007 snap_to (drag_info.current_pointer_frame);
4010 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
4014 if (drag_info.first_move) {
4020 trim_type = "Region start trim";
4023 trim_type = "Region end trim";
4026 trim_type = "Region content trim";
4030 begin_reversible_command (trim_type);
4032 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4033 (*i)->fake_set_opaque(false);
4034 (*i)->region()->freeze ();
4036 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
4038 arv->temporarily_hide_envelope ();
4040 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4041 insert_result = motion_frozen_playlists.insert (pl);
4042 if (insert_result.second) {
4043 session->add_command(new MementoCommand<Playlist>(*pl, &pl->get_state(), 0));
4048 if (left_direction) {
4049 frame_delta = (drag_info.last_pointer_frame - drag_info.current_pointer_frame);
4051 frame_delta = (drag_info.current_pointer_frame - drag_info.last_pointer_frame);
4056 if ((left_direction == false) && (drag_info.current_pointer_frame <= rv->region()->first_frame()/speed)) {
4059 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4060 single_start_trim (**i, frame_delta, left_direction, obey_snap);
4066 if ((left_direction == true) && (drag_info.current_pointer_frame > (nframes_t) (rv->region()->last_frame()/speed))) {
4069 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4070 single_end_trim (**i, frame_delta, left_direction, obey_snap);
4077 bool swap_direction = false;
4079 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
4080 swap_direction = true;
4083 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4084 i != selection->regions.by_layer().end(); ++i)
4086 single_contents_trim (**i, frame_delta, left_direction, swap_direction, obey_snap);
4094 show_verbose_time_cursor((nframes_t) (rv->region()->position()/speed), 10);
4097 show_verbose_time_cursor((nframes_t) (rv->region()->last_frame()/speed), 10);
4100 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4104 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4105 drag_info.first_move = false;
4109 Editor::single_contents_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool swap_direction, bool obey_snap)
4111 boost::shared_ptr<Region> region (rv.region());
4113 if (region->locked()) {
4117 nframes_t new_bound;
4120 TimeAxisView* tvp = clicked_trackview;
4121 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4123 if (tv && tv->is_audio_track()) {
4124 speed = tv->get_diskstream()->speed();
4127 if (left_direction) {
4128 if (swap_direction) {
4129 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4131 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4134 if (swap_direction) {
4135 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4137 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4142 snap_to (new_bound);
4144 region->trim_start ((nframes_t) (new_bound * speed), this);
4145 rv.region_changed (StartChanged);
4149 Editor::single_start_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool obey_snap)
4151 boost::shared_ptr<Region> region (rv.region());
4153 if (region->locked()) {
4157 nframes_t new_bound;
4160 TimeAxisView* tvp = clicked_trackview;
4161 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
4163 if (tv && tv->is_audio_track()) {
4164 speed = tv->get_diskstream()->speed();
4167 if (left_direction) {
4168 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4170 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4174 snap_to (new_bound, (left_direction ? 0 : 1));
4177 region->trim_front ((nframes_t) (new_bound * speed), this);
4179 rv.region_changed (Change (LengthChanged|PositionChanged|StartChanged));
4183 Editor::single_end_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool obey_snap)
4185 boost::shared_ptr<Region> region (rv.region());
4187 if (region->locked()) {
4191 nframes_t new_bound;
4194 TimeAxisView* tvp = clicked_trackview;
4195 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
4197 if (tv && tv->is_audio_track()) {
4198 speed = tv->get_diskstream()->speed();
4201 if (left_direction) {
4202 new_bound = (nframes_t) ((region->last_frame() + 1)/speed) - frame_delta;
4204 new_bound = (nframes_t) ((region->last_frame() + 1)/speed) + frame_delta;
4208 snap_to (new_bound);
4210 region->trim_end ((nframes_t) (new_bound * speed), this);
4211 rv.region_changed (LengthChanged);
4215 Editor::trim_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
4217 if (!drag_info.first_move) {
4218 trim_motion_callback (item, event);
4220 if (!clicked_regionview->get_selected()) {
4221 thaw_region_after_trim (*clicked_regionview);
4224 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4225 i != selection->regions.by_layer().end(); ++i)
4227 thaw_region_after_trim (**i);
4228 (*i)->fake_set_opaque (true);
4232 for (set<boost::shared_ptr<Playlist> >::iterator p = motion_frozen_playlists.begin(); p != motion_frozen_playlists.end(); ++p) {
4234 session->add_command (new MementoCommand<Playlist>(*(*p).get(), 0, &(*p)->get_state()));
4237 motion_frozen_playlists.clear ();
4239 commit_reversible_command();
4241 /* no mouse movement */
4247 Editor::point_trim (GdkEvent* event)
4249 RegionView* rv = clicked_regionview;
4250 nframes_t new_bound = drag_info.current_pointer_frame;
4252 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4253 snap_to (new_bound);
4256 /* Choose action dependant on which button was pressed */
4257 switch (event->button.button) {
4259 trim_op = StartTrim;
4260 begin_reversible_command (_("Start point trim"));
4262 if (rv->get_selected()) {
4264 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4265 i != selection->regions.by_layer().end(); ++i)
4267 if (!(*i)->region()->locked()) {
4268 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4269 XMLNode &before = pl->get_state();
4270 (*i)->region()->trim_front (new_bound, this);
4271 XMLNode &after = pl->get_state();
4272 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4278 if (!rv->region()->locked()) {
4279 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
4280 XMLNode &before = pl->get_state();
4281 rv->region()->trim_front (new_bound, this);
4282 XMLNode &after = pl->get_state();
4283 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4287 commit_reversible_command();
4292 begin_reversible_command (_("End point trim"));
4294 if (rv->get_selected()) {
4296 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
4298 if (!(*i)->region()->locked()) {
4299 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4300 XMLNode &before = pl->get_state();
4301 (*i)->region()->trim_end (new_bound, this);
4302 XMLNode &after = pl->get_state();
4303 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4309 if (!rv->region()->locked()) {
4310 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
4311 XMLNode &before = pl->get_state();
4312 rv->region()->trim_end (new_bound, this);
4313 XMLNode &after = pl->get_state();
4314 session->add_command (new MementoCommand<Playlist>(*pl.get(), &before, &after));
4318 commit_reversible_command();
4327 Editor::thaw_region_after_trim (RegionView& rv)
4329 boost::shared_ptr<Region> region (rv.region());
4331 if (region->locked()) {
4335 region->thaw (_("trimmed region"));
4336 XMLNode &after = region->playlist()->get_state();
4337 session->add_command (new MementoCommand<Playlist>(*(region->playlist()), 0, &after));
4339 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(&rv);
4341 arv->unhide_envelope ();
4345 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* event)
4350 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
4351 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
4355 Location* location = find_location_from_marker (marker, is_start);
4356 location->set_hidden (true, this);
4361 Editor::start_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event, RangeMarkerOp op)
4367 drag_info.item = item;
4368 drag_info.motion_callback = &Editor::drag_range_markerbar_op;
4369 drag_info.finished_callback = &Editor::end_range_markerbar_op;
4371 range_marker_op = op;
4373 if (!temp_location) {
4374 temp_location = new Location;
4378 case CreateRangeMarker:
4379 case CreateTransportMarker:
4381 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
4382 drag_info.copy = true;
4384 drag_info.copy = false;
4386 start_grab (event, selector_cursor);
4390 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4395 Editor::drag_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4397 nframes_t start = 0;
4399 ArdourCanvas::SimpleRect *crect = (range_marker_op == CreateRangeMarker) ? range_bar_drag_rect: transport_bar_drag_rect;
4401 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4402 snap_to (drag_info.current_pointer_frame);
4405 /* only alter selection if the current frame is
4406 different from the last frame position.
4409 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4411 switch (range_marker_op) {
4412 case CreateRangeMarker:
4413 case CreateTransportMarker:
4414 if (drag_info.first_move) {
4415 snap_to (drag_info.grab_frame);
4418 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4419 start = drag_info.current_pointer_frame;
4420 end = drag_info.grab_frame;
4422 end = drag_info.current_pointer_frame;
4423 start = drag_info.grab_frame;
4426 /* first drag: Either add to the selection
4427 or create a new selection.
4430 if (drag_info.first_move) {
4432 temp_location->set (start, end);
4436 update_marker_drag_item (temp_location);
4437 range_marker_drag_rect->show();
4438 range_marker_drag_rect->raise_to_top();
4444 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
4445 start_canvas_autoscroll (1);
4449 temp_location->set (start, end);
4451 double x1 = frame_to_pixel (start);
4452 double x2 = frame_to_pixel (end);
4453 crect->property_x1() = x1;
4454 crect->property_x2() = x2;
4456 update_marker_drag_item (temp_location);
4459 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4460 drag_info.first_move = false;
4462 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4467 Editor::end_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4469 Location * newloc = 0;
4472 if (!drag_info.first_move) {
4473 drag_range_markerbar_op (item, event);
4475 switch (range_marker_op) {
4476 case CreateRangeMarker:
4478 begin_reversible_command (_("new range marker"));
4479 XMLNode &before = session->locations()->get_state();
4480 session->locations()->next_available_name(rangename,"unnamed");
4481 newloc = new Location(temp_location->start(), temp_location->end(), rangename, Location::IsRangeMarker);
4482 session->locations()->add (newloc, true);
4483 XMLNode &after = session->locations()->get_state();
4484 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
4485 commit_reversible_command ();
4487 range_bar_drag_rect->hide();
4488 range_marker_drag_rect->hide();
4492 case CreateTransportMarker:
4493 // popup menu to pick loop or punch
4494 new_transport_marker_context_menu (&event->button, item);
4499 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
4501 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
4506 start = session->locations()->first_mark_before (drag_info.grab_frame);
4507 end = session->locations()->first_mark_after (drag_info.grab_frame);
4509 if (end == max_frames) {
4510 end = session->current_end_frame ();
4514 start = session->current_start_frame ();
4517 switch (mouse_mode) {
4519 /* find the two markers on either side and then make the selection from it */
4520 select_all_within (start, end, 0.0f, FLT_MAX, Selection::Set);
4524 /* find the two markers on either side of the click and make the range out of it */
4525 selection->set (0, start, end);
4534 stop_canvas_autoscroll ();
4540 Editor::start_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4542 drag_info.item = item;
4543 drag_info.motion_callback = &Editor::drag_mouse_zoom;
4544 drag_info.finished_callback = &Editor::end_mouse_zoom;
4546 start_grab (event, zoom_cursor);
4548 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4552 Editor::drag_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4557 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4558 snap_to (drag_info.current_pointer_frame);
4560 if (drag_info.first_move) {
4561 snap_to (drag_info.grab_frame);
4565 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4567 /* base start and end on initial click position */
4568 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4569 start = drag_info.current_pointer_frame;
4570 end = drag_info.grab_frame;
4572 end = drag_info.current_pointer_frame;
4573 start = drag_info.grab_frame;
4578 if (drag_info.first_move) {
4580 zoom_rect->raise_to_top();
4583 reposition_zoom_rect(start, end);
4585 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4586 drag_info.first_move = false;
4588 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4593 Editor::end_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4595 if (!drag_info.first_move) {
4596 drag_mouse_zoom (item, event);
4598 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
4599 temporal_zoom_by_frame (drag_info.grab_frame, drag_info.last_pointer_frame, "mouse zoom");
4601 temporal_zoom_by_frame (drag_info.last_pointer_frame, drag_info.grab_frame, "mouse zoom");
4604 temporal_zoom_to_frame (false, drag_info.grab_frame);
4606 temporal_zoom_step (false);
4607 center_screen (drag_info.grab_frame);
4615 Editor::reposition_zoom_rect (nframes_t start, nframes_t end)
4617 double x1 = frame_to_pixel (start);
4618 double x2 = frame_to_pixel (end);
4619 double y2 = full_canvas_height - 1.0;
4621 zoom_rect->property_x1() = x1;
4622 zoom_rect->property_y1() = 1.0;
4623 zoom_rect->property_x2() = x2;
4624 zoom_rect->property_y2() = y2;
4628 Editor::start_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4630 drag_info.item = item;
4631 drag_info.motion_callback = &Editor::drag_rubberband_select;
4632 drag_info.finished_callback = &Editor::end_rubberband_select;
4634 start_grab (event, cross_hair_cursor);
4636 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4640 Editor::drag_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4647 /* use a bigger drag threshold than the default */
4649 if (abs ((int) (drag_info.current_pointer_frame - drag_info.grab_frame)) < 8) {
4653 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4654 if (drag_info.first_move) {
4655 snap_to (drag_info.grab_frame);
4657 snap_to (drag_info.current_pointer_frame);
4660 /* base start and end on initial click position */
4662 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4663 start = drag_info.current_pointer_frame;
4664 end = drag_info.grab_frame;
4666 end = drag_info.current_pointer_frame;
4667 start = drag_info.grab_frame;
4670 if (drag_info.current_pointer_y < drag_info.grab_y) {
4671 y1 = drag_info.current_pointer_y;
4672 y2 = drag_info.grab_y;
4674 y2 = drag_info.current_pointer_y;
4675 y1 = drag_info.grab_y;
4679 if (start != end || y1 != y2) {
4681 double x1 = frame_to_pixel (start);
4682 double x2 = frame_to_pixel (end);
4684 rubberband_rect->property_x1() = x1;
4685 rubberband_rect->property_y1() = y1;
4686 rubberband_rect->property_x2() = x2;
4687 rubberband_rect->property_y2() = y2;
4689 rubberband_rect->show();
4690 rubberband_rect->raise_to_top();
4692 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4693 drag_info.first_move = false;
4695 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4700 Editor::end_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4702 if (!drag_info.first_move) {
4704 drag_rubberband_select (item, event);
4707 if (drag_info.current_pointer_y < drag_info.grab_y) {
4708 y1 = drag_info.current_pointer_y;
4709 y2 = drag_info.grab_y;
4712 y2 = drag_info.current_pointer_y;
4713 y1 = drag_info.grab_y;
4717 Selection::Operation op = Keyboard::selection_type (event->button.state);
4720 begin_reversible_command (_("rubberband selection"));
4722 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
4723 commit = select_all_within (drag_info.grab_frame, drag_info.last_pointer_frame, y1, y2, op);
4725 commit = select_all_within (drag_info.last_pointer_frame, drag_info.grab_frame, y1, y2, op);
4729 commit_reversible_command ();
4733 selection->clear_tracks();
4734 selection->clear_regions();
4735 selection->clear_points ();
4736 selection->clear_lines ();
4739 rubberband_rect->hide();
4744 Editor::mouse_rename_region (ArdourCanvas::Item* item, GdkEvent* event)
4746 using namespace Gtkmm2ext;
4748 ArdourPrompter prompter (false);
4750 prompter.set_prompt (_("Name for region:"));
4751 prompter.set_initial_text (clicked_regionview->region()->name());
4752 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
4753 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
4754 prompter.show_all ();
4755 switch (prompter.run ()) {
4756 case Gtk::RESPONSE_ACCEPT:
4758 prompter.get_result(str);
4760 clicked_regionview->region()->set_name (str);
4768 Editor::start_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
4770 drag_info.item = item;
4771 drag_info.motion_callback = &Editor::time_fx_motion;
4772 drag_info.finished_callback = &Editor::end_time_fx;
4776 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4780 Editor::time_fx_motion (ArdourCanvas::Item *item, GdkEvent* event)
4782 RegionView* rv = clicked_regionview;
4784 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4785 snap_to (drag_info.current_pointer_frame);
4788 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
4792 if (drag_info.current_pointer_frame > rv->region()->position()) {
4793 rv->get_time_axis_view().show_timestretch (rv->region()->position(), drag_info.current_pointer_frame);
4796 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4797 drag_info.first_move = false;
4799 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4803 Editor::end_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
4805 clicked_regionview->get_time_axis_view().hide_timestretch ();
4807 if (drag_info.first_move) {
4811 nframes_t newlen = drag_info.last_pointer_frame - clicked_regionview->region()->position();
4812 float percentage = (float) ((double) newlen - (double) clicked_regionview->region()->length()) / ((double) newlen) * 100.0f;
4814 begin_reversible_command (_("timestretch"));
4816 if (run_timestretch (selection->regions, percentage) == 0) {
4817 session->commit_reversible_command ();
4822 Editor::mouse_brush_insert_region (RegionView* rv, nframes_t pos)
4824 /* no brushing without a useful snap setting */
4827 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
4830 switch (snap_mode) {
4832 return; /* can't work because it allows region to be placed anywhere */
4837 switch (snap_type) {
4840 case SnapToEditCursor:
4847 /* don't brush a copy over the original */
4849 if (pos == rv->region()->position()) {
4853 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*>(&arv->get_time_axis_view());
4855 if (atv == 0 || !atv->is_audio_track()) {
4859 boost::shared_ptr<Playlist> playlist = atv->playlist();
4860 double speed = atv->get_diskstream()->speed();
4862 XMLNode &before = playlist->get_state();
4863 playlist->add_region (boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (arv->audio_region())), (nframes_t) (pos * speed));
4864 XMLNode &after = playlist->get_state();
4865 session->add_command(new MementoCommand<Playlist>(*playlist.get(), &before, &after));
4867 // playlist is frozen, so we have to update manually
4869 playlist->Modified(); /* EMIT SIGNAL */
4873 Editor::track_height_step_timeout ()
4876 struct timeval delta;
4878 gettimeofday (&now, 0);
4879 timersub (&now, &last_track_height_step_timestamp, &delta);
4881 if (delta.tv_sec * 1000000 + delta.tv_usec > 250000) { /* milliseconds */
4882 current_stepping_trackview = 0;