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"
42 #include "control_point.h"
45 #include "selection.h"
48 #include "rgb_macros.h"
50 #include <ardour/types.h>
51 #include <ardour/profile.h>
52 #include <ardour/route.h>
53 #include <ardour/audio_track.h>
54 #include <ardour/audio_diskstream.h>
55 #include <ardour/playlist.h>
56 #include <ardour/audioplaylist.h>
57 #include <ardour/audioregion.h>
58 #include <ardour/midi_region.h>
59 #include <ardour/dB.h>
60 #include <ardour/utils.h>
61 #include <ardour/region_factory.h>
68 using namespace ARDOUR;
72 using namespace Editing;
75 Editor::event_frame (GdkEvent* event, double* pcx, double* pcy)
89 switch (event->type) {
90 case GDK_BUTTON_RELEASE:
91 case GDK_BUTTON_PRESS:
92 case GDK_2BUTTON_PRESS:
93 case GDK_3BUTTON_PRESS:
94 track_canvas.w2c(event->button.x, event->button.y, *pcx, *pcy);
96 case GDK_MOTION_NOTIFY:
97 track_canvas.w2c(event->motion.x, event->motion.y, *pcx, *pcy);
99 case GDK_ENTER_NOTIFY:
100 case GDK_LEAVE_NOTIFY:
101 track_canvas.w2c(event->crossing.x, event->crossing.y, *pcx, *pcy);
104 case GDK_KEY_RELEASE:
105 // track_canvas.w2c(event->key.x, event->key.y, *pcx, *pcy);
108 warning << string_compose (_("Editor::event_frame() used on unhandled event type %1"), event->type) << endmsg;
112 /* note that pixel_to_frame() never returns less than zero, so even if the pixel
113 position is negative (as can be the case with motion events in particular),
114 the frame location is always positive.
117 return pixel_to_frame (*pcx);
121 Editor::mouse_mode_toggled (MouseMode m)
123 if (ignore_mouse_mode_toggle) {
129 if (mouse_select_button.get_active()) {
135 if (mouse_move_button.get_active()) {
141 if (mouse_gain_button.get_active()) {
147 if (mouse_zoom_button.get_active()) {
153 if (mouse_timefx_button.get_active()) {
159 if (mouse_audition_button.get_active()) {
170 Editor::set_mouse_mode (MouseMode m, bool force)
172 if (drag_info.item) {
176 if (!force && m == mouse_mode) {
184 if (mouse_mode != MouseRange) {
186 /* in all modes except range, hide the range selection,
187 show the object (region) selection.
190 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
191 (*i)->set_should_show_selection (true);
193 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
194 (*i)->hide_selection ();
200 in range mode,show the range selection.
203 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
204 if ((*i)->get_selected()) {
205 (*i)->show_selection (selection->time);
210 /* XXX the hack of unsetting all other buttongs should go
211 away once GTK2 allows us to use regular radio buttons drawn like
212 normal buttons, rather than my silly GroupedButton hack.
215 ignore_mouse_mode_toggle = true;
217 switch (mouse_mode) {
219 mouse_select_button.set_active (true);
220 current_canvas_cursor = selector_cursor;
224 mouse_move_button.set_active (true);
225 current_canvas_cursor = grabber_cursor;
229 mouse_gain_button.set_active (true);
230 current_canvas_cursor = cross_hair_cursor;
234 mouse_zoom_button.set_active (true);
235 current_canvas_cursor = zoom_cursor;
239 mouse_timefx_button.set_active (true);
240 current_canvas_cursor = time_fx_cursor; // just use playhead
244 mouse_audition_button.set_active (true);
245 current_canvas_cursor = speaker_cursor;
249 ignore_mouse_mode_toggle = false;
252 track_canvas.get_window()->set_cursor(*current_canvas_cursor);
257 Editor::step_mouse_mode (bool next)
259 switch (current_mouse_mode()) {
261 if (next) set_mouse_mode (MouseRange);
262 else set_mouse_mode (MouseTimeFX);
266 if (next) set_mouse_mode (MouseZoom);
267 else set_mouse_mode (MouseObject);
271 if (next) set_mouse_mode (MouseGain);
272 else set_mouse_mode (MouseRange);
276 if (next) set_mouse_mode (MouseTimeFX);
277 else set_mouse_mode (MouseZoom);
281 if (next) set_mouse_mode (MouseAudition);
282 else set_mouse_mode (MouseGain);
286 if (next) set_mouse_mode (MouseObject);
287 else set_mouse_mode (MouseTimeFX);
293 Editor::button_selection (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
297 /* in object/audition/timefx mode, any button press sets
298 the selection if the object can be selected. this is a
299 bit of hack, because we want to avoid this if the
300 mouse operation is a region alignment.
302 note: not dbl-click or triple-click
305 if (((mouse_mode != MouseObject) &&
306 (mouse_mode != MouseAudition || item_type != RegionItem) &&
307 (mouse_mode != MouseTimeFX || item_type != RegionItem) &&
308 (mouse_mode != MouseRange)) ||
310 (event->type != GDK_BUTTON_PRESS && event->type != GDK_BUTTON_RELEASE || event->button.button > 3)) {
315 if (event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) {
317 if ((event->button.state & Keyboard::RelevantModifierKeyMask) && event->button.button != 1) {
319 /* almost no selection action on modified button-2 or button-3 events */
321 if (item_type != RegionItem && event->button.button != 2) {
327 Selection::Operation op = Keyboard::selection_type (event->button.state);
328 bool press = (event->type == GDK_BUTTON_PRESS);
330 // begin_reversible_command (_("select on click"));
334 case RegionViewNameHighlight:
336 case FadeInHandleItem:
338 case FadeOutHandleItem:
340 if (mouse_mode != MouseRange) {
341 commit = set_selected_regionview_from_click (press, op, true);
342 } else if (event->type == GDK_BUTTON_PRESS) {
343 commit = set_selected_track_from_click (press, op, false);
347 case CrossfadeViewItem:
348 commit = set_selected_track_from_click (press, op, false);
351 case ControlPointItem:
352 commit = set_selected_track_from_click (press, op, true);
353 if (mouse_mode != MouseRange) {
354 commit |= set_selected_control_point_from_click (op, false);
359 /* for context click or range selection, select track */
360 if (event->button.button == 3) {
361 commit = set_selected_track_from_click (press, op, true);
362 } else if (event->type == GDK_BUTTON_PRESS && mouse_mode == MouseRange) {
363 commit = set_selected_track_from_click (press, op, false);
367 case AutomationTrackItem:
368 commit = set_selected_track_from_click (press, op, true);
376 // commit_reversible_command ();
381 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
383 track_canvas.grab_focus();
385 if (session && session->actively_recording()) {
389 button_selection (item, event, item_type);
391 if (drag_info.item == 0 &&
392 (Keyboard::is_delete_event (&event->button) ||
393 Keyboard::is_context_menu_event (&event->button) ||
394 Keyboard::is_edit_event (&event->button))) {
396 /* handled by button release */
400 switch (event->button.button) {
403 if (event->type == GDK_BUTTON_PRESS) {
405 if (drag_info.item) {
406 drag_info.item->ungrab (event->button.time);
409 /* single mouse clicks on any of these item types operate
410 independent of mouse mode, mostly because they are
411 not on the main track canvas or because we want
417 case PlayheadCursorItem:
418 start_cursor_grab (item, event);
422 if (Keyboard::modifier_state_equals (event->button.state,
423 Keyboard::ModifierMask(Keyboard::Control|Keyboard::Shift))) {
424 hide_marker (item, event);
426 start_marker_grab (item, event);
430 case TempoMarkerItem:
431 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
432 start_tempo_marker_copy_grab (item, event);
434 start_tempo_marker_grab (item, event);
438 case MeterMarkerItem:
439 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
440 start_meter_marker_copy_grab (item, event);
442 start_meter_marker_grab (item, event);
452 case RangeMarkerBarItem:
453 start_range_markerbar_op (item, event, CreateRangeMarker);
457 case TransportMarkerBarItem:
458 start_range_markerbar_op (item, event, CreateTransportMarker);
467 switch (mouse_mode) {
470 case StartSelectionTrimItem:
471 start_selection_op (item, event, SelectionStartTrim);
474 case EndSelectionTrimItem:
475 start_selection_op (item, event, SelectionEndTrim);
479 if (Keyboard::modifier_state_contains
480 (event->button.state, Keyboard::ModifierMask(Keyboard::Alt))) {
481 // contains and not equals because I can't use alt as a modifier alone.
482 start_selection_grab (item, event);
483 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
484 /* grab selection for moving */
485 start_selection_op (item, event, SelectionMove);
488 /* this was debated, but decided the more common action was to
489 make a new selection */
490 start_selection_op (item, event, CreateSelection);
495 start_selection_op (item, event, CreateSelection);
501 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::Control|Keyboard::Alt)) &&
502 event->type == GDK_BUTTON_PRESS) {
504 start_rubberband_select (item, event);
506 } else if (event->type == GDK_BUTTON_PRESS) {
509 case FadeInHandleItem:
510 start_fade_in_grab (item, event);
513 case FadeOutHandleItem:
514 start_fade_out_grab (item, event);
518 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
519 start_region_copy_grab (item, event);
520 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
521 start_region_brush_grab (item, event);
523 start_region_grab (item, event);
527 case RegionViewNameHighlight:
528 start_trim (item, event);
533 /* rename happens on edit clicks */
534 start_trim (clicked_regionview->get_name_highlight(), event);
538 case ControlPointItem:
539 start_control_point_grab (item, event);
543 case AutomationLineItem:
544 start_line_grab_from_line (item, event);
549 case AutomationTrackItem:
550 start_rubberband_select (item, event);
554 case ImageFrameHandleStartItem:
555 imageframe_start_handle_op(item, event) ;
558 case ImageFrameHandleEndItem:
559 imageframe_end_handle_op(item, event) ;
562 case MarkerViewHandleStartItem:
563 markerview_item_start_handle_op(item, event) ;
566 case MarkerViewHandleEndItem:
567 markerview_item_end_handle_op(item, event) ;
571 start_markerview_grab(item, event) ;
574 start_imageframe_grab(item, event) ;
592 // start_line_grab_from_regionview (item, event);
596 start_line_grab_from_line (item, event);
599 case ControlPointItem:
600 start_control_point_grab (item, event);
611 case ControlPointItem:
612 start_control_point_grab (item, event);
615 case AutomationLineItem:
616 start_line_grab_from_line (item, event);
620 // XXX need automation mode to identify which
622 // start_line_grab_from_regionview (item, event);
632 if (event->type == GDK_BUTTON_PRESS) {
633 start_mouse_zoom (item, event);
640 if (item_type == RegionItem) {
641 start_time_fx (item, event);
646 /* handled in release */
655 switch (mouse_mode) {
657 if (event->type == GDK_BUTTON_PRESS) {
660 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
661 start_region_copy_grab (item, event);
663 start_region_grab (item, event);
667 case ControlPointItem:
668 start_control_point_grab (item, event);
679 case RegionViewNameHighlight:
680 start_trim (item, event);
685 start_trim (clicked_regionview->get_name_highlight(), event);
696 if (event->type == GDK_BUTTON_PRESS) {
697 /* relax till release */
704 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
705 temporal_zoom_session();
707 temporal_zoom_to_frame (true, event_frame(event));
730 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
732 nframes_t where = event_frame (event, 0, 0);
734 /* no action if we're recording */
736 if (session && session->actively_recording()) {
740 /* first, see if we're finishing a drag ... */
742 if (drag_info.item) {
743 if (end_grab (item, event)) {
744 /* grab dragged, so do nothing else */
749 button_selection (item, event, item_type);
751 /* edit events get handled here */
753 if (drag_info.item == 0 && Keyboard::is_edit_event (&event->button)) {
759 case TempoMarkerItem:
760 edit_tempo_marker (item);
763 case MeterMarkerItem:
764 edit_meter_marker (item);
768 if (clicked_regionview->name_active()) {
769 return mouse_rename_region (item, event);
779 /* context menu events get handled here */
781 if (Keyboard::is_context_menu_event (&event->button)) {
783 if (drag_info.item == 0) {
785 /* no matter which button pops up the context menu, tell the menu
786 widget to use button 1 to drive menu selection.
791 case FadeInHandleItem:
793 case FadeOutHandleItem:
794 popup_fade_context_menu (1, event->button.time, item, item_type);
799 case RegionViewNameHighlight:
802 case AutomationTrackItem:
803 case CrossfadeViewItem:
804 popup_track_context_menu (1, event->button.time, where);
808 case RangeMarkerBarItem:
809 case TransportMarkerBarItem:
812 popup_ruler_menu (pixel_to_frame(event->button.x), item_type);
816 marker_context_menu (&event->button, item);
819 case TempoMarkerItem:
820 tm_marker_context_menu (&event->button, item);
823 case MeterMarkerItem:
824 tm_marker_context_menu (&event->button, item);
829 popup_imageframe_edit_menu(1, event->button.time, item, true) ;
831 case ImageFrameTimeAxisItem:
832 popup_imageframe_edit_menu(1, event->button.time, item, false) ;
835 popup_marker_time_axis_edit_menu(1, event->button.time, item, true) ;
837 case MarkerTimeAxisItem:
838 popup_marker_time_axis_edit_menu(1, event->button.time, item, false) ;
850 /* delete events get handled here */
852 if (drag_info.item == 0 && Keyboard::is_delete_event (&event->button)) {
855 case TempoMarkerItem:
856 remove_tempo_marker (item);
859 case MeterMarkerItem:
860 remove_meter_marker (item);
864 remove_marker (*item, event);
868 if (mouse_mode == MouseObject) {
869 remove_clicked_region ();
873 case ControlPointItem:
874 if (mouse_mode == MouseGain) {
875 remove_gain_control_point (item, event);
877 remove_control_point (item, event);
887 switch (event->button.button) {
891 /* see comments in button_press_handler */
893 case PlayheadCursorItem:
896 case AutomationLineItem:
897 case StartSelectionTrimItem:
898 case EndSelectionTrimItem:
902 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
903 snap_to (where, 0, true);
905 mouse_add_new_marker (where);
909 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
912 mouse_add_new_tempo_event (where);
916 mouse_add_new_meter_event (pixel_to_frame (event->button.x));
924 switch (mouse_mode) {
927 case AutomationTrackItem:
928 dynamic_cast<AutomationTimeAxisView*>(clicked_axisview)->add_automation_event
942 // Gain only makes sense for audio regions
944 if (!dynamic_cast<AudioRegionView*>(clicked_regionview)) {
950 dynamic_cast<AudioRegionView*>(clicked_regionview)->add_gain_point_event (item, event);
954 case AutomationTrackItem:
955 dynamic_cast<AutomationTimeAxisView*>(clicked_axisview)->
956 add_automation_event (item, event, where, event->button.y);
967 audition_selected_region ();
984 switch (mouse_mode) {
988 // x_style_paste (where, 1.0);
1008 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1014 switch (item_type) {
1015 case ControlPointItem:
1016 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1017 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1018 cp->set_visible (true);
1022 at_y = cp->get_y ();
1023 cp->item()->i2w (at_x, at_y);
1027 fraction = 1.0 - (cp->get_y() / cp->line().height());
1029 set_verbose_canvas_cursor (cp->line().get_verbose_cursor_string (fraction), at_x, at_y);
1030 show_verbose_canvas_cursor ();
1032 if (is_drawable()) {
1033 track_canvas.get_window()->set_cursor (*fader_cursor);
1039 if (mouse_mode == MouseGain) {
1040 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1042 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredGainLine.get();
1043 if (is_drawable()) {
1044 track_canvas.get_window()->set_cursor (*fader_cursor);
1049 case AutomationLineItem:
1050 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1052 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1054 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredAutomationLine.get();
1056 if (is_drawable()) {
1057 track_canvas.get_window()->set_cursor (*fader_cursor);
1062 case RegionViewNameHighlight:
1063 if (is_drawable() && mouse_mode == MouseObject) {
1064 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1068 case StartSelectionTrimItem:
1069 case EndSelectionTrimItem:
1072 case ImageFrameHandleStartItem:
1073 case ImageFrameHandleEndItem:
1074 case MarkerViewHandleStartItem:
1075 case MarkerViewHandleEndItem:
1078 if (is_drawable()) {
1079 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1083 case EditCursorItem:
1084 case PlayheadCursorItem:
1085 if (is_drawable()) {
1086 track_canvas.get_window()->set_cursor (*grabber_cursor);
1090 case RegionViewName:
1092 /* when the name is not an active item, the entire name highlight is for trimming */
1094 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1095 if (mouse_mode == MouseObject && is_drawable()) {
1096 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1102 case AutomationTrackItem:
1103 if (is_drawable()) {
1104 Gdk::Cursor *cursor;
1105 switch (mouse_mode) {
1107 cursor = selector_cursor;
1110 cursor = zoom_cursor;
1113 cursor = cross_hair_cursor;
1117 track_canvas.get_window()->set_cursor (*cursor);
1119 AutomationTimeAxisView* atv;
1120 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1121 clear_entered_track = false;
1122 set_entered_track (atv);
1128 case RangeMarkerBarItem:
1129 case TransportMarkerBarItem:
1132 if (is_drawable()) {
1133 time_canvas.get_window()->set_cursor (*timebar_cursor);
1138 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1141 marker->set_color_rgba (ARDOUR_UI::config()->canvasvar_EnteredMarker.get());
1143 case MeterMarkerItem:
1144 case TempoMarkerItem:
1145 if (is_drawable()) {
1146 time_canvas.get_window()->set_cursor (*timebar_cursor);
1149 case FadeInHandleItem:
1150 case FadeOutHandleItem:
1151 if (mouse_mode == MouseObject) {
1152 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1154 rect->property_fill_color_rgba() = 0;
1155 rect->property_outline_pixels() = 1;
1164 /* second pass to handle entered track status in a comprehensible way.
1167 switch (item_type) {
1169 case AutomationLineItem:
1170 case ControlPointItem:
1171 /* these do not affect the current entered track state */
1172 clear_entered_track = false;
1175 case AutomationTrackItem:
1176 /* handled above already */
1180 set_entered_track (0);
1188 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1197 switch (item_type) {
1198 case ControlPointItem:
1199 cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
1200 if (cp->line().the_list()->interpolation() != AutomationList::Discrete) {
1201 if (cp->line().npoints() > 1 && !cp->selected()) {
1202 cp->set_visible (false);
1206 if (is_drawable()) {
1207 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1210 hide_verbose_canvas_cursor ();
1213 case RegionViewNameHighlight:
1214 case StartSelectionTrimItem:
1215 case EndSelectionTrimItem:
1216 case EditCursorItem:
1217 case PlayheadCursorItem:
1220 case ImageFrameHandleStartItem:
1221 case ImageFrameHandleEndItem:
1222 case MarkerViewHandleStartItem:
1223 case MarkerViewHandleEndItem:
1226 if (is_drawable()) {
1227 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1232 case AutomationLineItem:
1233 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1235 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1237 line->property_fill_color_rgba() = al->get_line_color();
1239 if (is_drawable()) {
1240 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1244 case RegionViewName:
1245 /* see enter_handler() for notes */
1246 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1247 if (is_drawable() && mouse_mode == MouseObject) {
1248 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1253 case RangeMarkerBarItem:
1254 case TransportMarkerBarItem:
1258 if (is_drawable()) {
1259 time_canvas.get_window()->set_cursor (*timebar_cursor);
1264 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1267 loc = find_location_from_marker (marker, is_start);
1268 if (loc) location_flags_changed (loc, this);
1270 case MeterMarkerItem:
1271 case TempoMarkerItem:
1273 if (is_drawable()) {
1274 time_canvas.get_window()->set_cursor (*timebar_cursor);
1279 case FadeInHandleItem:
1280 case FadeOutHandleItem:
1281 rv = static_cast<RegionView*>(item->get_data ("regionview"));
1283 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1285 rect->property_fill_color_rgba() = rv->get_fill_color();
1286 rect->property_outline_pixels() = 0;
1291 case AutomationTrackItem:
1292 if (is_drawable()) {
1293 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1294 clear_entered_track = true;
1295 Glib::signal_idle().connect (mem_fun(*this, &Editor::left_automation_track));
1307 Editor::left_automation_track ()
1309 if (clear_entered_track) {
1310 set_entered_track (0);
1311 clear_entered_track = false;
1317 Editor::motion_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type, bool from_autoscroll)
1321 /* We call this so that MOTION_NOTIFY events continue to be
1322 delivered to the canvas. We need to do this because we set
1323 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
1324 the density of the events, at the expense of a round-trip
1325 to the server. Given that this will mostly occur on cases
1326 where DISPLAY = :0.0, and given the cost of what the motion
1327 event might do, its a good tradeoff.
1330 track_canvas.get_pointer (x, y);
1332 if (current_stepping_trackview) {
1333 /* don't keep the persistent stepped trackview if the mouse moves */
1334 current_stepping_trackview = 0;
1335 step_timeout.disconnect ();
1338 if (session && session->actively_recording()) {
1339 /* Sorry. no dragging stuff around while we record */
1343 drag_info.item_type = item_type;
1344 drag_info.current_pointer_frame = event_frame (event, &drag_info.current_pointer_x,
1345 &drag_info.current_pointer_y);
1347 if (!from_autoscroll && drag_info.item) {
1348 /* item != 0 is the best test i can think of for dragging.
1350 if (!drag_info.move_threshold_passed) {
1352 bool x_threshold_passed = (abs ((nframes64_t) (drag_info.current_pointer_x - drag_info.grab_x)) > 4LL);
1353 bool y_threshold_passed = (abs ((nframes64_t) (drag_info.current_pointer_y - drag_info.grab_y)) > 4LL);
1355 drag_info.move_threshold_passed = (x_threshold_passed || y_threshold_passed);
1357 // and change the initial grab loc/frame if this drag info wants us to
1359 if (drag_info.want_move_threshold && drag_info.move_threshold_passed) {
1360 drag_info.grab_frame = drag_info.current_pointer_frame;
1361 drag_info.grab_x = drag_info.current_pointer_x;
1362 drag_info.grab_y = drag_info.current_pointer_y;
1363 drag_info.last_pointer_frame = drag_info.grab_frame;
1364 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
1369 switch (item_type) {
1370 case PlayheadCursorItem:
1371 case EditCursorItem:
1373 case ControlPointItem:
1374 case TempoMarkerItem:
1375 case MeterMarkerItem:
1376 case RegionViewNameHighlight:
1377 case StartSelectionTrimItem:
1378 case EndSelectionTrimItem:
1381 case AutomationLineItem:
1382 case FadeInHandleItem:
1383 case FadeOutHandleItem:
1386 case ImageFrameHandleStartItem:
1387 case ImageFrameHandleEndItem:
1388 case MarkerViewHandleStartItem:
1389 case MarkerViewHandleEndItem:
1392 if (drag_info.item && (event->motion.state & Gdk::BUTTON1_MASK ||
1393 (event->motion.state & Gdk::BUTTON2_MASK))) {
1394 if (!from_autoscroll) {
1395 maybe_autoscroll (event);
1397 (this->*(drag_info.motion_callback)) (item, event);
1406 switch (mouse_mode) {
1411 if (drag_info.item && (event->motion.state & GDK_BUTTON1_MASK ||
1412 (event->motion.state & GDK_BUTTON2_MASK))) {
1413 if (!from_autoscroll) {
1414 maybe_autoscroll (event);
1416 (this->*(drag_info.motion_callback)) (item, event);
1427 track_canvas_motion (event);
1428 // drag_info.last_pointer_frame = drag_info.current_pointer_frame;
1436 Editor::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
1438 if (drag_info.item == 0) {
1439 fatal << _("programming error: start_grab called without drag item") << endmsg;
1445 cursor = grabber_cursor;
1448 // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
1450 if (event->button.button == 2) {
1451 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Alt)) {
1452 drag_info.y_constrained = true;
1453 drag_info.x_constrained = false;
1455 drag_info.y_constrained = false;
1456 drag_info.x_constrained = true;
1459 drag_info.x_constrained = false;
1460 drag_info.y_constrained = false;
1463 drag_info.grab_frame = event_frame (event, &drag_info.grab_x, &drag_info.grab_y);
1464 drag_info.last_pointer_frame = drag_info.grab_frame;
1465 drag_info.current_pointer_frame = drag_info.grab_frame;
1466 drag_info.current_pointer_x = drag_info.grab_x;
1467 drag_info.current_pointer_y = drag_info.grab_y;
1468 drag_info.cumulative_x_drag = 0;
1469 drag_info.cumulative_y_drag = 0;
1470 drag_info.first_move = true;
1471 drag_info.move_threshold_passed = false;
1472 drag_info.want_move_threshold = false;
1473 drag_info.pointer_frame_offset = 0;
1474 drag_info.brushing = false;
1475 drag_info.copied_location = 0;
1477 drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK,
1479 event->button.time);
1481 if (session && session->transport_rolling()) {
1482 drag_info.was_rolling = true;
1484 drag_info.was_rolling = false;
1487 switch (snap_type) {
1488 case SnapToRegionStart:
1489 case SnapToRegionEnd:
1490 case SnapToRegionSync:
1491 case SnapToRegionBoundary:
1492 build_region_boundary_cache ();
1500 Editor::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t time)
1502 drag_info.item->ungrab (0);
1503 drag_info.item = new_item;
1506 cursor = grabber_cursor;
1509 drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK, *cursor, time);
1513 Editor::end_grab (ArdourCanvas::Item* item, GdkEvent* event)
1515 bool did_drag = false;
1517 stop_canvas_autoscroll ();
1519 if (drag_info.item == 0) {
1523 drag_info.item->ungrab (event->button.time);
1525 if (drag_info.finished_callback) {
1526 (this->*(drag_info.finished_callback)) (item, event);
1529 did_drag = !drag_info.first_move;
1531 hide_verbose_canvas_cursor();
1534 drag_info.copy = false;
1535 drag_info.motion_callback = 0;
1536 drag_info.finished_callback = 0;
1537 drag_info.last_trackview = 0;
1538 drag_info.last_frame_position = 0;
1539 drag_info.grab_frame = 0;
1540 drag_info.last_pointer_frame = 0;
1541 drag_info.current_pointer_frame = 0;
1542 drag_info.brushing = false;
1544 if (drag_info.copied_location) {
1545 delete drag_info.copied_location;
1546 drag_info.copied_location = 0;
1553 Editor::set_edit_cursor (GdkEvent* event)
1555 nframes_t pointer_frame = event_frame (event);
1557 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1558 if (snap_type != SnapToEditCursor) {
1559 snap_to (pointer_frame);
1563 edit_cursor->set_position (pointer_frame);
1564 edit_cursor_clock.set (pointer_frame);
1568 Editor::set_playhead_cursor (GdkEvent* event)
1570 nframes_t pointer_frame = event_frame (event);
1572 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1573 snap_to (pointer_frame);
1577 session->request_locate (pointer_frame, session->transport_rolling());
1582 Editor::start_fade_in_grab (ArdourCanvas::Item* item, GdkEvent* event)
1584 drag_info.item = item;
1585 drag_info.motion_callback = &Editor::fade_in_drag_motion_callback;
1586 drag_info.finished_callback = &Editor::fade_in_drag_finished_callback;
1590 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
1591 fatal << _("programming error: fade in canvas item has no regionview data pointer!") << endmsg;
1595 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1597 drag_info.pointer_frame_offset = drag_info.grab_frame - ((nframes_t) arv->audio_region()->fade_in()->back()->when + arv->region()->position());
1601 Editor::fade_in_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1603 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1605 nframes_t fade_length;
1607 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1608 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1614 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1618 if (pos < (arv->region()->position() + 64)) {
1619 fade_length = 64; // this should be a minimum defined somewhere
1620 } else if (pos > arv->region()->last_frame()) {
1621 fade_length = arv->region()->length();
1623 fade_length = pos - arv->region()->position();
1625 /* mapover the region selection */
1627 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1629 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1635 tmp->reset_fade_in_shape_width (fade_length);
1638 show_verbose_duration_cursor (arv->region()->position(), arv->region()->position() + fade_length, 10);
1640 drag_info.first_move = false;
1644 Editor::fade_in_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1646 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1648 nframes_t fade_length;
1650 if (drag_info.first_move) return;
1652 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1653 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1658 if (pos < (arv->region()->position() + 64)) {
1659 fade_length = 64; // this should be a minimum defined somewhere
1660 } else if (pos > arv->region()->last_frame()) {
1661 fade_length = arv->region()->length();
1663 fade_length = pos - arv->region()->position();
1666 begin_reversible_command (_("change fade in length"));
1668 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1670 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1676 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
1677 XMLNode &before = alist->get_state();
1679 tmp->audio_region()->set_fade_in_length (fade_length);
1681 XMLNode &after = alist->get_state();
1682 session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
1685 commit_reversible_command ();
1689 Editor::start_fade_out_grab (ArdourCanvas::Item* item, GdkEvent* event)
1691 drag_info.item = item;
1692 drag_info.motion_callback = &Editor::fade_out_drag_motion_callback;
1693 drag_info.finished_callback = &Editor::fade_out_drag_finished_callback;
1697 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
1698 fatal << _("programming error: fade out canvas item has no regionview data pointer!") << endmsg;
1702 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1704 drag_info.pointer_frame_offset = drag_info.grab_frame - (arv->region()->length() - (nframes_t) arv->audio_region()->fade_out()->back()->when + arv->region()->position());
1708 Editor::fade_out_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1710 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1712 nframes_t fade_length;
1714 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1715 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1720 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1724 if (pos > (arv->region()->last_frame() - 64)) {
1725 fade_length = 64; // this should really be a minimum fade defined somewhere
1727 else if (pos < arv->region()->position()) {
1728 fade_length = arv->region()->length();
1731 fade_length = arv->region()->last_frame() - pos;
1734 /* mapover the region selection */
1736 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1738 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1744 tmp->reset_fade_out_shape_width (fade_length);
1747 show_verbose_duration_cursor (arv->region()->last_frame() - fade_length, arv->region()->last_frame(), 10);
1749 drag_info.first_move = false;
1753 Editor::fade_out_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1755 if (drag_info.first_move) return;
1757 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1759 nframes_t fade_length;
1761 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1762 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1768 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1772 if (pos > (arv->region()->last_frame() - 64)) {
1773 fade_length = 64; // this should really be a minimum fade defined somewhere
1775 else if (pos < arv->region()->position()) {
1776 fade_length = arv->region()->length();
1779 fade_length = arv->region()->last_frame() - pos;
1782 begin_reversible_command (_("change fade out length"));
1784 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1786 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1792 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
1793 XMLNode &before = alist->get_state();
1795 tmp->audio_region()->set_fade_out_length (fade_length);
1797 XMLNode &after = alist->get_state();
1798 session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
1801 commit_reversible_command ();
1805 Editor::start_cursor_grab (ArdourCanvas::Item* item, GdkEvent* event)
1807 drag_info.item = item;
1808 drag_info.motion_callback = &Editor::cursor_drag_motion_callback;
1809 drag_info.finished_callback = &Editor::cursor_drag_finished_callback;
1813 if ((drag_info.data = (item->get_data ("cursor"))) == 0) {
1814 fatal << _("programming error: cursor canvas item has no cursor data pointer!") << endmsg;
1818 Cursor* cursor = (Cursor *) drag_info.data;
1820 if (cursor == playhead_cursor) {
1821 _dragging_playhead = true;
1823 if (session && drag_info.was_rolling) {
1824 session->request_stop ();
1827 if (session && session->is_auditioning()) {
1828 session->cancel_audition ();
1832 drag_info.pointer_frame_offset = drag_info.grab_frame - cursor->current_frame;
1834 show_verbose_time_cursor (cursor->current_frame, 10);
1838 Editor::cursor_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1840 Cursor* cursor = (Cursor *) drag_info.data;
1841 nframes_t adjusted_frame;
1843 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1844 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1850 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1851 if (cursor != edit_cursor || snap_type != SnapToEditCursor) {
1852 snap_to (adjusted_frame);
1856 if (adjusted_frame == drag_info.last_pointer_frame) return;
1858 cursor->set_position (adjusted_frame);
1860 if (cursor == edit_cursor) {
1861 edit_cursor_clock.set (cursor->current_frame);
1863 UpdateAllTransportClocks (cursor->current_frame);
1866 show_verbose_time_cursor (cursor->current_frame, 10);
1868 drag_info.last_pointer_frame = adjusted_frame;
1869 drag_info.first_move = false;
1873 Editor::cursor_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1875 if (drag_info.first_move) return;
1877 cursor_drag_motion_callback (item, event);
1879 _dragging_playhead = false;
1881 if (item == &playhead_cursor->canvas_item) {
1883 session->request_locate (playhead_cursor->current_frame, drag_info.was_rolling);
1885 } else if (item == &edit_cursor->canvas_item) {
1886 edit_cursor->set_position (edit_cursor->current_frame);
1887 edit_cursor_clock.set (edit_cursor->current_frame);
1892 Editor::update_marker_drag_item (Location *location)
1894 double x1 = frame_to_pixel (location->start());
1895 double x2 = frame_to_pixel (location->end());
1897 if (location->is_mark()) {
1898 marker_drag_line_points.front().set_x(x1);
1899 marker_drag_line_points.back().set_x(x1);
1900 marker_drag_line->property_points() = marker_drag_line_points;
1903 range_marker_drag_rect->property_x1() = x1;
1904 range_marker_drag_rect->property_x2() = x2;
1909 Editor::start_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
1913 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1914 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
1920 Location *location = find_location_from_marker (marker, is_start);
1922 drag_info.item = item;
1923 drag_info.data = marker;
1924 drag_info.motion_callback = &Editor::marker_drag_motion_callback;
1925 drag_info.finished_callback = &Editor::marker_drag_finished_callback;
1929 drag_info.copied_location = new Location (*location);
1930 drag_info.pointer_frame_offset = drag_info.grab_frame - (is_start ? location->start() : location->end());
1932 update_marker_drag_item (location);
1934 if (location->is_mark()) {
1935 marker_drag_line->show();
1936 marker_drag_line->raise_to_top();
1939 range_marker_drag_rect->show();
1940 range_marker_drag_rect->raise_to_top();
1943 if (is_start) show_verbose_time_cursor (location->start(), 10);
1944 else show_verbose_time_cursor (location->end(), 10);
1948 Editor::marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1951 Marker* marker = (Marker *) drag_info.data;
1952 Location *real_location;
1953 Location *copy_location;
1955 bool move_both = false;
1959 if (drag_info.pointer_frame_offset <= drag_info.current_pointer_frame) {
1960 newframe = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1965 nframes_t next = newframe;
1967 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1968 snap_to (newframe, 0, true);
1971 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
1975 /* call this to find out if its the start or end */
1977 real_location = find_location_from_marker (marker, is_start);
1979 /* use the copy that we're "dragging" around */
1981 copy_location = drag_info.copied_location;
1983 f_delta = copy_location->end() - copy_location->start();
1985 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
1989 if (copy_location->is_mark()) {
1992 copy_location->set_start (newframe);
1996 if (is_start) { // start-of-range marker
1999 copy_location->set_start (newframe);
2000 copy_location->set_end (newframe + f_delta);
2001 } else if (newframe < copy_location->end()) {
2002 copy_location->set_start (newframe);
2004 snap_to (next, 1, true);
2005 copy_location->set_end (next);
2006 copy_location->set_start (newframe);
2009 } else { // end marker
2012 copy_location->set_end (newframe);
2013 copy_location->set_start (newframe - f_delta);
2014 } else if (newframe > copy_location->start()) {
2015 copy_location->set_end (newframe);
2017 } else if (newframe > 0) {
2018 snap_to (next, -1, true);
2019 copy_location->set_start (next);
2020 copy_location->set_end (newframe);
2025 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
2026 drag_info.first_move = false;
2028 update_marker_drag_item (copy_location);
2030 LocationMarkers* lm = find_location_markers (real_location);
2031 lm->set_position (copy_location->start(), copy_location->end());
2033 show_verbose_time_cursor (newframe, 10);
2037 Editor::marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2039 if (drag_info.first_move) {
2040 marker_drag_motion_callback (item, event);
2044 Marker* marker = (Marker *) drag_info.data;
2048 begin_reversible_command ( _("move marker") );
2049 XMLNode &before = session->locations()->get_state();
2051 Location * location = find_location_from_marker (marker, is_start);
2054 if (location->is_mark()) {
2055 location->set_start (drag_info.copied_location->start());
2057 location->set (drag_info.copied_location->start(), drag_info.copied_location->end());
2061 XMLNode &after = session->locations()->get_state();
2062 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
2063 commit_reversible_command ();
2065 marker_drag_line->hide();
2066 range_marker_drag_rect->hide();
2070 Editor::start_meter_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2073 MeterMarker* meter_marker;
2075 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2076 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2080 meter_marker = dynamic_cast<MeterMarker*> (marker);
2082 MetricSection& section (meter_marker->meter());
2084 if (!section.movable()) {
2088 drag_info.item = item;
2089 drag_info.data = marker;
2090 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2091 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2095 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2097 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2101 Editor::start_meter_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2104 MeterMarker* meter_marker;
2106 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2107 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2111 meter_marker = dynamic_cast<MeterMarker*> (marker);
2113 // create a dummy marker for visual representation of moving the copy.
2114 // The actual copying is not done before we reach the finish callback.
2116 snprintf (name, sizeof(name), "%g/%g", meter_marker->meter().beats_per_bar(), meter_marker->meter().note_divisor ());
2117 MeterMarker* new_marker = new MeterMarker(*this, *meter_group, ARDOUR_UI::config()->canvasvar_MeterMarker.get(), name,
2118 *new MeterSection(meter_marker->meter()));
2120 drag_info.item = &new_marker->the_item();
2121 drag_info.copy = true;
2122 drag_info.data = new_marker;
2123 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2124 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2128 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2130 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2134 Editor::meter_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2136 MeterMarker* marker = (MeterMarker *) drag_info.data;
2137 nframes_t adjusted_frame;
2139 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2140 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2146 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2147 snap_to (adjusted_frame);
2150 if (adjusted_frame == drag_info.last_pointer_frame) return;
2152 marker->set_position (adjusted_frame);
2155 drag_info.last_pointer_frame = adjusted_frame;
2156 drag_info.first_move = false;
2158 show_verbose_time_cursor (adjusted_frame, 10);
2162 Editor::meter_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2164 if (drag_info.first_move) return;
2166 meter_marker_drag_motion_callback (drag_info.item, event);
2168 MeterMarker* marker = (MeterMarker *) drag_info.data;
2171 TempoMap& map (session->tempo_map());
2172 map.bbt_time (drag_info.last_pointer_frame, when);
2174 if (drag_info.copy == true) {
2175 begin_reversible_command (_("copy meter mark"));
2176 XMLNode &before = map.get_state();
2177 map.add_meter (marker->meter(), when);
2178 XMLNode &after = map.get_state();
2179 session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2180 commit_reversible_command ();
2182 // delete the dummy marker we used for visual representation of copying.
2183 // a new visual marker will show up automatically.
2186 begin_reversible_command (_("move meter mark"));
2187 XMLNode &before = map.get_state();
2188 map.move_meter (marker->meter(), when);
2189 XMLNode &after = map.get_state();
2190 session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2191 commit_reversible_command ();
2196 Editor::start_tempo_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2199 TempoMarker* tempo_marker;
2201 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2202 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2206 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2207 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2211 MetricSection& section (tempo_marker->tempo());
2213 if (!section.movable()) {
2217 drag_info.item = item;
2218 drag_info.data = marker;
2219 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2220 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2224 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2225 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2229 Editor::start_tempo_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2232 TempoMarker* tempo_marker;
2234 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2235 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2239 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2240 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2244 // create a dummy marker for visual representation of moving the copy.
2245 // The actual copying is not done before we reach the finish callback.
2247 snprintf (name, sizeof (name), "%.2f", tempo_marker->tempo().beats_per_minute());
2248 TempoMarker* new_marker = new TempoMarker(*this, *tempo_group, ARDOUR_UI::config()->canvasvar_TempoMarker.get(), name,
2249 *new TempoSection(tempo_marker->tempo()));
2251 drag_info.item = &new_marker->the_item();
2252 drag_info.copy = true;
2253 drag_info.data = new_marker;
2254 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2255 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2259 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2261 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2265 Editor::tempo_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2267 TempoMarker* marker = (TempoMarker *) drag_info.data;
2268 nframes_t adjusted_frame;
2270 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2271 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2277 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2278 snap_to (adjusted_frame);
2281 if (adjusted_frame == drag_info.last_pointer_frame) return;
2283 /* OK, we've moved far enough to make it worth actually move the thing. */
2285 marker->set_position (adjusted_frame);
2287 show_verbose_time_cursor (adjusted_frame, 10);
2289 drag_info.last_pointer_frame = adjusted_frame;
2290 drag_info.first_move = false;
2294 Editor::tempo_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2296 if (drag_info.first_move) return;
2298 tempo_marker_drag_motion_callback (drag_info.item, event);
2300 TempoMarker* marker = (TempoMarker *) drag_info.data;
2303 TempoMap& map (session->tempo_map());
2304 map.bbt_time (drag_info.last_pointer_frame, when);
2306 if (drag_info.copy == true) {
2307 begin_reversible_command (_("copy tempo mark"));
2308 XMLNode &before = map.get_state();
2309 map.add_tempo (marker->tempo(), when);
2310 XMLNode &after = map.get_state();
2311 session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2312 commit_reversible_command ();
2314 // delete the dummy marker we used for visual representation of copying.
2315 // a new visual marker will show up automatically.
2318 begin_reversible_command (_("move tempo mark"));
2319 XMLNode &before = map.get_state();
2320 map.move_tempo (marker->tempo(), when);
2321 XMLNode &after = map.get_state();
2322 session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2323 commit_reversible_command ();
2328 Editor::remove_gain_control_point (ArdourCanvas::Item*item, GdkEvent* event)
2330 ControlPoint* control_point;
2332 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2333 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2337 // We shouldn't remove the first or last gain point
2338 if (control_point->line().is_last_point(*control_point) ||
2339 control_point->line().is_first_point(*control_point)) {
2343 control_point->line().remove_point (*control_point);
2347 Editor::remove_control_point (ArdourCanvas::Item* item, GdkEvent* event)
2349 ControlPoint* control_point;
2351 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2352 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2356 control_point->line().remove_point (*control_point);
2360 Editor::start_control_point_grab (ArdourCanvas::Item* item, GdkEvent* event)
2362 ControlPoint* control_point;
2364 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2365 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2369 drag_info.item = item;
2370 drag_info.data = control_point;
2371 drag_info.motion_callback = &Editor::control_point_drag_motion_callback;
2372 drag_info.finished_callback = &Editor::control_point_drag_finished_callback;
2374 start_grab (event, fader_cursor);
2376 control_point->line().start_drag (control_point, drag_info.grab_frame, 0);
2378 double fraction = 1.0 - ((control_point->get_y() - control_point->line().y_position()) / (double)control_point->line().height());
2379 set_verbose_canvas_cursor (control_point->line().get_verbose_cursor_string (fraction),
2380 drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
2382 show_verbose_canvas_cursor ();
2386 Editor::control_point_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2388 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2390 double cx = drag_info.current_pointer_x;
2391 double cy = drag_info.current_pointer_y;
2393 drag_info.cumulative_x_drag = cx - drag_info.grab_x ;
2394 drag_info.cumulative_y_drag = cy - drag_info.grab_y ;
2396 if (drag_info.x_constrained) {
2397 cx = drag_info.grab_x;
2399 if (drag_info.y_constrained) {
2400 cy = drag_info.grab_y;
2403 cp->line().parent_group().w2i (cx, cy);
2407 cy = min ((double) (cp->line().y_position() + cp->line().height()), cy);
2409 //translate cx to frames
2410 nframes_t cx_frames = unit_to_frame (cx);
2412 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier()) && !drag_info.x_constrained) {
2413 snap_to (cx_frames);
2416 const double fraction = 1.0 - ((cy - cp->line().y_position()) / (double)cp->line().height());
2420 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
2426 cp->line().point_drag (*cp, cx_frames , fraction, push);
2428 set_verbose_canvas_cursor_text (cp->line().get_verbose_cursor_string (fraction));
2430 drag_info.first_move = false;
2434 Editor::control_point_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2436 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2438 if (drag_info.first_move) {
2442 if ((event->type == GDK_BUTTON_RELEASE) && (event->button.button == 1) && Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
2443 reset_point_selection ();
2447 control_point_drag_motion_callback (item, event);
2449 cp->line().end_drag (cp);
2453 Editor::start_line_grab_from_regionview (ArdourCanvas::Item* item, GdkEvent* event)
2455 switch (mouse_mode) {
2457 assert(dynamic_cast<AudioRegionView*>(clicked_regionview));
2458 start_line_grab (dynamic_cast<AudioRegionView*>(clicked_regionview)->get_gain_line(), event);
2466 Editor::start_line_grab_from_line (ArdourCanvas::Item* item, GdkEvent* event)
2470 if ((al = reinterpret_cast<AutomationLine*> (item->get_data ("line"))) == 0) {
2471 fatal << _("programming error: line canvas item has no line pointer!") << endmsg;
2475 start_line_grab (al, event);
2479 Editor::start_line_grab (AutomationLine* line, GdkEvent* event)
2483 nframes_t frame_within_region;
2485 /* need to get x coordinate in terms of parent (TimeAxisItemView)
2489 cx = event->button.x;
2490 cy = event->button.y;
2491 line->parent_group().w2i (cx, cy);
2492 frame_within_region = (nframes_t) floor (cx * frames_per_unit);
2494 if (!line->control_points_adjacent (frame_within_region, current_line_drag_info.before,
2495 current_line_drag_info.after)) {
2496 /* no adjacent points */
2500 drag_info.item = &line->grab_item();
2501 drag_info.data = line;
2502 drag_info.motion_callback = &Editor::line_drag_motion_callback;
2503 drag_info.finished_callback = &Editor::line_drag_finished_callback;
2505 start_grab (event, fader_cursor);
2507 const double fraction = 1.0 - ((cy - line->y_position()) / (double)line->height());
2509 line->start_drag (0, drag_info.grab_frame, fraction);
2511 set_verbose_canvas_cursor (line->get_verbose_cursor_string (fraction),
2512 drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
2513 show_verbose_canvas_cursor ();
2517 Editor::line_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2519 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
2520 double cx = drag_info.current_pointer_x;
2521 double cy = drag_info.current_pointer_y;
2523 line->parent_group().w2i (cx, cy);
2525 const double fraction = 1.0 - ((cy - line->y_position()) / (double)line->height());
2529 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
2535 line->line_drag (current_line_drag_info.before, current_line_drag_info.after, fraction, push);
2537 set_verbose_canvas_cursor_text (line->get_verbose_cursor_string (fraction));
2541 Editor::line_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2543 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
2544 line_drag_motion_callback (item, event);
2549 Editor::start_region_grab (ArdourCanvas::Item* item, GdkEvent* event)
2551 if (selection->regions.empty() || clicked_regionview == 0) {
2555 drag_info.copy = false;
2556 drag_info.item = item;
2557 drag_info.data = clicked_regionview;
2558 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2559 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2564 TimeAxisView* tvp = clicked_axisview;
2565 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2567 if (tv && tv->is_track()) {
2568 speed = tv->get_diskstream()->speed();
2571 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2572 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2573 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2574 // we want a move threshold
2575 drag_info.want_move_threshold = true;
2577 show_verbose_time_cursor (drag_info.last_frame_position, 10);
2579 begin_reversible_command (_("move region(s)"));
2583 Editor::start_region_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2585 if (selection->regions.empty() || clicked_regionview == 0) {
2589 drag_info.copy = true;
2590 drag_info.item = item;
2591 drag_info.data = clicked_regionview;
2595 TimeAxisView* tv = &clicked_regionview->get_time_axis_view();
2596 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
2599 if (rtv && rtv->is_track()) {
2600 speed = rtv->get_diskstream()->speed();
2603 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2604 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2605 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2606 // we want a move threshold
2607 drag_info.want_move_threshold = true;
2608 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2609 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2610 show_verbose_time_cursor (drag_info.last_frame_position, 10);
2614 Editor::start_region_brush_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;
2641 drag_info.brushing = true;
2643 begin_reversible_command (_("Drag region brush"));
2647 Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2651 RegionView* rv = reinterpret_cast<RegionView*> (drag_info.data);
2652 nframes_t pending_region_position = 0;
2653 int32_t pointer_y_span = 0, canvas_pointer_y_span = 0, original_pointer_order;
2654 int32_t visible_y_high = 0, visible_y_low = 512; //high meaning higher numbered.. not the height on the screen
2655 bool clamp_y_axis = false;
2656 vector<int32_t> height_list(512) ;
2657 vector<int32_t>::iterator j;
2659 if (drag_info.copy && drag_info.move_threshold_passed && drag_info.want_move_threshold) {
2661 drag_info.want_move_threshold = false; // don't copy again
2663 /* duplicate the region(s) */
2665 vector<RegionView*> new_regionviews;
2667 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
2673 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
2674 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
2677 nrv = new AudioRegionView (*arv);
2679 nrv = new MidiRegionView (*mrv);
2684 nrv->get_canvas_group()->show ();
2686 new_regionviews.push_back (nrv);
2689 if (new_regionviews.empty()) {
2693 /* reset selection to new regionviews */
2695 selection->set (new_regionviews);
2697 /* reset drag_info data to reflect the fact that we are dragging the copies */
2699 drag_info.data = new_regionviews.front();
2701 swap_grab (new_regionviews.front()->get_canvas_group (), 0, event->motion.time);
2704 /* Which trackview is this ? */
2706 TimeAxisView* tvp = trackview_by_y_position (drag_info.current_pointer_y);
2707 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2709 /* The region motion is only processed if the pointer is over
2713 if (!tv || !tv->is_track()) {
2714 /* To make sure we hide the verbose canvas cursor when the mouse is
2715 not held over a track.
2717 hide_verbose_canvas_cursor ();
2721 original_pointer_order = drag_info.last_trackview->order;
2723 /************************************************************
2725 ************************************************************/
2727 if (drag_info.brushing) {
2728 clamp_y_axis = true;
2733 if ((pointer_y_span = (drag_info.last_trackview->order - tv->order)) != 0) {
2735 int32_t children = 0, numtracks = 0;
2736 // XXX hard coding track limit, oh my, so very very bad
2737 bitset <1024> tracks (0x00);
2738 /* get a bitmask representing the visible tracks */
2740 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2741 TimeAxisView *tracklist_timeview;
2742 tracklist_timeview = (*i);
2743 RouteTimeAxisView* rtv2 = dynamic_cast<RouteTimeAxisView*>(tracklist_timeview);
2744 TimeAxisView::Children children_list;
2746 /* zeroes are audio tracks. ones are other types. */
2748 if (!rtv2->hidden()) {
2750 if (visible_y_high < rtv2->order) {
2751 visible_y_high = rtv2->order;
2753 if (visible_y_low > rtv2->order) {
2754 visible_y_low = rtv2->order;
2757 if (!rtv2->is_track()) {
2758 tracks = tracks |= (0x01 << rtv2->order);
2761 height_list[rtv2->order] = (*i)->height;
2763 if ((children_list = rtv2->get_child_list()).size() > 0) {
2764 for (TimeAxisView::Children::iterator j = children_list.begin(); j != children_list.end(); ++j) {
2765 tracks = tracks |= (0x01 << (rtv2->order + children));
2766 height_list[rtv2->order + children] = (*j)->height;
2774 /* find the actual span according to the canvas */
2776 canvas_pointer_y_span = pointer_y_span;
2777 if (drag_info.last_trackview->order >= tv->order) {
2779 for (y = tv->order; y < drag_info.last_trackview->order; y++) {
2780 if (height_list[y] == 0 ) {
2781 canvas_pointer_y_span--;
2786 for (y = drag_info.last_trackview->order;y <= tv->order; y++) {
2787 if ( height_list[y] == 0 ) {
2788 canvas_pointer_y_span++;
2793 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
2794 RegionView* rv2 = (*i);
2795 double ix1, ix2, iy1, iy2;
2798 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
2799 rv2->get_canvas_group()->i2w (ix1, iy1);
2800 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
2801 RouteTimeAxisView* rtv2 = dynamic_cast<RouteTimeAxisView*>(tvp2);
2803 if (rtv2->order != original_pointer_order) {
2804 /* this isn't the pointer track */
2806 if (canvas_pointer_y_span > 0) {
2808 /* moving up the canvas */
2809 if ((rtv2->order - canvas_pointer_y_span) >= visible_y_low) {
2811 int32_t visible_tracks = 0;
2812 while (visible_tracks < canvas_pointer_y_span ) {
2815 while (height_list[rtv2->order - (visible_tracks - n)] == 0) {
2816 /* we're passing through a hidden track */
2821 if (tracks[rtv2->order - (canvas_pointer_y_span - n)] != 0x00) {
2822 clamp_y_axis = true;
2826 clamp_y_axis = true;
2829 } else if (canvas_pointer_y_span < 0) {
2831 /*moving down the canvas*/
2833 if ((rtv2->order - (canvas_pointer_y_span - n)) <= visible_y_high) { // we will overflow
2836 int32_t visible_tracks = 0;
2838 while (visible_tracks > canvas_pointer_y_span ) {
2841 while (height_list[rtv2->order - (visible_tracks - n)] == 0) {
2845 if ( tracks[rtv2->order - ( canvas_pointer_y_span - n)] != 0x00) {
2846 clamp_y_axis = true;
2851 clamp_y_axis = true;
2857 /* this is the pointer's track */
2858 if ((rtv2->order - pointer_y_span) > visible_y_high) { // we will overflow
2859 clamp_y_axis = true;
2860 } else if ((rtv2->order - pointer_y_span) < visible_y_low) { // we will underflow
2861 clamp_y_axis = true;
2869 } else if (drag_info.last_trackview == tv) {
2870 clamp_y_axis = true;
2874 if (!clamp_y_axis) {
2875 drag_info.last_trackview = tv;
2878 /************************************************************
2880 ************************************************************/
2882 /* compute the amount of pointer motion in frames, and where
2883 the region would be if we moved it by that much.
2886 if (drag_info.move_threshold_passed) {
2888 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2890 nframes_t sync_frame;
2891 nframes_t sync_offset;
2894 pending_region_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2896 sync_offset = rv->region()->sync_offset (sync_dir);
2897 sync_frame = rv->region()->adjust_to_sync (pending_region_position);
2899 /* we snap if the snap modifier is not enabled.
2902 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2903 snap_to (sync_frame);
2906 if (sync_frame - sync_offset <= sync_frame) {
2907 pending_region_position = sync_frame - (sync_dir*sync_offset);
2909 pending_region_position = 0;
2913 pending_region_position = 0;
2916 if (pending_region_position > max_frames - rv->region()->length()) {
2917 pending_region_position = drag_info.last_frame_position;
2920 // printf ("3: pending_region_position= %lu %lu\n", pending_region_position, drag_info.last_frame_position );
2922 if (pending_region_position != drag_info.last_frame_position && !drag_info.x_constrained) {
2924 /* now compute the canvas unit distance we need to move the regiondrag_info.last_trackview->order
2925 to make it appear at the new location.
2928 if (pending_region_position > drag_info.last_frame_position) {
2929 x_delta = ((double) (pending_region_position - drag_info.last_frame_position) / frames_per_unit);
2931 x_delta = -((double) (drag_info.last_frame_position - pending_region_position) / frames_per_unit);
2934 drag_info.last_frame_position = pending_region_position;
2941 /* threshold not passed */
2946 /*************************************************************
2948 ************************************************************/
2950 if (x_delta == 0 && (pointer_y_span == 0)) {
2951 /* haven't reached next snap point, and we're not switching
2952 trackviews. nothing to do.
2959 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
2961 RegionView* rv2 = (*i);
2963 // If any regionview is at zero, we need to know so we can stop further leftward motion.
2965 double ix1, ix2, iy1, iy2;
2966 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
2967 rv2->get_canvas_group()->i2w (ix1, iy1);
2976 /*************************************************************
2978 ************************************************************/
2982 if (drag_info.first_move) {
2983 if (drag_info.move_threshold_passed) {
2994 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
2995 const list<RegionView*>& layered_regions = selection->regions.by_layer();
2997 for (list<RegionView*>::const_iterator i = layered_regions.begin(); i != layered_regions.end(); ++i) {
2999 RegionView* rv = (*i);
3000 double ix1, ix2, iy1, iy2;
3001 int32_t temp_pointer_y_span = pointer_y_span;
3003 /* get item BBox, which will be relative to parent. so we have
3004 to query on a child, then convert to world coordinates using
3008 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3009 rv->get_canvas_group()->i2w (ix1, iy1);
3010 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3011 RouteTimeAxisView* canvas_rtv = dynamic_cast<RouteTimeAxisView*>(tvp2);
3012 RouteTimeAxisView* temp_rtv;
3014 if ((pointer_y_span != 0) && !clamp_y_axis) {
3017 for (j = height_list.begin(); j!= height_list.end(); j++) {
3018 if (x == canvas_rtv->order) {
3019 /* we found the track the region is on */
3020 if (x != original_pointer_order) {
3021 /*this isn't from the same track we're dragging from */
3022 temp_pointer_y_span = canvas_pointer_y_span;
3024 while (temp_pointer_y_span > 0) {
3025 /* we're moving up canvas-wise,
3026 so we need to find the next track height
3028 if (j != height_list.begin()) {
3031 if (x != original_pointer_order) {
3032 /* we're not from the dragged track, so ignore hidden tracks. */
3034 temp_pointer_y_span++;
3038 temp_pointer_y_span--;
3040 while (temp_pointer_y_span < 0) {
3042 if (x != original_pointer_order) {
3044 temp_pointer_y_span--;
3048 if (j != height_list.end()) {
3051 temp_pointer_y_span++;
3053 /* find out where we'll be when we move and set height accordingly */
3055 tvp2 = trackview_by_y_position (iy1 + y_delta);
3056 temp_rtv = dynamic_cast<RouteTimeAxisView*>(tvp2);
3057 rv->set_y_position_and_height (0, temp_rtv->height);
3059 /* if you un-comment the following, the region colours will follow the track colours whilst dragging,
3060 personally, i think this can confuse things, but never mind.
3063 //const GdkColor& col (temp_rtv->view->get_region_color());
3064 //rv->set_color (const_cast<GdkColor&>(col));
3071 /* prevent the regionview from being moved to before
3072 the zero position on the canvas.
3077 if (-x_delta > ix1) {
3080 } else if ((x_delta > 0) && (rv->region()->last_frame() > max_frames - x_delta)) {
3081 x_delta = max_frames - rv->region()->last_frame();
3085 if (drag_info.first_move) {
3087 /* hide any dependent views */
3089 rv->get_time_axis_view().hide_dependent_views (*rv);
3091 /* this is subtle. raising the regionview itself won't help,
3092 because raise_to_top() just puts the item on the top of
3093 its parent's stack. so, we need to put the trackview canvas_display group
3094 on the top, since its parent is the whole canvas.
3097 rv->get_canvas_group()->raise_to_top();
3098 rv->get_time_axis_view().canvas_display->raise_to_top();
3099 cursor_group->raise_to_top();
3101 rv->fake_set_opaque (true);
3104 if (drag_info.brushing) {
3105 mouse_brush_insert_region (rv, pending_region_position);
3107 rv->move (x_delta, y_delta);
3110 } /* foreach region */
3114 if (drag_info.first_move && drag_info.move_threshold_passed) {
3115 cursor_group->raise_to_top();
3116 drag_info.first_move = false;
3119 if (x_delta != 0 && !drag_info.brushing) {
3120 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3125 Editor::region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3128 RegionView* rv = reinterpret_cast<RegionView *> (drag_info.data);
3129 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
3130 bool nocommit = true;
3132 RouteTimeAxisView* rtv;
3133 bool regionview_y_movement;
3134 bool regionview_x_movement;
3135 vector<RegionView*> copies;
3137 /* first_move is set to false if the regionview has been moved in the
3141 if (drag_info.first_move) {
3148 /* The regionview has been moved at some stage during the grab so we need
3149 to account for any mouse movement between this event and the last one.
3152 region_drag_motion_callback (item, event);
3154 if (drag_info.brushing) {
3155 /* all changes were made during motion event handlers */
3157 if (drag_info.copy) {
3158 for (list<RegionView*>::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
3159 copies.push_back (*i);
3166 /* adjust for track speed */
3169 rtv = dynamic_cast<RouteTimeAxisView*> (drag_info.last_trackview);
3170 if (rtv && rtv->get_diskstream()) {
3171 speed = rtv->get_diskstream()->speed();
3174 regionview_x_movement = (drag_info.last_frame_position != (nframes_t) (rv->region()->position()/speed));
3175 regionview_y_movement = (drag_info.last_trackview != &rv->get_time_axis_view());
3177 //printf ("last_frame: %s position is %lu %g\n", rv->get_time_axis_view().name().c_str(), drag_info.last_frame_position, speed);
3178 //printf ("last_rackview: %s \n", drag_info.last_trackview->name().c_str());
3182 if (drag_info.copy) {
3183 if (drag_info.x_constrained) {
3184 op_string = _("fixed time region copy");
3186 op_string = _("region copy");
3189 if (drag_info.x_constrained) {
3190 op_string = _("fixed time region drag");
3192 op_string = _("region drag");
3196 begin_reversible_command (op_string);
3198 if (regionview_y_movement) {
3200 /* moved to a different audio track. */
3202 vector<RegionView*> new_selection;
3204 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ) {
3206 RegionView* rv = (*i);
3208 double ix1, ix2, iy1, iy2;
3210 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3211 rv->get_canvas_group()->i2w (ix1, iy1);
3212 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3213 RouteTimeAxisView* rtv2 = dynamic_cast<RouteTimeAxisView*>(tvp2);
3215 boost::shared_ptr<Playlist> from_playlist = rv->region()->playlist();
3216 boost::shared_ptr<Playlist> to_playlist = rtv2->playlist();
3218 where = (nframes_t) (unit_to_frame (ix1) * speed);
3219 boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region()));
3221 /* undo the previous hide_dependent_views so that xfades don't
3222 disappear on copying regions
3225 rv->get_time_axis_view().reveal_dependent_views (*rv);
3227 if (!drag_info.copy) {
3229 /* the region that used to be in the old playlist is not
3230 moved to the new one - we make a copy of it. as a result,
3231 any existing editor for the region should no longer be
3235 rv->hide_region_editor();
3236 rv->fake_set_opaque (false);
3238 session->add_command (new MementoCommand<Playlist>(*from_playlist, &from_playlist->get_state(), 0));
3239 from_playlist->remove_region ((rv->region()));
3240 session->add_command (new MementoCommand<Playlist>(*from_playlist, 0, &from_playlist->get_state()));
3244 /* the regionview we dragged around is a temporary copy, queue it for deletion */
3246 copies.push_back (rv);
3249 latest_regionview = 0;
3251 sigc::connection c = rtv2->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3252 session->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
3253 to_playlist->add_region (new_region, where);
3254 session->add_command (new MementoCommand<Playlist>(*to_playlist, 0, &to_playlist->get_state()));
3257 if (latest_regionview) {
3258 new_selection.push_back (latest_regionview);
3261 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
3262 was selected in all of them, then removing it from the playlist will have removed all
3263 trace of it from the selection (i.e. there were N regions selected, we removed 1,
3264 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
3265 corresponding regionview, and the selection is now empty).
3267 this could have invalidated any and all iterators into the region selection.
3269 the heuristic we use here is: if the region selection is empty, break out of the loop
3270 here. if the region selection is not empty, then restart the loop because we know that
3271 we must have removed at least the region(view) we've just been working on as well as any
3272 that we processed on previous iterations.
3274 EXCEPT .... if we are doing a copy drag, then the selection hasn't been modified and
3275 we can just iterate.
3278 if (drag_info.copy) {
3281 if (selection->regions.empty()) {
3284 i = selection->regions.by_layer().begin();
3289 selection->set (new_selection);
3293 /* motion within a single track */
3295 list<RegionView*> regions = selection->regions.by_layer();
3297 if (drag_info.copy) {
3298 selection->clear_regions();
3301 for (list<RegionView*>::iterator i = regions.begin(); i != regions.end(); ++i) {
3305 if (!rv->region()->can_move()) {
3309 if (regionview_x_movement) {
3310 double ownspeed = 1.0;
3311 rtv = dynamic_cast<RouteTimeAxisView*> (&(rv->get_time_axis_view()));
3313 if (rtv && rtv->get_diskstream()) {
3314 ownspeed = rtv->get_diskstream()->speed();
3317 /* base the new region position on the current position of the regionview.*/
3319 double ix1, ix2, iy1, iy2;
3321 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3322 rv->get_canvas_group()->i2w (ix1, iy1);
3323 where = (nframes_t) (unit_to_frame (ix1) * ownspeed);
3327 where = rv->region()->position();
3330 boost::shared_ptr<Playlist> to_playlist = rv->region()->playlist();
3332 assert (to_playlist);
3336 session->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
3338 if (drag_info.copy) {
3340 boost::shared_ptr<Region> newregion;
3341 boost::shared_ptr<Region> ar;
3342 boost::shared_ptr<Region> mr;
3344 if ((ar = boost::dynamic_pointer_cast<AudioRegion>(rv->region())) != 0) {
3345 newregion = RegionFactory::create (ar);
3346 } else if ((mr = boost::dynamic_pointer_cast<MidiRegion>(rv->region())) != 0) {
3347 newregion = RegionFactory::create (mr);
3352 latest_regionview = 0;
3353 sigc::connection c = rtv->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3354 to_playlist->add_region (newregion, (nframes_t) (where * rtv->get_diskstream()->speed()));
3357 if (latest_regionview) {
3358 rtv->reveal_dependent_views (*latest_regionview);
3359 selection->add (latest_regionview);
3364 /* just change the model */
3366 rv->region()->set_position (where, (void*) this);
3372 session->add_command (new MementoCommand<Playlist>(*to_playlist, 0, &to_playlist->get_state()));
3374 if (drag_info.copy) {
3375 copies.push_back (rv);
3383 commit_reversible_command ();
3386 for (vector<RegionView*>::iterator x = copies.begin(); x != copies.end(); ++x) {
3392 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
3394 /* Either add to or set the set the region selection, unless
3395 this is an alignment click (control used)
3398 if (Keyboard::modifier_state_contains (event->state, Keyboard::Control)) {
3399 TimeAxisView* tv = &rv.get_time_axis_view();
3400 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
3402 if (rtv && rtv->is_track()) {
3403 speed = rtv->get_diskstream()->speed();
3406 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Alt))) {
3408 align_region (rv.region(), SyncPoint, (nframes_t) (edit_cursor->current_frame * speed));
3410 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Shift))) {
3412 align_region (rv.region(), End, (nframes_t) (edit_cursor->current_frame * speed));
3416 align_region (rv.region(), Start, (nframes_t) (edit_cursor->current_frame * speed));
3422 Editor::show_verbose_time_cursor (nframes_t frame, double offset, double xpos, double ypos)
3428 nframes_t frame_rate;
3435 switch (Profile->get_small_screen() ? ARDOUR_UI::instance()->primary_clock.mode () : ARDOUR_UI::instance()->secondary_clock.mode ()) {
3436 case AudioClock::BBT:
3437 session->bbt_time (frame, bbt);
3438 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, bbt.bars, bbt.beats, bbt.ticks);
3441 case AudioClock::SMPTE:
3442 session->smpte_time (frame, smpte);
3443 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
3446 case AudioClock::MinSec:
3447 /* XXX this is copied from show_verbose_duration_cursor() */
3448 frame_rate = session->frame_rate();
3449 hours = frame / (frame_rate * 3600);
3450 frame = frame % (frame_rate * 3600);
3451 mins = frame / (frame_rate * 60);
3452 frame = frame % (frame_rate * 60);
3453 secs = (float) frame / (float) frame_rate;
3454 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
3458 snprintf (buf, sizeof(buf), "%u", frame);
3462 if (xpos >= 0 && ypos >=0) {
3463 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
3466 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
3468 show_verbose_canvas_cursor ();
3472 Editor::show_verbose_duration_cursor (nframes_t start, nframes_t end, double offset, double xpos, double ypos)
3479 nframes_t distance, frame_rate;
3481 Meter meter_at_start(session->tempo_map().meter_at(start));
3487 switch (ARDOUR_UI::instance()->secondary_clock.mode ()) {
3488 case AudioClock::BBT:
3489 session->bbt_time (start, sbbt);
3490 session->bbt_time (end, ebbt);
3493 /* XXX this computation won't work well if the
3494 user makes a selection that spans any meter changes.
3497 ebbt.bars -= sbbt.bars;
3498 if (ebbt.beats >= sbbt.beats) {
3499 ebbt.beats -= sbbt.beats;
3502 ebbt.beats = int(meter_at_start.beats_per_bar()) + ebbt.beats - sbbt.beats;
3504 if (ebbt.ticks >= sbbt.ticks) {
3505 ebbt.ticks -= sbbt.ticks;
3508 ebbt.ticks = int(Meter::ticks_per_beat) + ebbt.ticks - sbbt.ticks;
3511 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, ebbt.bars, ebbt.beats, ebbt.ticks);
3514 case AudioClock::SMPTE:
3515 session->smpte_duration (end - start, smpte);
3516 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
3519 case AudioClock::MinSec:
3520 /* XXX this stuff should be elsewhere.. */
3521 distance = end - start;
3522 frame_rate = session->frame_rate();
3523 hours = distance / (frame_rate * 3600);
3524 distance = distance % (frame_rate * 3600);
3525 mins = distance / (frame_rate * 60);
3526 distance = distance % (frame_rate * 60);
3527 secs = (float) distance / (float) frame_rate;
3528 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
3532 snprintf (buf, sizeof(buf), "%u", end - start);
3536 if (xpos >= 0 && ypos >=0) {
3537 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
3540 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
3542 show_verbose_canvas_cursor ();
3546 Editor::collect_new_region_view (RegionView* rv)
3548 latest_regionview = rv;
3552 Editor::start_selection_grab (ArdourCanvas::Item* item, GdkEvent* event)
3554 if (clicked_regionview == 0) {
3558 /* lets try to create new Region for the selection */
3560 vector<boost::shared_ptr<AudioRegion> > new_regions;
3561 create_region_from_selection (new_regions);
3563 if (new_regions.empty()) {
3567 /* XXX fix me one day to use all new regions */
3569 boost::shared_ptr<Region> region (new_regions.front());
3571 /* add it to the current stream/playlist.
3573 tricky: the streamview for the track will add a new regionview. we will
3574 catch the signal it sends when it creates the regionview to
3575 set the regionview we want to then drag.
3578 latest_regionview = 0;
3579 sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3581 /* A selection grab currently creates two undo/redo operations, one for
3582 creating the new region and another for moving it.
3585 begin_reversible_command (_("selection grab"));
3587 boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
3589 XMLNode *before = &(playlist->get_state());
3590 clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
3591 XMLNode *after = &(playlist->get_state());
3592 session->add_command(new MementoCommand<Playlist>(*playlist, before, after));
3594 commit_reversible_command ();
3598 if (latest_regionview == 0) {
3599 /* something went wrong */
3603 /* we need to deselect all other regionviews, and select this one
3604 i'm ignoring undo stuff, because the region creation will take care of it */
3605 selection->set (latest_regionview);
3607 drag_info.item = latest_regionview->get_canvas_group();
3608 drag_info.data = latest_regionview;
3609 drag_info.motion_callback = &Editor::region_drag_motion_callback;
3610 drag_info.finished_callback = &Editor::region_drag_finished_callback;
3614 drag_info.last_trackview = clicked_axisview;
3615 drag_info.last_frame_position = latest_regionview->region()->position();
3616 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
3618 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3622 Editor::cancel_selection ()
3624 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
3625 (*i)->hide_selection ();
3627 begin_reversible_command (_("cancel selection"));
3628 selection->clear ();
3629 clicked_selection = 0;
3630 commit_reversible_command ();
3634 Editor::start_selection_op (ArdourCanvas::Item* item, GdkEvent* event, SelectionOp op)
3636 nframes_t start = 0;
3643 drag_info.item = item;
3644 drag_info.motion_callback = &Editor::drag_selection;
3645 drag_info.finished_callback = &Editor::end_selection_op;
3650 case CreateSelection:
3651 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
3652 drag_info.copy = true;
3654 drag_info.copy = false;
3656 start_grab (event, selector_cursor);
3659 case SelectionStartTrim:
3660 if (clicked_axisview) {
3661 clicked_axisview->order_selection_trims (item, true);
3663 start_grab (event, trimmer_cursor);
3664 start = selection->time[clicked_selection].start;
3665 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
3668 case SelectionEndTrim:
3669 if (clicked_axisview) {
3670 clicked_axisview->order_selection_trims (item, false);
3672 start_grab (event, trimmer_cursor);
3673 end = selection->time[clicked_selection].end;
3674 drag_info.pointer_frame_offset = drag_info.grab_frame - end;
3678 start = selection->time[clicked_selection].start;
3680 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
3684 if (selection_op == SelectionMove) {
3685 show_verbose_time_cursor(start, 10);
3687 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
3692 Editor::drag_selection (ArdourCanvas::Item* item, GdkEvent* event)
3694 nframes_t start = 0;
3697 nframes_t pending_position;
3699 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
3700 pending_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
3702 pending_position = 0;
3705 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3706 snap_to (pending_position);
3709 /* only alter selection if the current frame is
3710 different from the last frame position (adjusted)
3713 if (pending_position == drag_info.last_pointer_frame) return;
3715 switch (selection_op) {
3716 case CreateSelection:
3718 if (drag_info.first_move) {
3719 snap_to (drag_info.grab_frame);
3722 if (pending_position < drag_info.grab_frame) {
3723 start = pending_position;
3724 end = drag_info.grab_frame;
3726 end = pending_position;
3727 start = drag_info.grab_frame;
3730 /* first drag: Either add to the selection
3731 or create a new selection->
3734 if (drag_info.first_move) {
3736 begin_reversible_command (_("range selection"));
3738 if (drag_info.copy) {
3739 /* adding to the selection */
3740 clicked_selection = selection->add (start, end);
3741 drag_info.copy = false;
3743 /* new selection-> */
3744 clicked_selection = selection->set (clicked_axisview, start, end);
3749 case SelectionStartTrim:
3751 if (drag_info.first_move) {
3752 begin_reversible_command (_("trim selection start"));
3755 start = selection->time[clicked_selection].start;
3756 end = selection->time[clicked_selection].end;
3758 if (pending_position > end) {
3761 start = pending_position;
3765 case SelectionEndTrim:
3767 if (drag_info.first_move) {
3768 begin_reversible_command (_("trim selection end"));
3771 start = selection->time[clicked_selection].start;
3772 end = selection->time[clicked_selection].end;
3774 if (pending_position < start) {
3777 end = pending_position;
3784 if (drag_info.first_move) {
3785 begin_reversible_command (_("move selection"));
3788 start = selection->time[clicked_selection].start;
3789 end = selection->time[clicked_selection].end;
3791 length = end - start;
3793 start = pending_position;
3796 end = start + length;
3801 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
3802 start_canvas_autoscroll (1);
3806 selection->replace (clicked_selection, start, end);
3809 drag_info.last_pointer_frame = pending_position;
3810 drag_info.first_move = false;
3812 if (selection_op == SelectionMove) {
3813 show_verbose_time_cursor(start, 10);
3815 show_verbose_time_cursor(pending_position, 10);
3820 Editor::end_selection_op (ArdourCanvas::Item* item, GdkEvent* event)
3822 if (!drag_info.first_move) {
3823 drag_selection (item, event);
3824 /* XXX this is not object-oriented programming at all. ick */
3825 if (selection->time.consolidate()) {
3826 selection->TimeChanged ();
3828 commit_reversible_command ();
3830 /* just a click, no pointer movement.*/
3832 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
3834 selection->clear_time();
3839 /* XXX what happens if its a music selection? */
3840 session->set_audio_range (selection->time);
3841 stop_canvas_autoscroll ();
3845 Editor::start_trim (ArdourCanvas::Item* item, GdkEvent* event)
3848 TimeAxisView* tvp = clicked_axisview;
3849 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
3851 if (tv && tv->is_track()) {
3852 speed = tv->get_diskstream()->speed();
3855 nframes_t region_start = (nframes_t) (clicked_regionview->region()->position() / speed);
3856 nframes_t region_end = (nframes_t) (clicked_regionview->region()->last_frame() / speed);
3857 nframes_t region_length = (nframes_t) (clicked_regionview->region()->length() / speed);
3859 //drag_info.item = clicked_regionview->get_name_highlight();
3860 drag_info.item = item;
3861 drag_info.motion_callback = &Editor::trim_motion_callback;
3862 drag_info.finished_callback = &Editor::trim_finished_callback;
3864 start_grab (event, trimmer_cursor);
3866 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
3867 trim_op = ContentsTrim;
3869 /* These will get overridden for a point trim.*/
3870 if (drag_info.current_pointer_frame < (region_start + region_length/2)) {
3871 /* closer to start */
3872 trim_op = StartTrim;
3873 } else if (drag_info.current_pointer_frame > (region_end - region_length/2)) {
3881 show_verbose_time_cursor(region_start, 10);
3884 show_verbose_time_cursor(region_end, 10);
3887 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
3893 Editor::trim_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
3895 RegionView* rv = clicked_regionview;
3896 nframes_t frame_delta = 0;
3897 bool left_direction;
3898 bool obey_snap = !Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier());
3900 /* snap modifier works differently here..
3901 its' current state has to be passed to the
3902 various trim functions in order to work properly
3906 TimeAxisView* tvp = clicked_axisview;
3907 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
3908 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
3910 if (tv && tv->is_track()) {
3911 speed = tv->get_diskstream()->speed();
3914 if (drag_info.last_pointer_frame > drag_info.current_pointer_frame) {
3915 left_direction = true;
3917 left_direction = false;
3921 snap_to (drag_info.current_pointer_frame);
3924 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
3928 if (drag_info.first_move) {
3934 trim_type = "Region start trim";
3937 trim_type = "Region end trim";
3940 trim_type = "Region content trim";
3944 begin_reversible_command (trim_type);
3946 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3947 (*i)->fake_set_opaque(false);
3948 (*i)->region()->freeze ();
3950 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
3952 arv->temporarily_hide_envelope ();
3954 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
3955 insert_result = motion_frozen_playlists.insert (pl);
3956 if (insert_result.second) {
3957 session->add_command(new MementoCommand<Playlist>(*pl, &pl->get_state(), 0));
3962 if (left_direction) {
3963 frame_delta = (drag_info.last_pointer_frame - drag_info.current_pointer_frame);
3965 frame_delta = (drag_info.current_pointer_frame - drag_info.last_pointer_frame);
3970 if ((left_direction == false) && (drag_info.current_pointer_frame <= rv->region()->first_frame()/speed)) {
3973 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3974 single_start_trim (**i, frame_delta, left_direction, obey_snap);
3980 if ((left_direction == true) && (drag_info.current_pointer_frame > (nframes_t) (rv->region()->last_frame()/speed))) {
3983 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3984 single_end_trim (**i, frame_delta, left_direction, obey_snap);
3991 bool swap_direction = false;
3993 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
3994 swap_direction = true;
3997 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
3998 i != selection->regions.by_layer().end(); ++i)
4000 single_contents_trim (**i, frame_delta, left_direction, swap_direction, obey_snap);
4008 show_verbose_time_cursor((nframes_t) (rv->region()->position()/speed), 10);
4011 show_verbose_time_cursor((nframes_t) (rv->region()->last_frame()/speed), 10);
4014 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4018 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4019 drag_info.first_move = false;
4023 Editor::single_contents_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool swap_direction, bool obey_snap)
4025 boost::shared_ptr<Region> region (rv.region());
4027 if (region->locked()) {
4031 nframes_t new_bound;
4034 TimeAxisView* tvp = clicked_axisview;
4035 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4037 if (tv && tv->is_track()) {
4038 speed = tv->get_diskstream()->speed();
4041 if (left_direction) {
4042 if (swap_direction) {
4043 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4045 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4048 if (swap_direction) {
4049 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4051 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4056 snap_to (new_bound);
4058 region->trim_start ((nframes_t) (new_bound * speed), this);
4059 rv.region_changed (StartChanged);
4063 Editor::single_start_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool obey_snap)
4065 boost::shared_ptr<Region> region (rv.region());
4067 if (region->locked()) {
4071 nframes_t new_bound;
4074 TimeAxisView* tvp = clicked_axisview;
4075 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4077 if (tv && tv->is_track()) {
4078 speed = tv->get_diskstream()->speed();
4081 if (left_direction) {
4082 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4084 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4088 snap_to (new_bound, (left_direction ? 0 : 1));
4091 region->trim_front ((nframes_t) (new_bound * speed), this);
4093 rv.region_changed (Change (LengthChanged|PositionChanged|StartChanged));
4097 Editor::single_end_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool obey_snap)
4099 boost::shared_ptr<Region> region (rv.region());
4101 if (region->locked()) {
4105 nframes_t new_bound;
4108 TimeAxisView* tvp = clicked_axisview;
4109 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4111 if (tv && tv->is_track()) {
4112 speed = tv->get_diskstream()->speed();
4115 if (left_direction) {
4116 new_bound = (nframes_t) ((region->last_frame() + 1)/speed) - frame_delta;
4118 new_bound = (nframes_t) ((region->last_frame() + 1)/speed) + frame_delta;
4122 snap_to (new_bound);
4124 region->trim_end ((nframes_t) (new_bound * speed), this);
4125 rv.region_changed (LengthChanged);
4129 Editor::trim_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
4131 if (!drag_info.first_move) {
4132 trim_motion_callback (item, event);
4134 if (!clicked_regionview->get_selected()) {
4135 thaw_region_after_trim (*clicked_regionview);
4138 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4139 i != selection->regions.by_layer().end(); ++i)
4141 thaw_region_after_trim (**i);
4142 (*i)->fake_set_opaque (true);
4146 for (set<boost::shared_ptr<Playlist> >::iterator p = motion_frozen_playlists.begin(); p != motion_frozen_playlists.end(); ++p) {
4148 session->add_command (new MementoCommand<Playlist>(*(*p).get(), 0, &(*p)->get_state()));
4151 motion_frozen_playlists.clear ();
4153 commit_reversible_command();
4155 /* no mouse movement */
4161 Editor::point_trim (GdkEvent* event)
4163 RegionView* rv = clicked_regionview;
4164 nframes_t new_bound = drag_info.current_pointer_frame;
4166 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4167 snap_to (new_bound);
4170 /* Choose action dependant on which button was pressed */
4171 switch (event->button.button) {
4173 trim_op = StartTrim;
4174 begin_reversible_command (_("Start point trim"));
4176 if (rv->get_selected()) {
4178 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4179 i != selection->regions.by_layer().end(); ++i)
4181 if (!(*i)->region()->locked()) {
4182 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4183 XMLNode &before = pl->get_state();
4184 (*i)->region()->trim_front (new_bound, this);
4185 XMLNode &after = pl->get_state();
4186 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4192 if (!rv->region()->locked()) {
4193 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
4194 XMLNode &before = pl->get_state();
4195 rv->region()->trim_front (new_bound, this);
4196 XMLNode &after = pl->get_state();
4197 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4201 commit_reversible_command();
4206 begin_reversible_command (_("End point trim"));
4208 if (rv->get_selected()) {
4210 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
4212 if (!(*i)->region()->locked()) {
4213 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4214 XMLNode &before = pl->get_state();
4215 (*i)->region()->trim_end (new_bound, this);
4216 XMLNode &after = pl->get_state();
4217 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4223 if (!rv->region()->locked()) {
4224 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
4225 XMLNode &before = pl->get_state();
4226 rv->region()->trim_end (new_bound, this);
4227 XMLNode &after = pl->get_state();
4228 session->add_command (new MementoCommand<Playlist>(*pl.get(), &before, &after));
4232 commit_reversible_command();
4241 Editor::thaw_region_after_trim (RegionView& rv)
4243 boost::shared_ptr<Region> region (rv.region());
4245 if (region->locked()) {
4249 region->thaw (_("trimmed region"));
4250 XMLNode &after = region->playlist()->get_state();
4251 session->add_command (new MementoCommand<Playlist>(*(region->playlist()), 0, &after));
4253 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(&rv);
4255 arv->unhide_envelope ();
4259 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* event)
4264 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
4265 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
4269 Location* location = find_location_from_marker (marker, is_start);
4270 location->set_hidden (true, this);
4275 Editor::start_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event, RangeMarkerOp op)
4281 drag_info.item = item;
4282 drag_info.motion_callback = &Editor::drag_range_markerbar_op;
4283 drag_info.finished_callback = &Editor::end_range_markerbar_op;
4285 range_marker_op = op;
4287 if (!temp_location) {
4288 temp_location = new Location;
4292 case CreateRangeMarker:
4293 case CreateTransportMarker:
4295 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
4296 drag_info.copy = true;
4298 drag_info.copy = false;
4300 start_grab (event, selector_cursor);
4304 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4309 Editor::drag_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4311 nframes_t start = 0;
4313 ArdourCanvas::SimpleRect *crect = (range_marker_op == CreateRangeMarker) ? range_bar_drag_rect: transport_bar_drag_rect;
4315 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4316 snap_to (drag_info.current_pointer_frame);
4319 /* only alter selection if the current frame is
4320 different from the last frame position.
4323 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4325 switch (range_marker_op) {
4326 case CreateRangeMarker:
4327 case CreateTransportMarker:
4328 if (drag_info.first_move) {
4329 snap_to (drag_info.grab_frame);
4332 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4333 start = drag_info.current_pointer_frame;
4334 end = drag_info.grab_frame;
4336 end = drag_info.current_pointer_frame;
4337 start = drag_info.grab_frame;
4340 /* first drag: Either add to the selection
4341 or create a new selection.
4344 if (drag_info.first_move) {
4346 temp_location->set (start, end);
4350 update_marker_drag_item (temp_location);
4351 range_marker_drag_rect->show();
4352 range_marker_drag_rect->raise_to_top();
4358 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
4359 start_canvas_autoscroll (1);
4363 temp_location->set (start, end);
4365 double x1 = frame_to_pixel (start);
4366 double x2 = frame_to_pixel (end);
4367 crect->property_x1() = x1;
4368 crect->property_x2() = x2;
4370 update_marker_drag_item (temp_location);
4373 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4374 drag_info.first_move = false;
4376 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4381 Editor::end_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4383 Location * newloc = 0;
4386 if (!drag_info.first_move) {
4387 drag_range_markerbar_op (item, event);
4389 switch (range_marker_op) {
4390 case CreateRangeMarker:
4392 begin_reversible_command (_("new range marker"));
4393 XMLNode &before = session->locations()->get_state();
4394 session->locations()->next_available_name(rangename,"unnamed");
4395 newloc = new Location(temp_location->start(), temp_location->end(), rangename, Location::IsRangeMarker);
4396 session->locations()->add (newloc, true);
4397 XMLNode &after = session->locations()->get_state();
4398 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
4399 commit_reversible_command ();
4401 range_bar_drag_rect->hide();
4402 range_marker_drag_rect->hide();
4406 case CreateTransportMarker:
4407 // popup menu to pick loop or punch
4408 new_transport_marker_context_menu (&event->button, item);
4413 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
4415 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
4420 start = session->locations()->first_mark_before (drag_info.grab_frame);
4421 end = session->locations()->first_mark_after (drag_info.grab_frame);
4423 if (end == max_frames) {
4424 end = session->current_end_frame ();
4428 start = session->current_start_frame ();
4431 switch (mouse_mode) {
4433 /* find the two markers on either side and then make the selection from it */
4434 select_all_within (start, end, 0.0f, FLT_MAX, track_views, Selection::Set);
4438 /* find the two markers on either side of the click and make the range out of it */
4439 selection->set (0, start, end);
4448 stop_canvas_autoscroll ();
4454 Editor::start_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4456 drag_info.item = item;
4457 drag_info.motion_callback = &Editor::drag_mouse_zoom;
4458 drag_info.finished_callback = &Editor::end_mouse_zoom;
4460 start_grab (event, zoom_cursor);
4462 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4466 Editor::drag_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4471 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4472 snap_to (drag_info.current_pointer_frame);
4474 if (drag_info.first_move) {
4475 snap_to (drag_info.grab_frame);
4479 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4481 /* base start and end on initial click position */
4482 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4483 start = drag_info.current_pointer_frame;
4484 end = drag_info.grab_frame;
4486 end = drag_info.current_pointer_frame;
4487 start = drag_info.grab_frame;
4492 if (drag_info.first_move) {
4494 zoom_rect->raise_to_top();
4497 reposition_zoom_rect(start, end);
4499 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4500 drag_info.first_move = false;
4502 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4507 Editor::end_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4509 if (!drag_info.first_move) {
4510 drag_mouse_zoom (item, event);
4512 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
4513 temporal_zoom_by_frame (drag_info.grab_frame, drag_info.last_pointer_frame, "mouse zoom");
4515 temporal_zoom_by_frame (drag_info.last_pointer_frame, drag_info.grab_frame, "mouse zoom");
4518 temporal_zoom_to_frame (false, drag_info.grab_frame);
4520 temporal_zoom_step (false);
4521 center_screen (drag_info.grab_frame);
4529 Editor::reposition_zoom_rect (nframes_t start, nframes_t end)
4531 double x1 = frame_to_pixel (start);
4532 double x2 = frame_to_pixel (end);
4533 double y2 = full_canvas_height - 1.0;
4535 zoom_rect->property_x1() = x1;
4536 zoom_rect->property_y1() = 1.0;
4537 zoom_rect->property_x2() = x2;
4538 zoom_rect->property_y2() = y2;
4542 Editor::start_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4544 drag_info.item = item;
4545 drag_info.motion_callback = &Editor::drag_rubberband_select;
4546 drag_info.finished_callback = &Editor::end_rubberband_select;
4548 start_grab (event, cross_hair_cursor);
4550 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4554 Editor::drag_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4561 /* use a bigger drag threshold than the default */
4563 if (abs ((int) (drag_info.current_pointer_frame - drag_info.grab_frame)) < 8) {
4567 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4568 if (drag_info.first_move) {
4569 snap_to (drag_info.grab_frame);
4571 snap_to (drag_info.current_pointer_frame);
4574 /* base start and end on initial click position */
4576 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4577 start = drag_info.current_pointer_frame;
4578 end = drag_info.grab_frame;
4580 end = drag_info.current_pointer_frame;
4581 start = drag_info.grab_frame;
4584 if (drag_info.current_pointer_y < drag_info.grab_y) {
4585 y1 = drag_info.current_pointer_y;
4586 y2 = drag_info.grab_y;
4588 y2 = drag_info.current_pointer_y;
4589 y1 = drag_info.grab_y;
4593 if (start != end || y1 != y2) {
4595 double x1 = frame_to_pixel (start);
4596 double x2 = frame_to_pixel (end);
4598 rubberband_rect->property_x1() = x1;
4599 rubberband_rect->property_y1() = y1;
4600 rubberband_rect->property_x2() = x2;
4601 rubberband_rect->property_y2() = y2;
4603 rubberband_rect->show();
4604 rubberband_rect->raise_to_top();
4606 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4607 drag_info.first_move = false;
4609 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4614 Editor::end_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4616 if (!drag_info.first_move) {
4618 drag_rubberband_select (item, event);
4621 if (drag_info.current_pointer_y < drag_info.grab_y) {
4622 y1 = drag_info.current_pointer_y;
4623 y2 = drag_info.grab_y;
4626 y2 = drag_info.current_pointer_y;
4627 y1 = drag_info.grab_y;
4631 Selection::Operation op = Keyboard::selection_type (event->button.state);
4634 begin_reversible_command (_("rubberband selection"));
4636 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
4637 commit = select_all_within (drag_info.grab_frame, drag_info.last_pointer_frame, y1, y2, track_views, op);
4639 commit = select_all_within (drag_info.last_pointer_frame, drag_info.grab_frame, y1, y2, track_views, op);
4643 commit_reversible_command ();
4647 selection->clear_tracks();
4648 selection->clear_regions();
4649 selection->clear_points ();
4650 selection->clear_lines ();
4653 rubberband_rect->hide();
4658 Editor::mouse_rename_region (ArdourCanvas::Item* item, GdkEvent* event)
4660 using namespace Gtkmm2ext;
4662 ArdourPrompter prompter (false);
4664 prompter.set_prompt (_("Name for region:"));
4665 prompter.set_initial_text (clicked_regionview->region()->name());
4666 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
4667 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
4668 prompter.show_all ();
4669 switch (prompter.run ()) {
4670 case Gtk::RESPONSE_ACCEPT:
4672 prompter.get_result(str);
4674 clicked_regionview->region()->set_name (str);
4682 Editor::start_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
4684 drag_info.item = item;
4685 drag_info.motion_callback = &Editor::time_fx_motion;
4686 drag_info.finished_callback = &Editor::end_time_fx;
4690 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4694 Editor::time_fx_motion (ArdourCanvas::Item *item, GdkEvent* event)
4696 RegionView* rv = clicked_regionview;
4698 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4699 snap_to (drag_info.current_pointer_frame);
4702 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
4706 if (drag_info.current_pointer_frame > rv->region()->position()) {
4707 rv->get_time_axis_view().show_timestretch (rv->region()->position(), drag_info.current_pointer_frame);
4710 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4711 drag_info.first_move = false;
4713 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4717 Editor::end_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
4719 clicked_regionview->get_time_axis_view().hide_timestretch ();
4721 if (drag_info.first_move) {
4725 nframes_t newlen = drag_info.last_pointer_frame - clicked_regionview->region()->position();
4726 float percentage = (float) ((double) newlen - (double) clicked_regionview->region()->length()) / ((double) newlen) * 100.0f;
4728 begin_reversible_command (_("timestretch"));
4730 if (run_timestretch (selection->regions, percentage) == 0) {
4731 session->commit_reversible_command ();
4736 Editor::mouse_brush_insert_region (RegionView* rv, nframes_t pos)
4738 /* no brushing without a useful snap setting */
4741 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
4744 switch (snap_mode) {
4746 return; /* can't work because it allows region to be placed anywhere */
4751 switch (snap_type) {
4754 case SnapToEditCursor:
4761 /* don't brush a copy over the original */
4763 if (pos == rv->region()->position()) {
4767 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&arv->get_time_axis_view());
4769 if (rtv == 0 || !rtv->is_track()) {
4773 boost::shared_ptr<Playlist> playlist = rtv->playlist();
4774 double speed = rtv->get_diskstream()->speed();
4776 XMLNode &before = playlist->get_state();
4777 playlist->add_region (boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (arv->audio_region())), (nframes_t) (pos * speed));
4778 XMLNode &after = playlist->get_state();
4779 session->add_command(new MementoCommand<Playlist>(*playlist.get(), &before, &after));
4781 // playlist is frozen, so we have to update manually
4783 playlist->Modified(); /* EMIT SIGNAL */
4787 Editor::track_height_step_timeout ()
4790 struct timeval delta;
4792 gettimeofday (&now, 0);
4793 timersub (&now, &last_track_height_step_timestamp, &delta);
4795 if (delta.tv_sec * 1000000 + delta.tv_usec > 250000) { /* milliseconds */
4796 current_stepping_trackview = 0;