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"
37 #include "midi_region_view.h"
39 #include "streamview.h"
40 #include "region_gain_line.h"
41 #include "automation_time_axis.h"
44 #include "selection.h"
47 #include "rgb_macros.h"
49 #include <ardour/types.h>
50 #include <ardour/profile.h>
51 #include <ardour/route.h>
52 #include <ardour/audio_track.h>
53 #include <ardour/audio_diskstream.h>
54 #include <ardour/playlist.h>
55 #include <ardour/audioplaylist.h>
56 #include <ardour/audioregion.h>
57 #include <ardour/midi_region.h>
58 #include <ardour/dB.h>
59 #include <ardour/utils.h>
60 #include <ardour/region_factory.h>
67 using namespace ARDOUR;
71 using namespace Editing;
74 Editor::event_frame (GdkEvent* event, double* pcx, double* pcy)
88 switch (event->type) {
89 case GDK_BUTTON_RELEASE:
90 case GDK_BUTTON_PRESS:
91 case GDK_2BUTTON_PRESS:
92 case GDK_3BUTTON_PRESS:
93 track_canvas.w2c(event->button.x, event->button.y, *pcx, *pcy);
95 case GDK_MOTION_NOTIFY:
96 track_canvas.w2c(event->motion.x, event->motion.y, *pcx, *pcy);
98 case GDK_ENTER_NOTIFY:
99 case GDK_LEAVE_NOTIFY:
100 track_canvas.w2c(event->crossing.x, event->crossing.y, *pcx, *pcy);
103 case GDK_KEY_RELEASE:
104 // track_canvas.w2c(event->key.x, event->key.y, *pcx, *pcy);
107 warning << string_compose (_("Editor::event_frame() used on unhandled event type %1"), event->type) << endmsg;
111 /* note that pixel_to_frame() never returns less than zero, so even if the pixel
112 position is negative (as can be the case with motion events in particular),
113 the frame location is always positive.
116 return pixel_to_frame (*pcx);
120 Editor::mouse_mode_toggled (MouseMode m)
122 if (ignore_mouse_mode_toggle) {
128 if (mouse_select_button.get_active()) {
134 if (mouse_move_button.get_active()) {
140 if (mouse_gain_button.get_active()) {
146 if (mouse_zoom_button.get_active()) {
152 if (mouse_timefx_button.get_active()) {
158 if (mouse_audition_button.get_active()) {
169 Editor::set_mouse_mode (MouseMode m, bool force)
171 if (drag_info.item) {
175 if (!force && m == mouse_mode) {
183 if (mouse_mode != MouseRange) {
185 /* in all modes except range, hide the range selection,
186 show the object (region) selection.
189 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
190 (*i)->set_should_show_selection (true);
192 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
193 (*i)->hide_selection ();
199 in range mode,show the range selection.
202 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
203 if ((*i)->get_selected()) {
204 (*i)->show_selection (selection->time);
209 /* XXX the hack of unsetting all other buttongs should go
210 away once GTK2 allows us to use regular radio buttons drawn like
211 normal buttons, rather than my silly GroupedButton hack.
214 ignore_mouse_mode_toggle = true;
216 switch (mouse_mode) {
218 mouse_select_button.set_active (true);
219 current_canvas_cursor = selector_cursor;
223 mouse_move_button.set_active (true);
224 current_canvas_cursor = grabber_cursor;
228 mouse_gain_button.set_active (true);
229 current_canvas_cursor = cross_hair_cursor;
233 mouse_zoom_button.set_active (true);
234 current_canvas_cursor = zoom_cursor;
238 mouse_timefx_button.set_active (true);
239 current_canvas_cursor = time_fx_cursor; // just use playhead
243 mouse_audition_button.set_active (true);
244 current_canvas_cursor = speaker_cursor;
248 ignore_mouse_mode_toggle = false;
251 track_canvas.get_window()->set_cursor(*current_canvas_cursor);
256 Editor::step_mouse_mode (bool next)
258 switch (current_mouse_mode()) {
260 if (next) set_mouse_mode (MouseRange);
261 else set_mouse_mode (MouseTimeFX);
265 if (next) set_mouse_mode (MouseZoom);
266 else set_mouse_mode (MouseObject);
270 if (next) set_mouse_mode (MouseGain);
271 else set_mouse_mode (MouseRange);
275 if (next) set_mouse_mode (MouseTimeFX);
276 else set_mouse_mode (MouseZoom);
280 if (next) set_mouse_mode (MouseAudition);
281 else set_mouse_mode (MouseGain);
285 if (next) set_mouse_mode (MouseObject);
286 else set_mouse_mode (MouseTimeFX);
292 Editor::button_selection (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
296 /* in object/audition/timefx mode, any button press sets
297 the selection if the object can be selected. this is a
298 bit of hack, because we want to avoid this if the
299 mouse operation is a region alignment.
301 note: not dbl-click or triple-click
304 if (((mouse_mode != MouseObject) &&
305 (mouse_mode != MouseAudition || item_type != RegionItem) &&
306 (mouse_mode != MouseTimeFX || item_type != RegionItem) &&
307 (mouse_mode != MouseRange)) ||
309 (event->type != GDK_BUTTON_PRESS && event->type != GDK_BUTTON_RELEASE || event->button.button > 3)) {
314 if (event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) {
316 if ((event->button.state & Keyboard::RelevantModifierKeyMask) && event->button.button != 1) {
318 /* almost no selection action on modified button-2 or button-3 events */
320 if (item_type != RegionItem && event->button.button != 2) {
326 Selection::Operation op = Keyboard::selection_type (event->button.state);
327 bool press = (event->type == GDK_BUTTON_PRESS);
329 // begin_reversible_command (_("select on click"));
333 case RegionViewNameHighlight:
335 case FadeInHandleItem:
337 case FadeOutHandleItem:
339 if (mouse_mode != MouseRange) {
340 commit = set_selected_regionview_from_click (press, op, true);
341 } else if (event->type == GDK_BUTTON_PRESS) {
342 commit = set_selected_track_from_click (press, op, false);
346 case CrossfadeViewItem:
347 commit = set_selected_track_from_click (press, op, false);
350 case GainAutomationControlPointItem:
351 case PanAutomationControlPointItem:
352 case RedirectAutomationControlPointItem:
353 commit = set_selected_track_from_click (press, op, true);
354 if (mouse_mode != MouseRange) {
355 commit |= set_selected_control_point_from_click (op, false);
360 /* for context click or range selection, select track */
361 if (event->button.button == 3) {
362 commit = set_selected_track_from_click (press, op, true);
363 } else if (event->type == GDK_BUTTON_PRESS && mouse_mode == MouseRange) {
364 commit = set_selected_track_from_click (press, op, false);
368 case AutomationTrackItem:
369 commit = set_selected_track_from_click (press, op, true);
377 // commit_reversible_command ();
382 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
384 track_canvas.grab_focus();
386 if (session && session->actively_recording()) {
390 button_selection (item, event, item_type);
392 if (drag_info.item == 0 &&
393 (Keyboard::is_delete_event (&event->button) ||
394 Keyboard::is_context_menu_event (&event->button) ||
395 Keyboard::is_edit_event (&event->button))) {
397 /* handled by button release */
401 switch (event->button.button) {
404 if (event->type == GDK_BUTTON_PRESS) {
406 if (drag_info.item) {
407 drag_info.item->ungrab (event->button.time);
410 /* single mouse clicks on any of these item types operate
411 independent of mouse mode, mostly because they are
412 not on the main track canvas or because we want
418 case PlayheadCursorItem:
419 start_cursor_grab (item, event);
423 if (Keyboard::modifier_state_equals (event->button.state,
424 Keyboard::ModifierMask(Keyboard::Control|Keyboard::Shift))) {
425 hide_marker (item, event);
427 start_marker_grab (item, event);
431 case TempoMarkerItem:
432 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
433 start_tempo_marker_copy_grab (item, event);
435 start_tempo_marker_grab (item, event);
439 case MeterMarkerItem:
440 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
441 start_meter_marker_copy_grab (item, event);
443 start_meter_marker_grab (item, event);
453 case RangeMarkerBarItem:
454 start_range_markerbar_op (item, event, CreateRangeMarker);
458 case TransportMarkerBarItem:
459 start_range_markerbar_op (item, event, CreateTransportMarker);
468 switch (mouse_mode) {
471 case StartSelectionTrimItem:
472 start_selection_op (item, event, SelectionStartTrim);
475 case EndSelectionTrimItem:
476 start_selection_op (item, event, SelectionEndTrim);
480 if (Keyboard::modifier_state_contains
481 (event->button.state, Keyboard::ModifierMask(Keyboard::Alt))) {
482 // contains and not equals because I can't use alt as a modifier alone.
483 start_selection_grab (item, event);
484 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
485 /* grab selection for moving */
486 start_selection_op (item, event, SelectionMove);
489 /* this was debated, but decided the more common action was to
490 make a new selection */
491 start_selection_op (item, event, CreateSelection);
496 start_selection_op (item, event, CreateSelection);
502 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::Control|Keyboard::Alt)) &&
503 event->type == GDK_BUTTON_PRESS) {
505 start_rubberband_select (item, event);
507 } else if (event->type == GDK_BUTTON_PRESS) {
510 case FadeInHandleItem:
511 start_fade_in_grab (item, event);
514 case FadeOutHandleItem:
515 start_fade_out_grab (item, event);
519 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
520 start_region_copy_grab (item, event);
521 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
522 start_region_brush_grab (item, event);
524 start_region_grab (item, event);
528 case RegionViewNameHighlight:
529 start_trim (item, event);
534 /* rename happens on edit clicks */
535 start_trim (clicked_regionview->get_name_highlight(), event);
539 case GainAutomationControlPointItem:
540 case PanAutomationControlPointItem:
541 case RedirectAutomationControlPointItem:
542 start_control_point_grab (item, event);
546 case GainAutomationLineItem:
547 case PanAutomationLineItem:
548 case RedirectAutomationLineItem:
549 start_line_grab_from_line (item, event);
554 case AutomationTrackItem:
555 start_rubberband_select (item, event);
559 case ImageFrameHandleStartItem:
560 imageframe_start_handle_op(item, event) ;
563 case ImageFrameHandleEndItem:
564 imageframe_end_handle_op(item, event) ;
567 case MarkerViewHandleStartItem:
568 markerview_item_start_handle_op(item, event) ;
571 case MarkerViewHandleEndItem:
572 markerview_item_end_handle_op(item, event) ;
576 start_markerview_grab(item, event) ;
579 start_imageframe_grab(item, event) ;
597 // start_line_grab_from_regionview (item, event);
600 case GainControlPointItem:
601 start_control_point_grab (item, event);
605 start_line_grab_from_line (item, event);
608 case GainAutomationControlPointItem:
609 case PanAutomationControlPointItem:
610 case RedirectAutomationControlPointItem:
611 start_control_point_grab (item, event);
622 case GainAutomationControlPointItem:
623 case PanAutomationControlPointItem:
624 case RedirectAutomationControlPointItem:
625 start_control_point_grab (item, event);
628 case GainAutomationLineItem:
629 case PanAutomationLineItem:
630 case RedirectAutomationLineItem:
631 start_line_grab_from_line (item, event);
635 // XXX need automation mode to identify which
637 // start_line_grab_from_regionview (item, event);
647 if (event->type == GDK_BUTTON_PRESS) {
648 start_mouse_zoom (item, event);
655 if (item_type == RegionItem) {
656 start_time_fx (item, event);
661 /* handled in release */
670 switch (mouse_mode) {
672 if (event->type == GDK_BUTTON_PRESS) {
675 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
676 start_region_copy_grab (item, event);
678 start_region_grab (item, event);
682 case GainAutomationControlPointItem:
683 case PanAutomationControlPointItem:
684 case RedirectAutomationControlPointItem:
685 start_control_point_grab (item, event);
696 case RegionViewNameHighlight:
697 start_trim (item, event);
702 start_trim (clicked_regionview->get_name_highlight(), event);
713 if (event->type == GDK_BUTTON_PRESS) {
714 /* relax till release */
721 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
722 temporal_zoom_session();
724 temporal_zoom_to_frame (true, event_frame(event));
747 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
749 nframes_t where = event_frame (event, 0, 0);
751 /* no action if we're recording */
753 if (session && session->actively_recording()) {
757 /* first, see if we're finishing a drag ... */
759 if (drag_info.item) {
760 if (end_grab (item, event)) {
761 /* grab dragged, so do nothing else */
766 button_selection (item, event, item_type);
768 /* edit events get handled here */
770 if (drag_info.item == 0 && Keyboard::is_edit_event (&event->button)) {
776 case TempoMarkerItem:
777 edit_tempo_marker (item);
780 case MeterMarkerItem:
781 edit_meter_marker (item);
785 if (clicked_regionview->name_active()) {
786 return mouse_rename_region (item, event);
796 /* context menu events get handled here */
798 if (Keyboard::is_context_menu_event (&event->button)) {
800 if (drag_info.item == 0) {
802 /* no matter which button pops up the context menu, tell the menu
803 widget to use button 1 to drive menu selection.
808 case FadeInHandleItem:
810 case FadeOutHandleItem:
811 popup_fade_context_menu (1, event->button.time, item, item_type);
816 case RegionViewNameHighlight:
819 case AutomationTrackItem:
820 case CrossfadeViewItem:
821 popup_track_context_menu (1, event->button.time, where);
825 case RangeMarkerBarItem:
826 case TransportMarkerBarItem:
829 popup_ruler_menu (pixel_to_frame(event->button.x), item_type);
833 marker_context_menu (&event->button, item);
836 case TempoMarkerItem:
837 tm_marker_context_menu (&event->button, item);
840 case MeterMarkerItem:
841 tm_marker_context_menu (&event->button, item);
846 popup_imageframe_edit_menu(1, event->button.time, item, true) ;
848 case ImageFrameTimeAxisItem:
849 popup_imageframe_edit_menu(1, event->button.time, item, false) ;
852 popup_marker_time_axis_edit_menu(1, event->button.time, item, true) ;
854 case MarkerTimeAxisItem:
855 popup_marker_time_axis_edit_menu(1, event->button.time, item, false) ;
867 /* delete events get handled here */
869 if (drag_info.item == 0 && Keyboard::is_delete_event (&event->button)) {
872 case TempoMarkerItem:
873 remove_tempo_marker (item);
876 case MeterMarkerItem:
877 remove_meter_marker (item);
881 remove_marker (*item, event);
885 if (mouse_mode == MouseObject) {
886 remove_clicked_region ();
890 case GainControlPointItem:
891 if (mouse_mode == MouseGain) {
892 remove_gain_control_point (item, event);
896 case GainAutomationControlPointItem:
897 case PanAutomationControlPointItem:
898 case RedirectAutomationControlPointItem:
899 remove_control_point (item, event);
908 switch (event->button.button) {
912 /* see comments in button_press_handler */
914 case PlayheadCursorItem:
917 case GainAutomationLineItem:
918 case PanAutomationLineItem:
919 case RedirectAutomationLineItem:
920 case StartSelectionTrimItem:
921 case EndSelectionTrimItem:
925 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
926 snap_to (where, 0, true);
928 mouse_add_new_marker (where);
932 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
935 mouse_add_new_tempo_event (where);
939 mouse_add_new_meter_event (pixel_to_frame (event->button.x));
947 switch (mouse_mode) {
950 case AutomationTrackItem:
951 dynamic_cast<AutomationTimeAxisView*>(clicked_axisview)->add_automation_event
965 // Gain only makes sense for audio regions
967 if (!dynamic_cast<AudioRegionView*>(clicked_regionview)) {
973 dynamic_cast<AudioRegionView*>(clicked_regionview)->add_gain_point_event (item, event);
977 case AutomationTrackItem:
978 dynamic_cast<AutomationTimeAxisView*>(clicked_axisview)->
979 add_automation_event (item, event, where, event->button.y);
990 audition_selected_region ();
1007 switch (mouse_mode) {
1011 // x_style_paste (where, 1.0);
1031 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1037 switch (item_type) {
1038 case GainControlPointItem:
1039 if (mouse_mode == MouseGain) {
1040 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1041 cp->set_visible (true);
1045 at_y = cp->get_y ();
1046 cp->item->i2w (at_x, at_y);
1050 fraction = 1.0 - ((cp->get_y() - cp->line.y_position()) / cp->line.height());
1052 set_verbose_canvas_cursor (cp->line.get_verbose_cursor_string (fraction), at_x, at_y);
1053 show_verbose_canvas_cursor ();
1055 if (is_drawable()) {
1056 track_canvas.get_window()->set_cursor (*fader_cursor);
1061 case GainAutomationControlPointItem:
1062 case PanAutomationControlPointItem:
1063 case RedirectAutomationControlPointItem:
1064 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1065 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1066 cp->set_visible (true);
1070 at_y = cp->get_y ();
1071 cp->item->i2w (at_x, at_y);
1075 fraction = 1.0 - (cp->get_y() / cp->line.height());
1077 set_verbose_canvas_cursor (cp->line.get_verbose_cursor_string (fraction), at_x, at_y);
1078 show_verbose_canvas_cursor ();
1080 if (is_drawable()) {
1081 track_canvas.get_window()->set_cursor (*fader_cursor);
1087 if (mouse_mode == MouseGain) {
1088 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1090 line->property_fill_color_rgba() = color_map[cEnteredGainLine];
1091 if (is_drawable()) {
1092 track_canvas.get_window()->set_cursor (*fader_cursor);
1097 case GainAutomationLineItem:
1098 case RedirectAutomationLineItem:
1099 case PanAutomationLineItem:
1100 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1102 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1104 line->property_fill_color_rgba() = color_map[cEnteredAutomationLine];
1106 if (is_drawable()) {
1107 track_canvas.get_window()->set_cursor (*fader_cursor);
1112 case RegionViewNameHighlight:
1113 if (is_drawable() && mouse_mode == MouseObject) {
1114 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1118 case StartSelectionTrimItem:
1119 case EndSelectionTrimItem:
1122 case ImageFrameHandleStartItem:
1123 case ImageFrameHandleEndItem:
1124 case MarkerViewHandleStartItem:
1125 case MarkerViewHandleEndItem:
1128 if (is_drawable()) {
1129 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1133 case EditCursorItem:
1134 case PlayheadCursorItem:
1135 if (is_drawable()) {
1136 track_canvas.get_window()->set_cursor (*grabber_cursor);
1140 case RegionViewName:
1142 /* when the name is not an active item, the entire name highlight is for trimming */
1144 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1145 if (mouse_mode == MouseObject && is_drawable()) {
1146 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1152 case AutomationTrackItem:
1153 if (is_drawable()) {
1154 Gdk::Cursor *cursor;
1155 switch (mouse_mode) {
1157 cursor = selector_cursor;
1160 cursor = zoom_cursor;
1163 cursor = cross_hair_cursor;
1167 track_canvas.get_window()->set_cursor (*cursor);
1169 AutomationTimeAxisView* atv;
1170 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1171 clear_entered_track = false;
1172 set_entered_track (atv);
1178 case RangeMarkerBarItem:
1179 case TransportMarkerBarItem:
1182 if (is_drawable()) {
1183 time_canvas.get_window()->set_cursor (*timebar_cursor);
1188 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1191 marker->set_color_rgba (color_map[cEnteredMarker]);
1193 case MeterMarkerItem:
1194 case TempoMarkerItem:
1195 if (is_drawable()) {
1196 time_canvas.get_window()->set_cursor (*timebar_cursor);
1199 case FadeInHandleItem:
1200 case FadeOutHandleItem:
1201 if (mouse_mode == MouseObject) {
1202 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1204 rect->property_fill_color_rgba() = 0;
1205 rect->property_outline_pixels() = 1;
1214 /* second pass to handle entered track status in a comprehensible way.
1217 switch (item_type) {
1219 case GainAutomationLineItem:
1220 case RedirectAutomationLineItem:
1221 case PanAutomationLineItem:
1222 case GainControlPointItem:
1223 case GainAutomationControlPointItem:
1224 case PanAutomationControlPointItem:
1225 case RedirectAutomationControlPointItem:
1226 /* these do not affect the current entered track state */
1227 clear_entered_track = false;
1230 case AutomationTrackItem:
1231 /* handled above already */
1235 set_entered_track (0);
1243 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1252 switch (item_type) {
1253 case GainControlPointItem:
1254 case GainAutomationControlPointItem:
1255 case PanAutomationControlPointItem:
1256 case RedirectAutomationControlPointItem:
1257 cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
1258 if (cp->line.npoints() > 1) {
1259 if (!cp->selected) {
1260 cp->set_visible (false);
1264 if (is_drawable()) {
1265 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1268 hide_verbose_canvas_cursor ();
1271 case RegionViewNameHighlight:
1272 case StartSelectionTrimItem:
1273 case EndSelectionTrimItem:
1274 case EditCursorItem:
1275 case PlayheadCursorItem:
1278 case ImageFrameHandleStartItem:
1279 case ImageFrameHandleEndItem:
1280 case MarkerViewHandleStartItem:
1281 case MarkerViewHandleEndItem:
1284 if (is_drawable()) {
1285 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1290 case GainAutomationLineItem:
1291 case RedirectAutomationLineItem:
1292 case PanAutomationLineItem:
1293 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1295 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1297 line->property_fill_color_rgba() = al->get_line_color();
1299 if (is_drawable()) {
1300 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1304 case RegionViewName:
1305 /* see enter_handler() for notes */
1306 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1307 if (is_drawable() && mouse_mode == MouseObject) {
1308 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1313 case RangeMarkerBarItem:
1314 case TransportMarkerBarItem:
1318 if (is_drawable()) {
1319 time_canvas.get_window()->set_cursor (*timebar_cursor);
1324 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1327 loc = find_location_from_marker (marker, is_start);
1328 if (loc) location_flags_changed (loc, this);
1330 case MeterMarkerItem:
1331 case TempoMarkerItem:
1333 if (is_drawable()) {
1334 time_canvas.get_window()->set_cursor (*timebar_cursor);
1339 case FadeInHandleItem:
1340 case FadeOutHandleItem:
1341 rv = static_cast<RegionView*>(item->get_data ("regionview"));
1343 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1345 rect->property_fill_color_rgba() = rv->get_fill_color();
1346 rect->property_outline_pixels() = 0;
1351 case AutomationTrackItem:
1352 if (is_drawable()) {
1353 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1354 clear_entered_track = true;
1355 Glib::signal_idle().connect (mem_fun(*this, &Editor::left_automation_track));
1367 Editor::left_automation_track ()
1369 if (clear_entered_track) {
1370 set_entered_track (0);
1371 clear_entered_track = false;
1377 Editor::motion_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type, bool from_autoscroll)
1381 /* We call this so that MOTION_NOTIFY events continue to be
1382 delivered to the canvas. We need to do this because we set
1383 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
1384 the density of the events, at the expense of a round-trip
1385 to the server. Given that this will mostly occur on cases
1386 where DISPLAY = :0.0, and given the cost of what the motion
1387 event might do, its a good tradeoff.
1390 track_canvas.get_pointer (x, y);
1392 if (current_stepping_trackview) {
1393 /* don't keep the persistent stepped trackview if the mouse moves */
1394 current_stepping_trackview = 0;
1395 step_timeout.disconnect ();
1398 if (session && session->actively_recording()) {
1399 /* Sorry. no dragging stuff around while we record */
1403 drag_info.item_type = item_type;
1404 drag_info.current_pointer_frame = event_frame (event, &drag_info.current_pointer_x,
1405 &drag_info.current_pointer_y);
1407 if (!from_autoscroll && drag_info.item) {
1408 /* item != 0 is the best test i can think of for dragging.
1410 if (!drag_info.move_threshold_passed) {
1412 bool x_threshold_passed = (abs ((nframes64_t) (drag_info.current_pointer_x - drag_info.grab_x)) > 4LL);
1413 bool y_threshold_passed = (abs ((nframes64_t) (drag_info.current_pointer_y - drag_info.grab_y)) > 4LL);
1415 drag_info.move_threshold_passed = (x_threshold_passed || y_threshold_passed);
1417 // and change the initial grab loc/frame if this drag info wants us to
1419 if (drag_info.want_move_threshold && drag_info.move_threshold_passed) {
1420 drag_info.grab_frame = drag_info.current_pointer_frame;
1421 drag_info.grab_x = drag_info.current_pointer_x;
1422 drag_info.grab_y = drag_info.current_pointer_y;
1423 drag_info.last_pointer_frame = drag_info.grab_frame;
1424 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
1429 switch (item_type) {
1430 case PlayheadCursorItem:
1431 case EditCursorItem:
1433 case GainControlPointItem:
1434 case RedirectAutomationControlPointItem:
1435 case GainAutomationControlPointItem:
1436 case PanAutomationControlPointItem:
1437 case TempoMarkerItem:
1438 case MeterMarkerItem:
1439 case RegionViewNameHighlight:
1440 case StartSelectionTrimItem:
1441 case EndSelectionTrimItem:
1444 case RedirectAutomationLineItem:
1445 case GainAutomationLineItem:
1446 case PanAutomationLineItem:
1447 case FadeInHandleItem:
1448 case FadeOutHandleItem:
1451 case ImageFrameHandleStartItem:
1452 case ImageFrameHandleEndItem:
1453 case MarkerViewHandleStartItem:
1454 case MarkerViewHandleEndItem:
1457 if (drag_info.item && (event->motion.state & Gdk::BUTTON1_MASK ||
1458 (event->motion.state & Gdk::BUTTON2_MASK))) {
1459 if (!from_autoscroll) {
1460 maybe_autoscroll (event);
1462 (this->*(drag_info.motion_callback)) (item, event);
1471 switch (mouse_mode) {
1476 if (drag_info.item && (event->motion.state & GDK_BUTTON1_MASK ||
1477 (event->motion.state & GDK_BUTTON2_MASK))) {
1478 if (!from_autoscroll) {
1479 maybe_autoscroll (event);
1481 (this->*(drag_info.motion_callback)) (item, event);
1492 track_canvas_motion (event);
1493 // drag_info.last_pointer_frame = drag_info.current_pointer_frame;
1501 Editor::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
1503 if (drag_info.item == 0) {
1504 fatal << _("programming error: start_grab called without drag item") << endmsg;
1510 cursor = grabber_cursor;
1513 // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
1515 if (event->button.button == 2) {
1516 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Alt)) {
1517 drag_info.y_constrained = true;
1518 drag_info.x_constrained = false;
1520 drag_info.y_constrained = false;
1521 drag_info.x_constrained = true;
1524 drag_info.x_constrained = false;
1525 drag_info.y_constrained = false;
1528 drag_info.grab_frame = event_frame (event, &drag_info.grab_x, &drag_info.grab_y);
1529 drag_info.last_pointer_frame = drag_info.grab_frame;
1530 drag_info.current_pointer_frame = drag_info.grab_frame;
1531 drag_info.current_pointer_x = drag_info.grab_x;
1532 drag_info.current_pointer_y = drag_info.grab_y;
1533 drag_info.cumulative_x_drag = 0;
1534 drag_info.cumulative_y_drag = 0;
1535 drag_info.first_move = true;
1536 drag_info.move_threshold_passed = false;
1537 drag_info.want_move_threshold = false;
1538 drag_info.pointer_frame_offset = 0;
1539 drag_info.brushing = false;
1540 drag_info.copied_location = 0;
1542 drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK,
1544 event->button.time);
1546 if (session && session->transport_rolling()) {
1547 drag_info.was_rolling = true;
1549 drag_info.was_rolling = false;
1552 switch (snap_type) {
1553 case SnapToRegionStart:
1554 case SnapToRegionEnd:
1555 case SnapToRegionSync:
1556 case SnapToRegionBoundary:
1557 build_region_boundary_cache ();
1565 Editor::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t time)
1567 drag_info.item->ungrab (0);
1568 drag_info.item = new_item;
1571 cursor = grabber_cursor;
1574 drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK, *cursor, time);
1578 Editor::end_grab (ArdourCanvas::Item* item, GdkEvent* event)
1580 bool did_drag = false;
1582 stop_canvas_autoscroll ();
1584 if (drag_info.item == 0) {
1588 drag_info.item->ungrab (event->button.time);
1590 if (drag_info.finished_callback) {
1591 (this->*(drag_info.finished_callback)) (item, event);
1594 did_drag = !drag_info.first_move;
1596 hide_verbose_canvas_cursor();
1599 drag_info.copy = false;
1600 drag_info.motion_callback = 0;
1601 drag_info.finished_callback = 0;
1602 drag_info.last_trackview = 0;
1603 drag_info.last_frame_position = 0;
1604 drag_info.grab_frame = 0;
1605 drag_info.last_pointer_frame = 0;
1606 drag_info.current_pointer_frame = 0;
1607 drag_info.brushing = false;
1609 if (drag_info.copied_location) {
1610 delete drag_info.copied_location;
1611 drag_info.copied_location = 0;
1618 Editor::set_edit_cursor (GdkEvent* event)
1620 nframes_t pointer_frame = event_frame (event);
1622 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1623 if (snap_type != SnapToEditCursor) {
1624 snap_to (pointer_frame);
1628 edit_cursor->set_position (pointer_frame);
1629 edit_cursor_clock.set (pointer_frame);
1633 Editor::set_playhead_cursor (GdkEvent* event)
1635 nframes_t pointer_frame = event_frame (event);
1637 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1638 snap_to (pointer_frame);
1642 session->request_locate (pointer_frame, session->transport_rolling());
1647 Editor::start_fade_in_grab (ArdourCanvas::Item* item, GdkEvent* event)
1649 drag_info.item = item;
1650 drag_info.motion_callback = &Editor::fade_in_drag_motion_callback;
1651 drag_info.finished_callback = &Editor::fade_in_drag_finished_callback;
1655 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
1656 fatal << _("programming error: fade in canvas item has no regionview data pointer!") << endmsg;
1660 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1662 drag_info.pointer_frame_offset = drag_info.grab_frame - ((nframes_t) arv->audio_region()->fade_in().back()->when + arv->region()->position());
1666 Editor::fade_in_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1668 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1670 nframes_t fade_length;
1672 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1673 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1679 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1683 if (pos < (arv->region()->position() + 64)) {
1684 fade_length = 64; // this should be a minimum defined somewhere
1685 } else if (pos > arv->region()->last_frame()) {
1686 fade_length = arv->region()->length();
1688 fade_length = pos - arv->region()->position();
1690 /* mapover the region selection */
1692 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1694 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1700 tmp->reset_fade_in_shape_width (fade_length);
1703 show_verbose_duration_cursor (arv->region()->position(), arv->region()->position() + fade_length, 10);
1705 drag_info.first_move = false;
1709 Editor::fade_in_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1711 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1713 nframes_t fade_length;
1715 if (drag_info.first_move) return;
1717 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1718 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1723 if (pos < (arv->region()->position() + 64)) {
1724 fade_length = 64; // this should be a minimum defined somewhere
1725 } else if (pos > arv->region()->last_frame()) {
1726 fade_length = arv->region()->length();
1728 fade_length = pos - arv->region()->position();
1731 begin_reversible_command (_("change fade in length"));
1733 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1735 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1741 AutomationList& alist = tmp->audio_region()->fade_in();
1742 XMLNode &before = alist.get_state();
1744 tmp->audio_region()->set_fade_in_length (fade_length);
1746 XMLNode &after = alist.get_state();
1747 session->add_command(new MementoCommand<AutomationList>(alist, &before, &after));
1750 commit_reversible_command ();
1754 Editor::start_fade_out_grab (ArdourCanvas::Item* item, GdkEvent* event)
1756 drag_info.item = item;
1757 drag_info.motion_callback = &Editor::fade_out_drag_motion_callback;
1758 drag_info.finished_callback = &Editor::fade_out_drag_finished_callback;
1762 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
1763 fatal << _("programming error: fade out canvas item has no regionview data pointer!") << endmsg;
1767 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1769 drag_info.pointer_frame_offset = drag_info.grab_frame - (arv->region()->length() - (nframes_t) arv->audio_region()->fade_out().back()->when + arv->region()->position());
1773 Editor::fade_out_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1775 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1777 nframes_t fade_length;
1779 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1780 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1785 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1789 if (pos > (arv->region()->last_frame() - 64)) {
1790 fade_length = 64; // this should really be a minimum fade defined somewhere
1792 else if (pos < arv->region()->position()) {
1793 fade_length = arv->region()->length();
1796 fade_length = arv->region()->last_frame() - pos;
1799 /* mapover the region selection */
1801 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1803 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1809 tmp->reset_fade_out_shape_width (fade_length);
1812 show_verbose_duration_cursor (arv->region()->last_frame() - fade_length, arv->region()->last_frame(), 10);
1814 drag_info.first_move = false;
1818 Editor::fade_out_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1820 if (drag_info.first_move) return;
1822 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1824 nframes_t fade_length;
1826 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1827 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1833 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1837 if (pos > (arv->region()->last_frame() - 64)) {
1838 fade_length = 64; // this should really be a minimum fade defined somewhere
1840 else if (pos < arv->region()->position()) {
1841 fade_length = arv->region()->length();
1844 fade_length = arv->region()->last_frame() - pos;
1847 begin_reversible_command (_("change fade out length"));
1849 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1851 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1857 AutomationList& alist = tmp->audio_region()->fade_out();
1858 XMLNode &before = alist.get_state();
1860 tmp->audio_region()->set_fade_out_length (fade_length);
1862 XMLNode &after = alist.get_state();
1863 session->add_command(new MementoCommand<AutomationList>(alist, &before, &after));
1866 commit_reversible_command ();
1870 Editor::start_cursor_grab (ArdourCanvas::Item* item, GdkEvent* event)
1872 drag_info.item = item;
1873 drag_info.motion_callback = &Editor::cursor_drag_motion_callback;
1874 drag_info.finished_callback = &Editor::cursor_drag_finished_callback;
1878 if ((drag_info.data = (item->get_data ("cursor"))) == 0) {
1879 fatal << _("programming error: cursor canvas item has no cursor data pointer!") << endmsg;
1883 Cursor* cursor = (Cursor *) drag_info.data;
1885 if (cursor == playhead_cursor) {
1886 _dragging_playhead = true;
1888 if (session && drag_info.was_rolling) {
1889 session->request_stop ();
1892 if (session && session->is_auditioning()) {
1893 session->cancel_audition ();
1897 drag_info.pointer_frame_offset = drag_info.grab_frame - cursor->current_frame;
1899 show_verbose_time_cursor (cursor->current_frame, 10);
1903 Editor::cursor_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1905 Cursor* cursor = (Cursor *) drag_info.data;
1906 nframes_t adjusted_frame;
1908 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1909 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1915 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1916 if (cursor != edit_cursor || snap_type != SnapToEditCursor) {
1917 snap_to (adjusted_frame);
1921 if (adjusted_frame == drag_info.last_pointer_frame) return;
1923 cursor->set_position (adjusted_frame);
1925 if (cursor == edit_cursor) {
1926 edit_cursor_clock.set (cursor->current_frame);
1928 UpdateAllTransportClocks (cursor->current_frame);
1931 show_verbose_time_cursor (cursor->current_frame, 10);
1933 drag_info.last_pointer_frame = adjusted_frame;
1934 drag_info.first_move = false;
1938 Editor::cursor_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1940 if (drag_info.first_move) return;
1942 cursor_drag_motion_callback (item, event);
1944 _dragging_playhead = false;
1946 if (item == &playhead_cursor->canvas_item) {
1948 session->request_locate (playhead_cursor->current_frame, drag_info.was_rolling);
1950 } else if (item == &edit_cursor->canvas_item) {
1951 edit_cursor->set_position (edit_cursor->current_frame);
1952 edit_cursor_clock.set (edit_cursor->current_frame);
1957 Editor::update_marker_drag_item (Location *location)
1959 double x1 = frame_to_pixel (location->start());
1960 double x2 = frame_to_pixel (location->end());
1962 if (location->is_mark()) {
1963 marker_drag_line_points.front().set_x(x1);
1964 marker_drag_line_points.back().set_x(x1);
1965 marker_drag_line->property_points() = marker_drag_line_points;
1968 range_marker_drag_rect->property_x1() = x1;
1969 range_marker_drag_rect->property_x2() = x2;
1974 Editor::start_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
1978 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1979 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
1985 Location *location = find_location_from_marker (marker, is_start);
1987 drag_info.item = item;
1988 drag_info.data = marker;
1989 drag_info.motion_callback = &Editor::marker_drag_motion_callback;
1990 drag_info.finished_callback = &Editor::marker_drag_finished_callback;
1994 drag_info.copied_location = new Location (*location);
1995 drag_info.pointer_frame_offset = drag_info.grab_frame - (is_start ? location->start() : location->end());
1997 update_marker_drag_item (location);
1999 if (location->is_mark()) {
2000 marker_drag_line->show();
2001 marker_drag_line->raise_to_top();
2004 range_marker_drag_rect->show();
2005 range_marker_drag_rect->raise_to_top();
2008 if (is_start) show_verbose_time_cursor (location->start(), 10);
2009 else show_verbose_time_cursor (location->end(), 10);
2013 Editor::marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2016 Marker* marker = (Marker *) drag_info.data;
2017 Location *real_location;
2018 Location *copy_location;
2020 bool move_both = false;
2024 if (drag_info.pointer_frame_offset <= drag_info.current_pointer_frame) {
2025 newframe = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2030 nframes_t next = newframe;
2032 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2033 snap_to (newframe, 0, true);
2036 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
2040 /* call this to find out if its the start or end */
2042 real_location = find_location_from_marker (marker, is_start);
2044 /* use the copy that we're "dragging" around */
2046 copy_location = drag_info.copied_location;
2048 f_delta = copy_location->end() - copy_location->start();
2050 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
2054 if (copy_location->is_mark()) {
2057 copy_location->set_start (newframe);
2061 if (is_start) { // start-of-range marker
2064 copy_location->set_start (newframe);
2065 copy_location->set_end (newframe + f_delta);
2066 } else if (newframe < copy_location->end()) {
2067 copy_location->set_start (newframe);
2069 snap_to (next, 1, true);
2070 copy_location->set_end (next);
2071 copy_location->set_start (newframe);
2074 } else { // end marker
2077 copy_location->set_end (newframe);
2078 copy_location->set_start (newframe - f_delta);
2079 } else if (newframe > copy_location->start()) {
2080 copy_location->set_end (newframe);
2082 } else if (newframe > 0) {
2083 snap_to (next, -1, true);
2084 copy_location->set_start (next);
2085 copy_location->set_end (newframe);
2090 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
2091 drag_info.first_move = false;
2093 update_marker_drag_item (copy_location);
2095 LocationMarkers* lm = find_location_markers (real_location);
2096 lm->set_position (copy_location->start(), copy_location->end());
2098 show_verbose_time_cursor (newframe, 10);
2102 Editor::marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2104 if (drag_info.first_move) {
2105 marker_drag_motion_callback (item, event);
2109 Marker* marker = (Marker *) drag_info.data;
2113 begin_reversible_command ( _("move marker") );
2114 XMLNode &before = session->locations()->get_state();
2116 Location * location = find_location_from_marker (marker, is_start);
2119 if (location->is_mark()) {
2120 location->set_start (drag_info.copied_location->start());
2122 location->set (drag_info.copied_location->start(), drag_info.copied_location->end());
2126 XMLNode &after = session->locations()->get_state();
2127 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
2128 commit_reversible_command ();
2130 marker_drag_line->hide();
2131 range_marker_drag_rect->hide();
2135 Editor::start_meter_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2138 MeterMarker* meter_marker;
2140 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2141 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2145 meter_marker = dynamic_cast<MeterMarker*> (marker);
2147 MetricSection& section (meter_marker->meter());
2149 if (!section.movable()) {
2153 drag_info.item = item;
2154 drag_info.data = marker;
2155 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2156 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2160 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2162 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2166 Editor::start_meter_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2169 MeterMarker* meter_marker;
2171 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2172 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2176 meter_marker = dynamic_cast<MeterMarker*> (marker);
2178 // create a dummy marker for visual representation of moving the copy.
2179 // The actual copying is not done before we reach the finish callback.
2181 snprintf (name, sizeof(name), "%g/%g", meter_marker->meter().beats_per_bar(), meter_marker->meter().note_divisor ());
2182 MeterMarker* new_marker = new MeterMarker(*this, *meter_group, color_map[cMeterMarker], name,
2183 *new MeterSection(meter_marker->meter()));
2185 drag_info.item = &new_marker->the_item();
2186 drag_info.copy = true;
2187 drag_info.data = new_marker;
2188 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2189 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2193 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2195 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2199 Editor::meter_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2201 MeterMarker* marker = (MeterMarker *) drag_info.data;
2202 nframes_t adjusted_frame;
2204 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2205 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2211 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2212 snap_to (adjusted_frame);
2215 if (adjusted_frame == drag_info.last_pointer_frame) return;
2217 marker->set_position (adjusted_frame);
2220 drag_info.last_pointer_frame = adjusted_frame;
2221 drag_info.first_move = false;
2223 show_verbose_time_cursor (adjusted_frame, 10);
2227 Editor::meter_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2229 if (drag_info.first_move) return;
2231 meter_marker_drag_motion_callback (drag_info.item, event);
2233 MeterMarker* marker = (MeterMarker *) drag_info.data;
2236 TempoMap& map (session->tempo_map());
2237 map.bbt_time (drag_info.last_pointer_frame, when);
2239 if (drag_info.copy == true) {
2240 begin_reversible_command (_("copy meter mark"));
2241 XMLNode &before = map.get_state();
2242 map.add_meter (marker->meter(), when);
2243 XMLNode &after = map.get_state();
2244 session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2245 commit_reversible_command ();
2247 // delete the dummy marker we used for visual representation of copying.
2248 // a new visual marker will show up automatically.
2251 begin_reversible_command (_("move meter mark"));
2252 XMLNode &before = map.get_state();
2253 map.move_meter (marker->meter(), when);
2254 XMLNode &after = map.get_state();
2255 session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2256 commit_reversible_command ();
2261 Editor::start_tempo_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2264 TempoMarker* tempo_marker;
2266 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2267 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2271 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2272 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2276 MetricSection& section (tempo_marker->tempo());
2278 if (!section.movable()) {
2282 drag_info.item = item;
2283 drag_info.data = marker;
2284 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2285 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2289 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2290 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2294 Editor::start_tempo_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2297 TempoMarker* tempo_marker;
2299 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2300 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2304 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2305 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2309 // create a dummy marker for visual representation of moving the copy.
2310 // The actual copying is not done before we reach the finish callback.
2312 snprintf (name, sizeof (name), "%.2f", tempo_marker->tempo().beats_per_minute());
2313 TempoMarker* new_marker = new TempoMarker(*this, *tempo_group, color_map[cTempoMarker], name,
2314 *new TempoSection(tempo_marker->tempo()));
2316 drag_info.item = &new_marker->the_item();
2317 drag_info.copy = true;
2318 drag_info.data = new_marker;
2319 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2320 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2324 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2326 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2330 Editor::tempo_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2332 TempoMarker* marker = (TempoMarker *) drag_info.data;
2333 nframes_t adjusted_frame;
2335 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2336 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2342 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2343 snap_to (adjusted_frame);
2346 if (adjusted_frame == drag_info.last_pointer_frame) return;
2348 /* OK, we've moved far enough to make it worth actually move the thing. */
2350 marker->set_position (adjusted_frame);
2352 show_verbose_time_cursor (adjusted_frame, 10);
2354 drag_info.last_pointer_frame = adjusted_frame;
2355 drag_info.first_move = false;
2359 Editor::tempo_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2361 if (drag_info.first_move) return;
2363 tempo_marker_drag_motion_callback (drag_info.item, event);
2365 TempoMarker* marker = (TempoMarker *) drag_info.data;
2368 TempoMap& map (session->tempo_map());
2369 map.bbt_time (drag_info.last_pointer_frame, when);
2371 if (drag_info.copy == true) {
2372 begin_reversible_command (_("copy tempo mark"));
2373 XMLNode &before = map.get_state();
2374 map.add_tempo (marker->tempo(), when);
2375 XMLNode &after = map.get_state();
2376 session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2377 commit_reversible_command ();
2379 // delete the dummy marker we used for visual representation of copying.
2380 // a new visual marker will show up automatically.
2383 begin_reversible_command (_("move tempo mark"));
2384 XMLNode &before = map.get_state();
2385 map.move_tempo (marker->tempo(), when);
2386 XMLNode &after = map.get_state();
2387 session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2388 commit_reversible_command ();
2393 Editor::remove_gain_control_point (ArdourCanvas::Item*item, GdkEvent* event)
2395 ControlPoint* control_point;
2397 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2398 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2402 // We shouldn't remove the first or last gain point
2403 if (control_point->line.is_last_point(*control_point) ||
2404 control_point->line.is_first_point(*control_point)) {
2408 control_point->line.remove_point (*control_point);
2412 Editor::remove_control_point (ArdourCanvas::Item*item, GdkEvent* event)
2414 ControlPoint* control_point;
2416 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2417 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2421 control_point->line.remove_point (*control_point);
2425 Editor::start_control_point_grab (ArdourCanvas::Item* item, GdkEvent* event)
2427 ControlPoint* control_point;
2429 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2430 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2434 drag_info.item = item;
2435 drag_info.data = control_point;
2436 drag_info.motion_callback = &Editor::control_point_drag_motion_callback;
2437 drag_info.finished_callback = &Editor::control_point_drag_finished_callback;
2439 start_grab (event, fader_cursor);
2441 control_point->line.start_drag (control_point, drag_info.grab_frame, 0);
2443 float fraction = 1.0 - ((control_point->get_y() - control_point->line.y_position()) / control_point->line.height());
2444 set_verbose_canvas_cursor (control_point->line.get_verbose_cursor_string (fraction),
2445 drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
2447 show_verbose_canvas_cursor ();
2451 Editor::control_point_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2453 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2455 double cx = drag_info.current_pointer_x;
2456 double cy = drag_info.current_pointer_y;
2458 drag_info.cumulative_x_drag = cx - drag_info.grab_x ;
2459 drag_info.cumulative_y_drag = cy - drag_info.grab_y ;
2461 if (drag_info.x_constrained) {
2462 cx = drag_info.grab_x;
2464 if (drag_info.y_constrained) {
2465 cy = drag_info.grab_y;
2468 cp->line.parent_group().w2i (cx, cy);
2472 cy = min ((double) (cp->line.y_position() + cp->line.height()), cy);
2474 //translate cx to frames
2475 nframes_t cx_frames = unit_to_frame (cx);
2477 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier()) && !drag_info.x_constrained) {
2478 snap_to (cx_frames);
2481 float const fraction = 1.0 - ((cy - cp->line.y_position()) / cp->line.height());
2485 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
2491 cp->line.point_drag (*cp, cx_frames , fraction, push);
2493 set_verbose_canvas_cursor_text (cp->line.get_verbose_cursor_string (fraction));
2495 drag_info.first_move = false;
2499 Editor::control_point_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2501 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2503 if (drag_info.first_move) {
2507 if ((event->type == GDK_BUTTON_RELEASE) && (event->button.button == 1) && Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
2508 reset_point_selection ();
2512 control_point_drag_motion_callback (item, event);
2514 cp->line.end_drag (cp);
2518 Editor::start_line_grab_from_regionview (ArdourCanvas::Item* item, GdkEvent* event)
2520 switch (mouse_mode) {
2522 assert(dynamic_cast<AudioRegionView*>(clicked_regionview));
2523 start_line_grab (dynamic_cast<AudioRegionView*>(clicked_regionview)->get_gain_line(), event);
2531 Editor::start_line_grab_from_line (ArdourCanvas::Item* item, GdkEvent* event)
2535 if ((al = reinterpret_cast<AutomationLine*> (item->get_data ("line"))) == 0) {
2536 fatal << _("programming error: line canvas item has no line pointer!") << endmsg;
2540 start_line_grab (al, event);
2544 Editor::start_line_grab (AutomationLine* line, GdkEvent* event)
2548 nframes_t frame_within_region;
2550 /* need to get x coordinate in terms of parent (TimeAxisItemView)
2554 cx = event->button.x;
2555 cy = event->button.y;
2556 line->parent_group().w2i (cx, cy);
2557 frame_within_region = (nframes_t) floor (cx * frames_per_unit);
2559 if (!line->control_points_adjacent (frame_within_region, current_line_drag_info.before,
2560 current_line_drag_info.after)) {
2561 /* no adjacent points */
2565 drag_info.item = &line->grab_item();
2566 drag_info.data = line;
2567 drag_info.motion_callback = &Editor::line_drag_motion_callback;
2568 drag_info.finished_callback = &Editor::line_drag_finished_callback;
2570 start_grab (event, fader_cursor);
2572 double const fraction = 1.0 - ((cy - line->y_position()) / line->height());
2574 line->start_drag (0, drag_info.grab_frame, fraction);
2576 set_verbose_canvas_cursor (line->get_verbose_cursor_string (fraction),
2577 drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
2578 show_verbose_canvas_cursor ();
2582 Editor::line_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2584 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
2585 double cx = drag_info.current_pointer_x;
2586 double cy = drag_info.current_pointer_y;
2588 line->parent_group().w2i (cx, cy);
2590 double const fraction = 1.0 - ((cy - line->y_position()) / line->height());
2594 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
2600 line->line_drag (current_line_drag_info.before, current_line_drag_info.after, fraction, push);
2602 set_verbose_canvas_cursor_text (line->get_verbose_cursor_string (fraction));
2606 Editor::line_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2608 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
2609 line_drag_motion_callback (item, event);
2614 Editor::start_region_grab (ArdourCanvas::Item* item, GdkEvent* event)
2616 if (selection->regions.empty() || clicked_regionview == 0) {
2620 drag_info.copy = false;
2621 drag_info.item = item;
2622 drag_info.data = clicked_regionview;
2623 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2624 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2629 TimeAxisView* tvp = clicked_axisview;
2630 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2632 if (tv && tv->is_track()) {
2633 speed = tv->get_diskstream()->speed();
2636 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2637 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2638 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2639 // we want a move threshold
2640 drag_info.want_move_threshold = true;
2642 show_verbose_time_cursor (drag_info.last_frame_position, 10);
2644 begin_reversible_command (_("move region(s)"));
2648 Editor::start_region_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2650 if (selection->regions.empty() || clicked_regionview == 0) {
2654 drag_info.copy = true;
2655 drag_info.item = item;
2656 drag_info.data = clicked_regionview;
2660 TimeAxisView* tv = &clicked_regionview->get_time_axis_view();
2661 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
2664 if (rtv && rtv->is_track()) {
2665 speed = rtv->get_diskstream()->speed();
2668 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2669 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2670 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2671 // we want a move threshold
2672 drag_info.want_move_threshold = true;
2673 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2674 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2675 show_verbose_time_cursor (drag_info.last_frame_position, 10);
2679 Editor::start_region_brush_grab (ArdourCanvas::Item* item, GdkEvent* event)
2681 if (selection->regions.empty() || clicked_regionview == 0) {
2685 drag_info.copy = false;
2686 drag_info.item = item;
2687 drag_info.data = clicked_regionview;
2688 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2689 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2694 TimeAxisView* tvp = clicked_axisview;
2695 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2697 if (tv && tv->is_track()) {
2698 speed = tv->get_diskstream()->speed();
2701 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2702 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2703 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2704 // we want a move threshold
2705 drag_info.want_move_threshold = true;
2706 drag_info.brushing = true;
2708 begin_reversible_command (_("Drag region brush"));
2712 Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2716 RegionView* rv = reinterpret_cast<RegionView*> (drag_info.data);
2717 nframes_t pending_region_position = 0;
2718 int32_t pointer_y_span = 0, canvas_pointer_y_span = 0, original_pointer_order;
2719 int32_t visible_y_high = 0, visible_y_low = 512; //high meaning higher numbered.. not the height on the screen
2720 bool clamp_y_axis = false;
2721 vector<int32_t> height_list(512) ;
2722 vector<int32_t>::iterator j;
2724 if (drag_info.copy && drag_info.move_threshold_passed && drag_info.want_move_threshold) {
2726 drag_info.want_move_threshold = false; // don't copy again
2728 /* duplicate the region(s) */
2730 vector<RegionView*> new_regionviews;
2732 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
2738 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
2739 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
2742 nrv = new AudioRegionView (*arv);
2744 nrv = new MidiRegionView (*mrv);
2749 nrv->get_canvas_group()->show ();
2751 new_regionviews.push_back (nrv);
2754 if (new_regionviews.empty()) {
2758 /* reset selection to new regionviews */
2760 selection->set (new_regionviews);
2762 /* reset drag_info data to reflect the fact that we are dragging the copies */
2764 drag_info.data = new_regionviews.front();
2766 swap_grab (new_regionviews.front()->get_canvas_group (), 0, event->motion.time);
2769 /* Which trackview is this ? */
2771 TimeAxisView* tvp = trackview_by_y_position (drag_info.current_pointer_y);
2772 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2774 /* The region motion is only processed if the pointer is over
2778 if (!tv || !tv->is_track()) {
2779 /* To make sure we hide the verbose canvas cursor when the mouse is
2780 not held over a track.
2782 hide_verbose_canvas_cursor ();
2786 original_pointer_order = drag_info.last_trackview->order;
2788 /************************************************************
2790 ************************************************************/
2792 if (drag_info.brushing) {
2793 clamp_y_axis = true;
2798 if ((pointer_y_span = (drag_info.last_trackview->order - tv->order)) != 0) {
2800 int32_t children = 0, numtracks = 0;
2801 // XXX hard coding track limit, oh my, so very very bad
2802 bitset <1024> tracks (0x00);
2803 /* get a bitmask representing the visible tracks */
2805 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2806 TimeAxisView *tracklist_timeview;
2807 tracklist_timeview = (*i);
2808 RouteTimeAxisView* rtv2 = dynamic_cast<RouteTimeAxisView*>(tracklist_timeview);
2809 list<TimeAxisView*> children_list;
2811 /* zeroes are audio tracks. ones are other types. */
2813 if (!rtv2->hidden()) {
2815 if (visible_y_high < rtv2->order) {
2816 visible_y_high = rtv2->order;
2818 if (visible_y_low > rtv2->order) {
2819 visible_y_low = rtv2->order;
2822 if (!rtv2->is_track()) {
2823 tracks = tracks |= (0x01 << rtv2->order);
2826 height_list[rtv2->order] = (*i)->height;
2828 if ((children_list = rtv2->get_child_list()).size() > 0) {
2829 for (list<TimeAxisView*>::iterator j = children_list.begin(); j != children_list.end(); ++j) {
2830 tracks = tracks |= (0x01 << (rtv2->order + children));
2831 height_list[rtv2->order + children] = (*j)->height;
2839 /* find the actual span according to the canvas */
2841 canvas_pointer_y_span = pointer_y_span;
2842 if (drag_info.last_trackview->order >= tv->order) {
2844 for (y = tv->order; y < drag_info.last_trackview->order; y++) {
2845 if (height_list[y] == 0 ) {
2846 canvas_pointer_y_span--;
2851 for (y = drag_info.last_trackview->order;y <= tv->order; y++) {
2852 if ( height_list[y] == 0 ) {
2853 canvas_pointer_y_span++;
2858 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
2859 RegionView* rv2 = (*i);
2860 double ix1, ix2, iy1, iy2;
2863 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
2864 rv2->get_canvas_group()->i2w (ix1, iy1);
2865 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
2866 RouteTimeAxisView* rtv2 = dynamic_cast<RouteTimeAxisView*>(tvp2);
2868 if (rtv2->order != original_pointer_order) {
2869 /* this isn't the pointer track */
2871 if (canvas_pointer_y_span > 0) {
2873 /* moving up the canvas */
2874 if ((rtv2->order - canvas_pointer_y_span) >= visible_y_low) {
2876 int32_t visible_tracks = 0;
2877 while (visible_tracks < canvas_pointer_y_span ) {
2880 while (height_list[rtv2->order - (visible_tracks - n)] == 0) {
2881 /* we're passing through a hidden track */
2886 if (tracks[rtv2->order - (canvas_pointer_y_span - n)] != 0x00) {
2887 clamp_y_axis = true;
2891 clamp_y_axis = true;
2894 } else if (canvas_pointer_y_span < 0) {
2896 /*moving down the canvas*/
2898 if ((rtv2->order - (canvas_pointer_y_span - n)) <= visible_y_high) { // we will overflow
2901 int32_t visible_tracks = 0;
2903 while (visible_tracks > canvas_pointer_y_span ) {
2906 while (height_list[rtv2->order - (visible_tracks - n)] == 0) {
2910 if ( tracks[rtv2->order - ( canvas_pointer_y_span - n)] != 0x00) {
2911 clamp_y_axis = true;
2916 clamp_y_axis = true;
2922 /* this is the pointer's track */
2923 if ((rtv2->order - pointer_y_span) > visible_y_high) { // we will overflow
2924 clamp_y_axis = true;
2925 } else if ((rtv2->order - pointer_y_span) < visible_y_low) { // we will underflow
2926 clamp_y_axis = true;
2934 } else if (drag_info.last_trackview == tv) {
2935 clamp_y_axis = true;
2939 if (!clamp_y_axis) {
2940 drag_info.last_trackview = tv;
2943 /************************************************************
2945 ************************************************************/
2947 /* compute the amount of pointer motion in frames, and where
2948 the region would be if we moved it by that much.
2951 if (drag_info.move_threshold_passed) {
2953 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2955 nframes_t sync_frame;
2956 nframes_t sync_offset;
2959 pending_region_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2961 sync_offset = rv->region()->sync_offset (sync_dir);
2962 sync_frame = rv->region()->adjust_to_sync (pending_region_position);
2964 /* we snap if the snap modifier is not enabled.
2967 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2968 snap_to (sync_frame);
2971 if (sync_frame - sync_offset <= sync_frame) {
2972 pending_region_position = sync_frame - (sync_dir*sync_offset);
2974 pending_region_position = 0;
2978 pending_region_position = 0;
2981 if (pending_region_position > max_frames - rv->region()->length()) {
2982 pending_region_position = drag_info.last_frame_position;
2985 // printf ("3: pending_region_position= %lu %lu\n", pending_region_position, drag_info.last_frame_position );
2987 if (pending_region_position != drag_info.last_frame_position && !drag_info.x_constrained) {
2989 /* now compute the canvas unit distance we need to move the regiondrag_info.last_trackview->order
2990 to make it appear at the new location.
2993 if (pending_region_position > drag_info.last_frame_position) {
2994 x_delta = ((double) (pending_region_position - drag_info.last_frame_position) / frames_per_unit);
2996 x_delta = -((double) (drag_info.last_frame_position - pending_region_position) / frames_per_unit);
2999 drag_info.last_frame_position = pending_region_position;
3006 /* threshold not passed */
3011 /*************************************************************
3013 ************************************************************/
3015 if (x_delta == 0 && (pointer_y_span == 0)) {
3016 /* haven't reached next snap point, and we're not switching
3017 trackviews. nothing to do.
3024 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3026 RegionView* rv2 = (*i);
3028 // If any regionview is at zero, we need to know so we can stop further leftward motion.
3030 double ix1, ix2, iy1, iy2;
3031 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3032 rv2->get_canvas_group()->i2w (ix1, iy1);
3041 /*************************************************************
3043 ************************************************************/
3047 if (drag_info.first_move) {
3048 if (drag_info.move_threshold_passed) {
3059 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
3060 const list<RegionView*>& layered_regions = selection->regions.by_layer();
3062 for (list<RegionView*>::const_iterator i = layered_regions.begin(); i != layered_regions.end(); ++i) {
3064 RegionView* rv = (*i);
3065 double ix1, ix2, iy1, iy2;
3066 int32_t temp_pointer_y_span = pointer_y_span;
3068 /* get item BBox, which will be relative to parent. so we have
3069 to query on a child, then convert to world coordinates using
3073 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3074 rv->get_canvas_group()->i2w (ix1, iy1);
3075 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3076 RouteTimeAxisView* canvas_rtv = dynamic_cast<RouteTimeAxisView*>(tvp2);
3077 RouteTimeAxisView* temp_rtv;
3079 if ((pointer_y_span != 0) && !clamp_y_axis) {
3082 for (j = height_list.begin(); j!= height_list.end(); j++) {
3083 if (x == canvas_rtv->order) {
3084 /* we found the track the region is on */
3085 if (x != original_pointer_order) {
3086 /*this isn't from the same track we're dragging from */
3087 temp_pointer_y_span = canvas_pointer_y_span;
3089 while (temp_pointer_y_span > 0) {
3090 /* we're moving up canvas-wise,
3091 so we need to find the next track height
3093 if (j != height_list.begin()) {
3096 if (x != original_pointer_order) {
3097 /* we're not from the dragged track, so ignore hidden tracks. */
3099 temp_pointer_y_span++;
3103 temp_pointer_y_span--;
3105 while (temp_pointer_y_span < 0) {
3107 if (x != original_pointer_order) {
3109 temp_pointer_y_span--;
3113 if (j != height_list.end()) {
3116 temp_pointer_y_span++;
3118 /* find out where we'll be when we move and set height accordingly */
3120 tvp2 = trackview_by_y_position (iy1 + y_delta);
3121 temp_rtv = dynamic_cast<RouteTimeAxisView*>(tvp2);
3122 rv->set_y_position_and_height (0, temp_rtv->height);
3124 /* if you un-comment the following, the region colours will follow the track colours whilst dragging,
3125 personally, i think this can confuse things, but never mind.
3128 //const GdkColor& col (temp_rtv->view->get_region_color());
3129 //rv->set_color (const_cast<GdkColor&>(col));
3136 /* prevent the regionview from being moved to before
3137 the zero position on the canvas.
3142 if (-x_delta > ix1) {
3145 } else if ((x_delta > 0) && (rv->region()->last_frame() > max_frames - x_delta)) {
3146 x_delta = max_frames - rv->region()->last_frame();
3150 if (drag_info.first_move) {
3152 /* hide any dependent views */
3154 rv->get_time_axis_view().hide_dependent_views (*rv);
3156 /* this is subtle. raising the regionview itself won't help,
3157 because raise_to_top() just puts the item on the top of
3158 its parent's stack. so, we need to put the trackview canvas_display group
3159 on the top, since its parent is the whole canvas.
3162 rv->get_canvas_group()->raise_to_top();
3163 rv->get_time_axis_view().canvas_display->raise_to_top();
3164 cursor_group->raise_to_top();
3166 rv->fake_set_opaque (true);
3169 if (drag_info.brushing) {
3170 mouse_brush_insert_region (rv, pending_region_position);
3172 rv->move (x_delta, y_delta);
3175 } /* foreach region */
3179 if (drag_info.first_move && drag_info.move_threshold_passed) {
3180 cursor_group->raise_to_top();
3181 drag_info.first_move = false;
3184 if (x_delta != 0 && !drag_info.brushing) {
3185 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3190 Editor::region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3193 RegionView* rv = reinterpret_cast<RegionView *> (drag_info.data);
3194 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
3195 bool nocommit = true;
3197 RouteTimeAxisView* rtv;
3198 bool regionview_y_movement;
3199 bool regionview_x_movement;
3200 vector<RegionView*> copies;
3202 /* first_move is set to false if the regionview has been moved in the
3206 if (drag_info.first_move) {
3213 /* The regionview has been moved at some stage during the grab so we need
3214 to account for any mouse movement between this event and the last one.
3217 region_drag_motion_callback (item, event);
3219 if (drag_info.brushing) {
3220 /* all changes were made during motion event handlers */
3222 if (drag_info.copy) {
3223 for (list<RegionView*>::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
3224 copies.push_back (*i);
3231 /* adjust for track speed */
3234 rtv = dynamic_cast<RouteTimeAxisView*> (drag_info.last_trackview);
3235 if (rtv && rtv->get_diskstream()) {
3236 speed = rtv->get_diskstream()->speed();
3239 regionview_x_movement = (drag_info.last_frame_position != (nframes_t) (rv->region()->position()/speed));
3240 regionview_y_movement = (drag_info.last_trackview != &rv->get_time_axis_view());
3242 //printf ("last_frame: %s position is %lu %g\n", rv->get_time_axis_view().name().c_str(), drag_info.last_frame_position, speed);
3243 //printf ("last_rackview: %s \n", drag_info.last_trackview->name().c_str());
3247 if (drag_info.copy) {
3248 if (drag_info.x_constrained) {
3249 op_string = _("fixed time region copy");
3251 op_string = _("region copy");
3254 if (drag_info.x_constrained) {
3255 op_string = _("fixed time region drag");
3257 op_string = _("region drag");
3261 begin_reversible_command (op_string);
3263 if (regionview_y_movement) {
3265 /* moved to a different audio track. */
3267 vector<RegionView*> new_selection;
3269 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ) {
3271 RegionView* rv = (*i);
3273 double ix1, ix2, iy1, iy2;
3275 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3276 rv->get_canvas_group()->i2w (ix1, iy1);
3277 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3278 RouteTimeAxisView* rtv2 = dynamic_cast<RouteTimeAxisView*>(tvp2);
3280 boost::shared_ptr<Playlist> from_playlist = rv->region()->playlist();
3281 boost::shared_ptr<Playlist> to_playlist = rtv2->playlist();
3283 where = (nframes_t) (unit_to_frame (ix1) * speed);
3284 boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region()));
3286 /* undo the previous hide_dependent_views so that xfades don't
3287 disappear on copying regions
3290 rv->get_time_axis_view().reveal_dependent_views (*rv);
3292 if (!drag_info.copy) {
3294 /* the region that used to be in the old playlist is not
3295 moved to the new one - we make a copy of it. as a result,
3296 any existing editor for the region should no longer be
3300 rv->hide_region_editor();
3301 rv->fake_set_opaque (false);
3303 session->add_command (new MementoCommand<Playlist>(*from_playlist, &from_playlist->get_state(), 0));
3304 from_playlist->remove_region ((rv->region()));
3305 session->add_command (new MementoCommand<Playlist>(*from_playlist, 0, &from_playlist->get_state()));
3309 /* the regionview we dragged around is a temporary copy, queue it for deletion */
3311 copies.push_back (rv);
3314 latest_regionview = 0;
3316 sigc::connection c = rtv2->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3317 session->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
3318 to_playlist->add_region (new_region, where);
3319 session->add_command (new MementoCommand<Playlist>(*to_playlist, 0, &to_playlist->get_state()));
3322 if (latest_regionview) {
3323 new_selection.push_back (latest_regionview);
3326 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
3327 was selected in all of them, then removing it from the playlist will have removed all
3328 trace of it from the selection (i.e. there were N regions selected, we removed 1,
3329 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
3330 corresponding regionview, and the selection is now empty).
3332 this could have invalidated any and all iterators into the region selection.
3334 the heuristic we use here is: if the region selection is empty, break out of the loop
3335 here. if the region selection is not empty, then restart the loop because we know that
3336 we must have removed at least the region(view) we've just been working on as well as any
3337 that we processed on previous iterations.
3339 EXCEPT .... if we are doing a copy drag, then the selection hasn't been modified and
3340 we can just iterate.
3343 if (drag_info.copy) {
3346 if (selection->regions.empty()) {
3349 i = selection->regions.by_layer().begin();
3354 selection->set (new_selection);
3358 /* motion within a single track */
3360 list<RegionView*> regions = selection->regions.by_layer();
3362 if (drag_info.copy) {
3363 selection->clear_regions();
3366 for (list<RegionView*>::iterator i = regions.begin(); i != regions.end(); ++i) {
3370 if (!rv->region()->can_move()) {
3374 if (regionview_x_movement) {
3375 double ownspeed = 1.0;
3376 rtv = dynamic_cast<RouteTimeAxisView*> (&(rv->get_time_axis_view()));
3378 if (rtv && rtv->get_diskstream()) {
3379 ownspeed = rtv->get_diskstream()->speed();
3382 /* base the new region position on the current position of the regionview.*/
3384 double ix1, ix2, iy1, iy2;
3386 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3387 rv->get_canvas_group()->i2w (ix1, iy1);
3388 where = (nframes_t) (unit_to_frame (ix1) * ownspeed);
3392 where = rv->region()->position();
3395 boost::shared_ptr<Playlist> to_playlist = rv->region()->playlist();
3397 assert (to_playlist);
3401 session->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
3403 if (drag_info.copy) {
3405 boost::shared_ptr<Region> newregion;
3406 boost::shared_ptr<Region> ar;
3407 boost::shared_ptr<Region> mr;
3409 if ((ar = boost::dynamic_pointer_cast<AudioRegion>(rv->region())) != 0) {
3410 newregion = RegionFactory::create (ar);
3411 } else if ((mr = boost::dynamic_pointer_cast<MidiRegion>(rv->region())) != 0) {
3412 newregion = RegionFactory::create (mr);
3417 latest_regionview = 0;
3418 sigc::connection c = rtv->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3419 to_playlist->add_region (newregion, (nframes_t) (where * rtv->get_diskstream()->speed()));
3422 if (latest_regionview) {
3423 rtv->reveal_dependent_views (*latest_regionview);
3424 selection->add (latest_regionview);
3429 /* just change the model */
3431 rv->region()->set_position (where, (void*) this);
3437 session->add_command (new MementoCommand<Playlist>(*to_playlist, 0, &to_playlist->get_state()));
3439 if (drag_info.copy) {
3440 copies.push_back (rv);
3448 commit_reversible_command ();
3451 for (vector<RegionView*>::iterator x = copies.begin(); x != copies.end(); ++x) {
3457 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
3459 /* Either add to or set the set the region selection, unless
3460 this is an alignment click (control used)
3463 if (Keyboard::modifier_state_contains (event->state, Keyboard::Control)) {
3464 TimeAxisView* tv = &rv.get_time_axis_view();
3465 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
3467 if (rtv && rtv->is_track()) {
3468 speed = rtv->get_diskstream()->speed();
3471 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Alt))) {
3473 align_region (rv.region(), SyncPoint, (nframes_t) (edit_cursor->current_frame * speed));
3475 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Shift))) {
3477 align_region (rv.region(), End, (nframes_t) (edit_cursor->current_frame * speed));
3481 align_region (rv.region(), Start, (nframes_t) (edit_cursor->current_frame * speed));
3487 Editor::show_verbose_time_cursor (nframes_t frame, double offset, double xpos, double ypos)
3493 nframes_t frame_rate;
3500 switch (Profile->get_small_screen() ? ARDOUR_UI::instance()->primary_clock.mode () : ARDOUR_UI::instance()->secondary_clock.mode ()) {
3501 case AudioClock::BBT:
3502 session->bbt_time (frame, bbt);
3503 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, bbt.bars, bbt.beats, bbt.ticks);
3506 case AudioClock::SMPTE:
3507 session->smpte_time (frame, smpte);
3508 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
3511 case AudioClock::MinSec:
3512 /* XXX this is copied from show_verbose_duration_cursor() */
3513 frame_rate = session->frame_rate();
3514 hours = frame / (frame_rate * 3600);
3515 frame = frame % (frame_rate * 3600);
3516 mins = frame / (frame_rate * 60);
3517 frame = frame % (frame_rate * 60);
3518 secs = (float) frame / (float) frame_rate;
3519 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
3523 snprintf (buf, sizeof(buf), "%u", frame);
3527 if (xpos >= 0 && ypos >=0) {
3528 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
3531 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
3533 show_verbose_canvas_cursor ();
3537 Editor::show_verbose_duration_cursor (nframes_t start, nframes_t end, double offset, double xpos, double ypos)
3544 nframes_t distance, frame_rate;
3546 Meter meter_at_start(session->tempo_map().meter_at(start));
3552 switch (ARDOUR_UI::instance()->secondary_clock.mode ()) {
3553 case AudioClock::BBT:
3554 session->bbt_time (start, sbbt);
3555 session->bbt_time (end, ebbt);
3558 /* XXX this computation won't work well if the
3559 user makes a selection that spans any meter changes.
3562 ebbt.bars -= sbbt.bars;
3563 if (ebbt.beats >= sbbt.beats) {
3564 ebbt.beats -= sbbt.beats;
3567 ebbt.beats = int(meter_at_start.beats_per_bar()) + ebbt.beats - sbbt.beats;
3569 if (ebbt.ticks >= sbbt.ticks) {
3570 ebbt.ticks -= sbbt.ticks;
3573 ebbt.ticks = int(Meter::ticks_per_beat) + ebbt.ticks - sbbt.ticks;
3576 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, ebbt.bars, ebbt.beats, ebbt.ticks);
3579 case AudioClock::SMPTE:
3580 session->smpte_duration (end - start, smpte);
3581 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
3584 case AudioClock::MinSec:
3585 /* XXX this stuff should be elsewhere.. */
3586 distance = end - start;
3587 frame_rate = session->frame_rate();
3588 hours = distance / (frame_rate * 3600);
3589 distance = distance % (frame_rate * 3600);
3590 mins = distance / (frame_rate * 60);
3591 distance = distance % (frame_rate * 60);
3592 secs = (float) distance / (float) frame_rate;
3593 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
3597 snprintf (buf, sizeof(buf), "%u", end - start);
3601 if (xpos >= 0 && ypos >=0) {
3602 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
3605 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
3607 show_verbose_canvas_cursor ();
3611 Editor::collect_new_region_view (RegionView* rv)
3613 latest_regionview = rv;
3617 Editor::start_selection_grab (ArdourCanvas::Item* item, GdkEvent* event)
3619 if (clicked_regionview == 0) {
3623 /* lets try to create new Region for the selection */
3625 vector<boost::shared_ptr<AudioRegion> > new_regions;
3626 create_region_from_selection (new_regions);
3628 if (new_regions.empty()) {
3632 /* XXX fix me one day to use all new regions */
3634 boost::shared_ptr<Region> region (new_regions.front());
3636 /* add it to the current stream/playlist.
3638 tricky: the streamview for the track will add a new regionview. we will
3639 catch the signal it sends when it creates the regionview to
3640 set the regionview we want to then drag.
3643 latest_regionview = 0;
3644 sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3646 /* A selection grab currently creates two undo/redo operations, one for
3647 creating the new region and another for moving it.
3650 begin_reversible_command (_("selection grab"));
3652 boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
3654 XMLNode *before = &(playlist->get_state());
3655 clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
3656 XMLNode *after = &(playlist->get_state());
3657 session->add_command(new MementoCommand<Playlist>(*playlist, before, after));
3659 commit_reversible_command ();
3663 if (latest_regionview == 0) {
3664 /* something went wrong */
3668 /* we need to deselect all other regionviews, and select this one
3669 i'm ignoring undo stuff, because the region creation will take care of it */
3670 selection->set (latest_regionview);
3672 drag_info.item = latest_regionview->get_canvas_group();
3673 drag_info.data = latest_regionview;
3674 drag_info.motion_callback = &Editor::region_drag_motion_callback;
3675 drag_info.finished_callback = &Editor::region_drag_finished_callback;
3679 drag_info.last_trackview = clicked_axisview;
3680 drag_info.last_frame_position = latest_regionview->region()->position();
3681 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
3683 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3687 Editor::cancel_selection ()
3689 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
3690 (*i)->hide_selection ();
3692 begin_reversible_command (_("cancel selection"));
3693 selection->clear ();
3694 clicked_selection = 0;
3695 commit_reversible_command ();
3699 Editor::start_selection_op (ArdourCanvas::Item* item, GdkEvent* event, SelectionOp op)
3701 nframes_t start = 0;
3708 drag_info.item = item;
3709 drag_info.motion_callback = &Editor::drag_selection;
3710 drag_info.finished_callback = &Editor::end_selection_op;
3715 case CreateSelection:
3716 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
3717 drag_info.copy = true;
3719 drag_info.copy = false;
3721 start_grab (event, selector_cursor);
3724 case SelectionStartTrim:
3725 if (clicked_axisview) {
3726 clicked_axisview->order_selection_trims (item, true);
3728 start_grab (event, trimmer_cursor);
3729 start = selection->time[clicked_selection].start;
3730 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
3733 case SelectionEndTrim:
3734 if (clicked_axisview) {
3735 clicked_axisview->order_selection_trims (item, false);
3737 start_grab (event, trimmer_cursor);
3738 end = selection->time[clicked_selection].end;
3739 drag_info.pointer_frame_offset = drag_info.grab_frame - end;
3743 start = selection->time[clicked_selection].start;
3745 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
3749 if (selection_op == SelectionMove) {
3750 show_verbose_time_cursor(start, 10);
3752 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
3757 Editor::drag_selection (ArdourCanvas::Item* item, GdkEvent* event)
3759 nframes_t start = 0;
3762 nframes_t pending_position;
3764 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
3765 pending_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
3767 pending_position = 0;
3770 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3771 snap_to (pending_position);
3774 /* only alter selection if the current frame is
3775 different from the last frame position (adjusted)
3778 if (pending_position == drag_info.last_pointer_frame) return;
3780 switch (selection_op) {
3781 case CreateSelection:
3783 if (drag_info.first_move) {
3784 snap_to (drag_info.grab_frame);
3787 if (pending_position < drag_info.grab_frame) {
3788 start = pending_position;
3789 end = drag_info.grab_frame;
3791 end = pending_position;
3792 start = drag_info.grab_frame;
3795 /* first drag: Either add to the selection
3796 or create a new selection->
3799 if (drag_info.first_move) {
3801 begin_reversible_command (_("range selection"));
3803 if (drag_info.copy) {
3804 /* adding to the selection */
3805 clicked_selection = selection->add (start, end);
3806 drag_info.copy = false;
3808 /* new selection-> */
3809 clicked_selection = selection->set (clicked_axisview, start, end);
3814 case SelectionStartTrim:
3816 if (drag_info.first_move) {
3817 begin_reversible_command (_("trim selection start"));
3820 start = selection->time[clicked_selection].start;
3821 end = selection->time[clicked_selection].end;
3823 if (pending_position > end) {
3826 start = pending_position;
3830 case SelectionEndTrim:
3832 if (drag_info.first_move) {
3833 begin_reversible_command (_("trim selection end"));
3836 start = selection->time[clicked_selection].start;
3837 end = selection->time[clicked_selection].end;
3839 if (pending_position < start) {
3842 end = pending_position;
3849 if (drag_info.first_move) {
3850 begin_reversible_command (_("move selection"));
3853 start = selection->time[clicked_selection].start;
3854 end = selection->time[clicked_selection].end;
3856 length = end - start;
3858 start = pending_position;
3861 end = start + length;
3866 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
3867 start_canvas_autoscroll (1);
3871 selection->replace (clicked_selection, start, end);
3874 drag_info.last_pointer_frame = pending_position;
3875 drag_info.first_move = false;
3877 if (selection_op == SelectionMove) {
3878 show_verbose_time_cursor(start, 10);
3880 show_verbose_time_cursor(pending_position, 10);
3885 Editor::end_selection_op (ArdourCanvas::Item* item, GdkEvent* event)
3887 if (!drag_info.first_move) {
3888 drag_selection (item, event);
3889 /* XXX this is not object-oriented programming at all. ick */
3890 if (selection->time.consolidate()) {
3891 selection->TimeChanged ();
3893 commit_reversible_command ();
3895 /* just a click, no pointer movement.*/
3897 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
3899 selection->clear_time();
3904 /* XXX what happens if its a music selection? */
3905 session->set_audio_range (selection->time);
3906 stop_canvas_autoscroll ();
3910 Editor::start_trim (ArdourCanvas::Item* item, GdkEvent* event)
3913 TimeAxisView* tvp = clicked_axisview;
3914 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
3916 if (tv && tv->is_track()) {
3917 speed = tv->get_diskstream()->speed();
3920 nframes_t region_start = (nframes_t) (clicked_regionview->region()->position() / speed);
3921 nframes_t region_end = (nframes_t) (clicked_regionview->region()->last_frame() / speed);
3922 nframes_t region_length = (nframes_t) (clicked_regionview->region()->length() / speed);
3924 //drag_info.item = clicked_regionview->get_name_highlight();
3925 drag_info.item = item;
3926 drag_info.motion_callback = &Editor::trim_motion_callback;
3927 drag_info.finished_callback = &Editor::trim_finished_callback;
3929 start_grab (event, trimmer_cursor);
3931 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
3932 trim_op = ContentsTrim;
3934 /* These will get overridden for a point trim.*/
3935 if (drag_info.current_pointer_frame < (region_start + region_length/2)) {
3936 /* closer to start */
3937 trim_op = StartTrim;
3938 } else if (drag_info.current_pointer_frame > (region_end - region_length/2)) {
3946 show_verbose_time_cursor(region_start, 10);
3949 show_verbose_time_cursor(region_end, 10);
3952 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
3958 Editor::trim_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
3960 RegionView* rv = clicked_regionview;
3961 nframes_t frame_delta = 0;
3962 bool left_direction;
3963 bool obey_snap = !Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier());
3965 /* snap modifier works differently here..
3966 its' current state has to be passed to the
3967 various trim functions in order to work properly
3971 TimeAxisView* tvp = clicked_axisview;
3972 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
3973 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
3975 if (tv && tv->is_track()) {
3976 speed = tv->get_diskstream()->speed();
3979 if (drag_info.last_pointer_frame > drag_info.current_pointer_frame) {
3980 left_direction = true;
3982 left_direction = false;
3986 snap_to (drag_info.current_pointer_frame);
3989 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
3993 if (drag_info.first_move) {
3999 trim_type = "Region start trim";
4002 trim_type = "Region end trim";
4005 trim_type = "Region content trim";
4009 begin_reversible_command (trim_type);
4011 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4012 (*i)->fake_set_opaque(false);
4013 (*i)->region()->freeze ();
4015 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
4017 arv->temporarily_hide_envelope ();
4019 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4020 insert_result = motion_frozen_playlists.insert (pl);
4021 if (insert_result.second) {
4022 session->add_command(new MementoCommand<Playlist>(*pl, &pl->get_state(), 0));
4027 if (left_direction) {
4028 frame_delta = (drag_info.last_pointer_frame - drag_info.current_pointer_frame);
4030 frame_delta = (drag_info.current_pointer_frame - drag_info.last_pointer_frame);
4035 if ((left_direction == false) && (drag_info.current_pointer_frame <= rv->region()->first_frame()/speed)) {
4038 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4039 single_start_trim (**i, frame_delta, left_direction, obey_snap);
4045 if ((left_direction == true) && (drag_info.current_pointer_frame > (nframes_t) (rv->region()->last_frame()/speed))) {
4048 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4049 single_end_trim (**i, frame_delta, left_direction, obey_snap);
4056 bool swap_direction = false;
4058 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
4059 swap_direction = true;
4062 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4063 i != selection->regions.by_layer().end(); ++i)
4065 single_contents_trim (**i, frame_delta, left_direction, swap_direction, obey_snap);
4073 show_verbose_time_cursor((nframes_t) (rv->region()->position()/speed), 10);
4076 show_verbose_time_cursor((nframes_t) (rv->region()->last_frame()/speed), 10);
4079 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4083 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4084 drag_info.first_move = false;
4088 Editor::single_contents_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool swap_direction, bool obey_snap)
4090 boost::shared_ptr<Region> region (rv.region());
4092 if (region->locked()) {
4096 nframes_t new_bound;
4099 TimeAxisView* tvp = clicked_axisview;
4100 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4102 if (tv && tv->is_track()) {
4103 speed = tv->get_diskstream()->speed();
4106 if (left_direction) {
4107 if (swap_direction) {
4108 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4110 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4113 if (swap_direction) {
4114 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4116 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4121 snap_to (new_bound);
4123 region->trim_start ((nframes_t) (new_bound * speed), this);
4124 rv.region_changed (StartChanged);
4128 Editor::single_start_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool obey_snap)
4130 boost::shared_ptr<Region> region (rv.region());
4132 if (region->locked()) {
4136 nframes_t new_bound;
4139 TimeAxisView* tvp = clicked_axisview;
4140 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4142 if (tv && tv->is_track()) {
4143 speed = tv->get_diskstream()->speed();
4146 if (left_direction) {
4147 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4149 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4153 snap_to (new_bound, (left_direction ? 0 : 1));
4156 region->trim_front ((nframes_t) (new_bound * speed), this);
4158 rv.region_changed (Change (LengthChanged|PositionChanged|StartChanged));
4162 Editor::single_end_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool obey_snap)
4164 boost::shared_ptr<Region> region (rv.region());
4166 if (region->locked()) {
4170 nframes_t new_bound;
4173 TimeAxisView* tvp = clicked_axisview;
4174 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4176 if (tv && tv->is_track()) {
4177 speed = tv->get_diskstream()->speed();
4180 if (left_direction) {
4181 new_bound = (nframes_t) ((region->last_frame() + 1)/speed) - frame_delta;
4183 new_bound = (nframes_t) ((region->last_frame() + 1)/speed) + frame_delta;
4187 snap_to (new_bound);
4189 region->trim_end ((nframes_t) (new_bound * speed), this);
4190 rv.region_changed (LengthChanged);
4194 Editor::trim_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
4196 if (!drag_info.first_move) {
4197 trim_motion_callback (item, event);
4199 if (!clicked_regionview->get_selected()) {
4200 thaw_region_after_trim (*clicked_regionview);
4203 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4204 i != selection->regions.by_layer().end(); ++i)
4206 thaw_region_after_trim (**i);
4207 (*i)->fake_set_opaque (true);
4211 for (set<boost::shared_ptr<Playlist> >::iterator p = motion_frozen_playlists.begin(); p != motion_frozen_playlists.end(); ++p) {
4213 session->add_command (new MementoCommand<Playlist>(*(*p).get(), 0, &(*p)->get_state()));
4216 motion_frozen_playlists.clear ();
4218 commit_reversible_command();
4220 /* no mouse movement */
4226 Editor::point_trim (GdkEvent* event)
4228 RegionView* rv = clicked_regionview;
4229 nframes_t new_bound = drag_info.current_pointer_frame;
4231 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4232 snap_to (new_bound);
4235 /* Choose action dependant on which button was pressed */
4236 switch (event->button.button) {
4238 trim_op = StartTrim;
4239 begin_reversible_command (_("Start point trim"));
4241 if (rv->get_selected()) {
4243 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4244 i != selection->regions.by_layer().end(); ++i)
4246 if (!(*i)->region()->locked()) {
4247 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4248 XMLNode &before = pl->get_state();
4249 (*i)->region()->trim_front (new_bound, this);
4250 XMLNode &after = pl->get_state();
4251 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4257 if (!rv->region()->locked()) {
4258 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
4259 XMLNode &before = pl->get_state();
4260 rv->region()->trim_front (new_bound, this);
4261 XMLNode &after = pl->get_state();
4262 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4266 commit_reversible_command();
4271 begin_reversible_command (_("End point trim"));
4273 if (rv->get_selected()) {
4275 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
4277 if (!(*i)->region()->locked()) {
4278 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4279 XMLNode &before = pl->get_state();
4280 (*i)->region()->trim_end (new_bound, this);
4281 XMLNode &after = pl->get_state();
4282 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4288 if (!rv->region()->locked()) {
4289 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
4290 XMLNode &before = pl->get_state();
4291 rv->region()->trim_end (new_bound, this);
4292 XMLNode &after = pl->get_state();
4293 session->add_command (new MementoCommand<Playlist>(*pl.get(), &before, &after));
4297 commit_reversible_command();
4306 Editor::thaw_region_after_trim (RegionView& rv)
4308 boost::shared_ptr<Region> region (rv.region());
4310 if (region->locked()) {
4314 region->thaw (_("trimmed region"));
4315 XMLNode &after = region->playlist()->get_state();
4316 session->add_command (new MementoCommand<Playlist>(*(region->playlist()), 0, &after));
4318 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(&rv);
4320 arv->unhide_envelope ();
4324 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* event)
4329 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
4330 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
4334 Location* location = find_location_from_marker (marker, is_start);
4335 location->set_hidden (true, this);
4340 Editor::start_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event, RangeMarkerOp op)
4346 drag_info.item = item;
4347 drag_info.motion_callback = &Editor::drag_range_markerbar_op;
4348 drag_info.finished_callback = &Editor::end_range_markerbar_op;
4350 range_marker_op = op;
4352 if (!temp_location) {
4353 temp_location = new Location;
4357 case CreateRangeMarker:
4358 case CreateTransportMarker:
4360 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
4361 drag_info.copy = true;
4363 drag_info.copy = false;
4365 start_grab (event, selector_cursor);
4369 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4374 Editor::drag_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4376 nframes_t start = 0;
4378 ArdourCanvas::SimpleRect *crect = (range_marker_op == CreateRangeMarker) ? range_bar_drag_rect: transport_bar_drag_rect;
4380 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4381 snap_to (drag_info.current_pointer_frame);
4384 /* only alter selection if the current frame is
4385 different from the last frame position.
4388 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4390 switch (range_marker_op) {
4391 case CreateRangeMarker:
4392 case CreateTransportMarker:
4393 if (drag_info.first_move) {
4394 snap_to (drag_info.grab_frame);
4397 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4398 start = drag_info.current_pointer_frame;
4399 end = drag_info.grab_frame;
4401 end = drag_info.current_pointer_frame;
4402 start = drag_info.grab_frame;
4405 /* first drag: Either add to the selection
4406 or create a new selection.
4409 if (drag_info.first_move) {
4411 temp_location->set (start, end);
4415 update_marker_drag_item (temp_location);
4416 range_marker_drag_rect->show();
4417 range_marker_drag_rect->raise_to_top();
4423 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
4424 start_canvas_autoscroll (1);
4428 temp_location->set (start, end);
4430 double x1 = frame_to_pixel (start);
4431 double x2 = frame_to_pixel (end);
4432 crect->property_x1() = x1;
4433 crect->property_x2() = x2;
4435 update_marker_drag_item (temp_location);
4438 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4439 drag_info.first_move = false;
4441 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4446 Editor::end_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4448 Location * newloc = 0;
4451 if (!drag_info.first_move) {
4452 drag_range_markerbar_op (item, event);
4454 switch (range_marker_op) {
4455 case CreateRangeMarker:
4457 begin_reversible_command (_("new range marker"));
4458 XMLNode &before = session->locations()->get_state();
4459 session->locations()->next_available_name(rangename,"unnamed");
4460 newloc = new Location(temp_location->start(), temp_location->end(), rangename, Location::IsRangeMarker);
4461 session->locations()->add (newloc, true);
4462 XMLNode &after = session->locations()->get_state();
4463 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
4464 commit_reversible_command ();
4466 range_bar_drag_rect->hide();
4467 range_marker_drag_rect->hide();
4471 case CreateTransportMarker:
4472 // popup menu to pick loop or punch
4473 new_transport_marker_context_menu (&event->button, item);
4478 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
4480 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
4485 start = session->locations()->first_mark_before (drag_info.grab_frame);
4486 end = session->locations()->first_mark_after (drag_info.grab_frame);
4488 if (end == max_frames) {
4489 end = session->current_end_frame ();
4493 start = session->current_start_frame ();
4496 switch (mouse_mode) {
4498 /* find the two markers on either side and then make the selection from it */
4499 select_all_within (start, end, 0.0f, FLT_MAX, track_views, Selection::Set);
4503 /* find the two markers on either side of the click and make the range out of it */
4504 selection->set (0, start, end);
4513 stop_canvas_autoscroll ();
4519 Editor::start_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4521 drag_info.item = item;
4522 drag_info.motion_callback = &Editor::drag_mouse_zoom;
4523 drag_info.finished_callback = &Editor::end_mouse_zoom;
4525 start_grab (event, zoom_cursor);
4527 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4531 Editor::drag_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4536 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4537 snap_to (drag_info.current_pointer_frame);
4539 if (drag_info.first_move) {
4540 snap_to (drag_info.grab_frame);
4544 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4546 /* base start and end on initial click position */
4547 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4548 start = drag_info.current_pointer_frame;
4549 end = drag_info.grab_frame;
4551 end = drag_info.current_pointer_frame;
4552 start = drag_info.grab_frame;
4557 if (drag_info.first_move) {
4559 zoom_rect->raise_to_top();
4562 reposition_zoom_rect(start, end);
4564 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4565 drag_info.first_move = false;
4567 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4572 Editor::end_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4574 if (!drag_info.first_move) {
4575 drag_mouse_zoom (item, event);
4577 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
4578 temporal_zoom_by_frame (drag_info.grab_frame, drag_info.last_pointer_frame, "mouse zoom");
4580 temporal_zoom_by_frame (drag_info.last_pointer_frame, drag_info.grab_frame, "mouse zoom");
4583 temporal_zoom_to_frame (false, drag_info.grab_frame);
4585 temporal_zoom_step (false);
4586 center_screen (drag_info.grab_frame);
4594 Editor::reposition_zoom_rect (nframes_t start, nframes_t end)
4596 double x1 = frame_to_pixel (start);
4597 double x2 = frame_to_pixel (end);
4598 double y2 = full_canvas_height - 1.0;
4600 zoom_rect->property_x1() = x1;
4601 zoom_rect->property_y1() = 1.0;
4602 zoom_rect->property_x2() = x2;
4603 zoom_rect->property_y2() = y2;
4607 Editor::start_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4609 drag_info.item = item;
4610 drag_info.motion_callback = &Editor::drag_rubberband_select;
4611 drag_info.finished_callback = &Editor::end_rubberband_select;
4613 start_grab (event, cross_hair_cursor);
4615 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4619 Editor::drag_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4626 /* use a bigger drag threshold than the default */
4628 if (abs ((int) (drag_info.current_pointer_frame - drag_info.grab_frame)) < 8) {
4632 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4633 if (drag_info.first_move) {
4634 snap_to (drag_info.grab_frame);
4636 snap_to (drag_info.current_pointer_frame);
4639 /* base start and end on initial click position */
4641 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4642 start = drag_info.current_pointer_frame;
4643 end = drag_info.grab_frame;
4645 end = drag_info.current_pointer_frame;
4646 start = drag_info.grab_frame;
4649 if (drag_info.current_pointer_y < drag_info.grab_y) {
4650 y1 = drag_info.current_pointer_y;
4651 y2 = drag_info.grab_y;
4653 y2 = drag_info.current_pointer_y;
4654 y1 = drag_info.grab_y;
4658 if (start != end || y1 != y2) {
4660 double x1 = frame_to_pixel (start);
4661 double x2 = frame_to_pixel (end);
4663 rubberband_rect->property_x1() = x1;
4664 rubberband_rect->property_y1() = y1;
4665 rubberband_rect->property_x2() = x2;
4666 rubberband_rect->property_y2() = y2;
4668 rubberband_rect->show();
4669 rubberband_rect->raise_to_top();
4671 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4672 drag_info.first_move = false;
4674 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4679 Editor::end_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4681 if (!drag_info.first_move) {
4683 drag_rubberband_select (item, event);
4686 if (drag_info.current_pointer_y < drag_info.grab_y) {
4687 y1 = drag_info.current_pointer_y;
4688 y2 = drag_info.grab_y;
4691 y2 = drag_info.current_pointer_y;
4692 y1 = drag_info.grab_y;
4696 Selection::Operation op = Keyboard::selection_type (event->button.state);
4699 begin_reversible_command (_("rubberband selection"));
4701 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
4702 commit = select_all_within (drag_info.grab_frame, drag_info.last_pointer_frame, y1, y2, track_views, op);
4704 commit = select_all_within (drag_info.last_pointer_frame, drag_info.grab_frame, y1, y2, track_views, op);
4708 commit_reversible_command ();
4712 selection->clear_tracks();
4713 selection->clear_regions();
4714 selection->clear_points ();
4715 selection->clear_lines ();
4718 rubberband_rect->hide();
4723 Editor::mouse_rename_region (ArdourCanvas::Item* item, GdkEvent* event)
4725 using namespace Gtkmm2ext;
4727 ArdourPrompter prompter (false);
4729 prompter.set_prompt (_("Name for region:"));
4730 prompter.set_initial_text (clicked_regionview->region()->name());
4731 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
4732 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
4733 prompter.show_all ();
4734 switch (prompter.run ()) {
4735 case Gtk::RESPONSE_ACCEPT:
4737 prompter.get_result(str);
4739 clicked_regionview->region()->set_name (str);
4747 Editor::start_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
4749 drag_info.item = item;
4750 drag_info.motion_callback = &Editor::time_fx_motion;
4751 drag_info.finished_callback = &Editor::end_time_fx;
4755 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4759 Editor::time_fx_motion (ArdourCanvas::Item *item, GdkEvent* event)
4761 RegionView* rv = clicked_regionview;
4763 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4764 snap_to (drag_info.current_pointer_frame);
4767 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
4771 if (drag_info.current_pointer_frame > rv->region()->position()) {
4772 rv->get_time_axis_view().show_timestretch (rv->region()->position(), drag_info.current_pointer_frame);
4775 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4776 drag_info.first_move = false;
4778 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4782 Editor::end_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
4784 clicked_regionview->get_time_axis_view().hide_timestretch ();
4786 if (drag_info.first_move) {
4790 nframes_t newlen = drag_info.last_pointer_frame - clicked_regionview->region()->position();
4791 float percentage = (float) ((double) newlen - (double) clicked_regionview->region()->length()) / ((double) newlen) * 100.0f;
4793 begin_reversible_command (_("timestretch"));
4795 if (run_timestretch (selection->regions, percentage) == 0) {
4796 session->commit_reversible_command ();
4801 Editor::mouse_brush_insert_region (RegionView* rv, nframes_t pos)
4803 /* no brushing without a useful snap setting */
4806 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
4809 switch (snap_mode) {
4811 return; /* can't work because it allows region to be placed anywhere */
4816 switch (snap_type) {
4819 case SnapToEditCursor:
4826 /* don't brush a copy over the original */
4828 if (pos == rv->region()->position()) {
4832 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&arv->get_time_axis_view());
4834 if (rtv == 0 || !rtv->is_track()) {
4838 boost::shared_ptr<Playlist> playlist = rtv->playlist();
4839 double speed = rtv->get_diskstream()->speed();
4841 XMLNode &before = playlist->get_state();
4842 playlist->add_region (boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (arv->audio_region())), (nframes_t) (pos * speed));
4843 XMLNode &after = playlist->get_state();
4844 session->add_command(new MementoCommand<Playlist>(*playlist.get(), &before, &after));
4846 // playlist is frozen, so we have to update manually
4848 playlist->Modified(); /* EMIT SIGNAL */
4852 Editor::track_height_step_timeout ()
4855 struct timeval delta;
4857 gettimeofday (&now, 0);
4858 timersub (&now, &last_track_height_step_timestamp, &delta);
4860 if (delta.tv_sec * 1000000 + delta.tv_usec > 250000) { /* milliseconds */
4861 current_stepping_trackview = 0;