2 Copyright (C) 2000-2001 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
28 #include <pbd/error.h>
29 #include <gtkmm2ext/utils.h>
30 #include <pbd/memento_command.h>
32 #include "ardour_ui.h"
34 #include "time_axis_view.h"
35 #include "audio_time_axis.h"
36 #include "audio_region_view.h"
37 #include "midi_region_view.h"
39 #include "streamview.h"
40 #include "region_gain_line.h"
41 #include "automation_time_axis.h"
44 #include "selection.h"
47 #include "rgb_macros.h"
49 #include <ardour/types.h>
50 #include <ardour/profile.h>
51 #include <ardour/route.h>
52 #include <ardour/audio_track.h>
53 #include <ardour/audio_diskstream.h>
54 #include <ardour/playlist.h>
55 #include <ardour/audioplaylist.h>
56 #include <ardour/audioregion.h>
57 #include <ardour/midi_region.h>
58 #include <ardour/dB.h>
59 #include <ardour/utils.h>
60 #include <ardour/region_factory.h>
67 using namespace ARDOUR;
71 using namespace Editing;
74 Editor::event_frame (GdkEvent* event, double* pcx, double* pcy)
88 switch (event->type) {
89 case GDK_BUTTON_RELEASE:
90 case GDK_BUTTON_PRESS:
91 case GDK_2BUTTON_PRESS:
92 case GDK_3BUTTON_PRESS:
93 track_canvas.w2c(event->button.x, event->button.y, *pcx, *pcy);
95 case GDK_MOTION_NOTIFY:
96 track_canvas.w2c(event->motion.x, event->motion.y, *pcx, *pcy);
98 case GDK_ENTER_NOTIFY:
99 case GDK_LEAVE_NOTIFY:
100 track_canvas.w2c(event->crossing.x, event->crossing.y, *pcx, *pcy);
103 case GDK_KEY_RELEASE:
104 // track_canvas.w2c(event->key.x, event->key.y, *pcx, *pcy);
107 warning << string_compose (_("Editor::event_frame() used on unhandled event type %1"), event->type) << endmsg;
111 /* note that pixel_to_frame() never returns less than zero, so even if the pixel
112 position is negative (as can be the case with motion events in particular),
113 the frame location is always positive.
116 return pixel_to_frame (*pcx);
120 Editor::mouse_mode_toggled (MouseMode m)
122 if (ignore_mouse_mode_toggle) {
128 if (mouse_select_button.get_active()) {
134 if (mouse_move_button.get_active()) {
140 if (mouse_gain_button.get_active()) {
146 if (mouse_zoom_button.get_active()) {
152 if (mouse_timefx_button.get_active()) {
158 if (mouse_audition_button.get_active()) {
169 Editor::set_mouse_mode (MouseMode m, bool force)
171 if (drag_info.item) {
175 if (!force && m == mouse_mode) {
183 if (mouse_mode != MouseRange) {
185 /* in all modes except range, hide the range selection,
186 show the object (region) selection.
189 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
190 (*i)->set_should_show_selection (true);
192 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
193 (*i)->hide_selection ();
199 in range mode,show the range selection.
202 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
203 if ((*i)->get_selected()) {
204 (*i)->show_selection (selection->time);
209 /* XXX the hack of unsetting all other buttongs should go
210 away once GTK2 allows us to use regular radio buttons drawn like
211 normal buttons, rather than my silly GroupedButton hack.
214 ignore_mouse_mode_toggle = true;
216 switch (mouse_mode) {
218 mouse_select_button.set_active (true);
219 current_canvas_cursor = selector_cursor;
223 mouse_move_button.set_active (true);
224 current_canvas_cursor = grabber_cursor;
228 mouse_gain_button.set_active (true);
229 current_canvas_cursor = cross_hair_cursor;
233 mouse_zoom_button.set_active (true);
234 current_canvas_cursor = zoom_cursor;
238 mouse_timefx_button.set_active (true);
239 current_canvas_cursor = time_fx_cursor; // just use playhead
243 mouse_audition_button.set_active (true);
244 current_canvas_cursor = speaker_cursor;
248 ignore_mouse_mode_toggle = false;
251 track_canvas.get_window()->set_cursor(*current_canvas_cursor);
256 Editor::step_mouse_mode (bool next)
258 switch (current_mouse_mode()) {
260 if (next) set_mouse_mode (MouseRange);
261 else set_mouse_mode (MouseTimeFX);
265 if (next) set_mouse_mode (MouseZoom);
266 else set_mouse_mode (MouseObject);
270 if (next) set_mouse_mode (MouseGain);
271 else set_mouse_mode (MouseRange);
275 if (next) set_mouse_mode (MouseTimeFX);
276 else set_mouse_mode (MouseZoom);
280 if (next) set_mouse_mode (MouseAudition);
281 else set_mouse_mode (MouseGain);
285 if (next) set_mouse_mode (MouseObject);
286 else set_mouse_mode (MouseTimeFX);
292 Editor::button_selection (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
296 /* in object/audition/timefx mode, any button press sets
297 the selection if the object can be selected. this is a
298 bit of hack, because we want to avoid this if the
299 mouse operation is a region alignment.
301 note: not dbl-click or triple-click
304 if (((mouse_mode != MouseObject) &&
305 (mouse_mode != MouseAudition || item_type != RegionItem) &&
306 (mouse_mode != MouseTimeFX || item_type != RegionItem) &&
307 (mouse_mode != MouseRange)) ||
309 (event->type != GDK_BUTTON_PRESS && event->type != GDK_BUTTON_RELEASE || event->button.button > 3)) {
314 if (event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) {
316 if ((event->button.state & Keyboard::RelevantModifierKeyMask) && event->button.button != 1) {
318 /* almost no selection action on modified button-2 or button-3 events */
320 if (item_type != RegionItem && event->button.button != 2) {
326 Selection::Operation op = Keyboard::selection_type (event->button.state);
327 bool press = (event->type == GDK_BUTTON_PRESS);
329 // begin_reversible_command (_("select on click"));
333 case RegionViewNameHighlight:
335 case FadeInHandleItem:
337 case FadeOutHandleItem:
339 if (mouse_mode != MouseRange) {
340 commit = set_selected_regionview_from_click (press, op, true);
341 } else if (event->type == GDK_BUTTON_PRESS) {
342 commit = set_selected_track_from_click (press, op, false);
346 case CrossfadeViewItem:
347 commit = set_selected_track_from_click (press, op, false);
350 case ControlPointItem:
351 commit = set_selected_track_from_click (press, op, true);
352 if (mouse_mode != MouseRange) {
353 commit |= set_selected_control_point_from_click (op, false);
358 /* for context click or range selection, select track */
359 if (event->button.button == 3) {
360 commit = set_selected_track_from_click (press, op, true);
361 } else if (event->type == GDK_BUTTON_PRESS && mouse_mode == MouseRange) {
362 commit = set_selected_track_from_click (press, op, false);
366 case AutomationTrackItem:
367 commit = set_selected_track_from_click (press, op, true);
375 // commit_reversible_command ();
380 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
382 track_canvas.grab_focus();
384 if (session && session->actively_recording()) {
388 button_selection (item, event, item_type);
390 if (drag_info.item == 0 &&
391 (Keyboard::is_delete_event (&event->button) ||
392 Keyboard::is_context_menu_event (&event->button) ||
393 Keyboard::is_edit_event (&event->button))) {
395 /* handled by button release */
399 switch (event->button.button) {
402 if (event->type == GDK_BUTTON_PRESS) {
404 if (drag_info.item) {
405 drag_info.item->ungrab (event->button.time);
408 /* single mouse clicks on any of these item types operate
409 independent of mouse mode, mostly because they are
410 not on the main track canvas or because we want
416 case PlayheadCursorItem:
417 start_cursor_grab (item, event);
421 if (Keyboard::modifier_state_equals (event->button.state,
422 Keyboard::ModifierMask(Keyboard::Control|Keyboard::Shift))) {
423 hide_marker (item, event);
425 start_marker_grab (item, event);
429 case TempoMarkerItem:
430 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
431 start_tempo_marker_copy_grab (item, event);
433 start_tempo_marker_grab (item, event);
437 case MeterMarkerItem:
438 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
439 start_meter_marker_copy_grab (item, event);
441 start_meter_marker_grab (item, event);
451 case RangeMarkerBarItem:
452 start_range_markerbar_op (item, event, CreateRangeMarker);
456 case TransportMarkerBarItem:
457 start_range_markerbar_op (item, event, CreateTransportMarker);
466 switch (mouse_mode) {
469 case StartSelectionTrimItem:
470 start_selection_op (item, event, SelectionStartTrim);
473 case EndSelectionTrimItem:
474 start_selection_op (item, event, SelectionEndTrim);
478 if (Keyboard::modifier_state_contains
479 (event->button.state, Keyboard::ModifierMask(Keyboard::Alt))) {
480 // contains and not equals because I can't use alt as a modifier alone.
481 start_selection_grab (item, event);
482 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
483 /* grab selection for moving */
484 start_selection_op (item, event, SelectionMove);
487 /* this was debated, but decided the more common action was to
488 make a new selection */
489 start_selection_op (item, event, CreateSelection);
494 start_selection_op (item, event, CreateSelection);
500 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::Control|Keyboard::Alt)) &&
501 event->type == GDK_BUTTON_PRESS) {
503 start_rubberband_select (item, event);
505 } else if (event->type == GDK_BUTTON_PRESS) {
508 case FadeInHandleItem:
509 start_fade_in_grab (item, event);
512 case FadeOutHandleItem:
513 start_fade_out_grab (item, event);
517 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
518 start_region_copy_grab (item, event);
519 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
520 start_region_brush_grab (item, event);
522 start_region_grab (item, event);
526 case RegionViewNameHighlight:
527 start_trim (item, event);
532 /* rename happens on edit clicks */
533 start_trim (clicked_regionview->get_name_highlight(), event);
537 case ControlPointItem:
538 start_control_point_grab (item, event);
542 case AutomationLineItem:
543 start_line_grab_from_line (item, event);
548 case AutomationTrackItem:
549 start_rubberband_select (item, event);
553 case ImageFrameHandleStartItem:
554 imageframe_start_handle_op(item, event) ;
557 case ImageFrameHandleEndItem:
558 imageframe_end_handle_op(item, event) ;
561 case MarkerViewHandleStartItem:
562 markerview_item_start_handle_op(item, event) ;
565 case MarkerViewHandleEndItem:
566 markerview_item_end_handle_op(item, event) ;
570 start_markerview_grab(item, event) ;
573 start_imageframe_grab(item, event) ;
591 // start_line_grab_from_regionview (item, event);
595 start_line_grab_from_line (item, event);
598 case ControlPointItem:
599 start_control_point_grab (item, event);
610 case ControlPointItem:
611 start_control_point_grab (item, event);
614 case AutomationLineItem:
615 start_line_grab_from_line (item, event);
619 // XXX need automation mode to identify which
621 // start_line_grab_from_regionview (item, event);
631 if (event->type == GDK_BUTTON_PRESS) {
632 start_mouse_zoom (item, event);
639 if (item_type == RegionItem) {
640 start_time_fx (item, event);
645 /* handled in release */
654 switch (mouse_mode) {
656 if (event->type == GDK_BUTTON_PRESS) {
659 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
660 start_region_copy_grab (item, event);
662 start_region_grab (item, event);
666 case ControlPointItem:
667 start_control_point_grab (item, event);
678 case RegionViewNameHighlight:
679 start_trim (item, event);
684 start_trim (clicked_regionview->get_name_highlight(), event);
695 if (event->type == GDK_BUTTON_PRESS) {
696 /* relax till release */
703 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
704 temporal_zoom_session();
706 temporal_zoom_to_frame (true, event_frame(event));
729 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
731 nframes_t where = event_frame (event, 0, 0);
733 /* no action if we're recording */
735 if (session && session->actively_recording()) {
739 /* first, see if we're finishing a drag ... */
741 if (drag_info.item) {
742 if (end_grab (item, event)) {
743 /* grab dragged, so do nothing else */
748 button_selection (item, event, item_type);
750 /* edit events get handled here */
752 if (drag_info.item == 0 && Keyboard::is_edit_event (&event->button)) {
758 case TempoMarkerItem:
759 edit_tempo_marker (item);
762 case MeterMarkerItem:
763 edit_meter_marker (item);
767 if (clicked_regionview->name_active()) {
768 return mouse_rename_region (item, event);
778 /* context menu events get handled here */
780 if (Keyboard::is_context_menu_event (&event->button)) {
782 if (drag_info.item == 0) {
784 /* no matter which button pops up the context menu, tell the menu
785 widget to use button 1 to drive menu selection.
790 case FadeInHandleItem:
792 case FadeOutHandleItem:
793 popup_fade_context_menu (1, event->button.time, item, item_type);
798 case RegionViewNameHighlight:
801 case AutomationTrackItem:
802 case CrossfadeViewItem:
803 popup_track_context_menu (1, event->button.time, where);
807 case RangeMarkerBarItem:
808 case TransportMarkerBarItem:
811 popup_ruler_menu (pixel_to_frame(event->button.x), item_type);
815 marker_context_menu (&event->button, item);
818 case TempoMarkerItem:
819 tm_marker_context_menu (&event->button, item);
822 case MeterMarkerItem:
823 tm_marker_context_menu (&event->button, item);
828 popup_imageframe_edit_menu(1, event->button.time, item, true) ;
830 case ImageFrameTimeAxisItem:
831 popup_imageframe_edit_menu(1, event->button.time, item, false) ;
834 popup_marker_time_axis_edit_menu(1, event->button.time, item, true) ;
836 case MarkerTimeAxisItem:
837 popup_marker_time_axis_edit_menu(1, event->button.time, item, false) ;
849 /* delete events get handled here */
851 if (drag_info.item == 0 && Keyboard::is_delete_event (&event->button)) {
854 case TempoMarkerItem:
855 remove_tempo_marker (item);
858 case MeterMarkerItem:
859 remove_meter_marker (item);
863 remove_marker (*item, event);
867 if (mouse_mode == MouseObject) {
868 remove_clicked_region ();
872 case ControlPointItem:
873 if (mouse_mode == MouseGain) {
874 remove_gain_control_point (item, event);
876 remove_control_point (item, event);
886 switch (event->button.button) {
890 /* see comments in button_press_handler */
892 case PlayheadCursorItem:
895 case AutomationLineItem:
896 case StartSelectionTrimItem:
897 case EndSelectionTrimItem:
901 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
902 snap_to (where, 0, true);
904 mouse_add_new_marker (where);
908 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
911 mouse_add_new_tempo_event (where);
915 mouse_add_new_meter_event (pixel_to_frame (event->button.x));
923 switch (mouse_mode) {
926 case AutomationTrackItem:
927 dynamic_cast<AutomationTimeAxisView*>(clicked_axisview)->add_automation_event
941 // Gain only makes sense for audio regions
943 if (!dynamic_cast<AudioRegionView*>(clicked_regionview)) {
949 dynamic_cast<AudioRegionView*>(clicked_regionview)->add_gain_point_event (item, event);
953 case AutomationTrackItem:
954 dynamic_cast<AutomationTimeAxisView*>(clicked_axisview)->
955 add_automation_event (item, event, where, event->button.y);
966 audition_selected_region ();
983 switch (mouse_mode) {
987 // x_style_paste (where, 1.0);
1007 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1013 switch (item_type) {
1014 case ControlPointItem:
1015 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1016 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1017 cp->set_visible (true);
1021 at_y = cp->get_y ();
1022 cp->item->i2w (at_x, at_y);
1026 fraction = 1.0 - (cp->get_y() / cp->line.height());
1028 set_verbose_canvas_cursor (cp->line.get_verbose_cursor_string (fraction), at_x, at_y);
1029 show_verbose_canvas_cursor ();
1031 if (is_drawable()) {
1032 track_canvas.get_window()->set_cursor (*fader_cursor);
1038 if (mouse_mode == MouseGain) {
1039 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1041 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredGainLine.get();
1042 if (is_drawable()) {
1043 track_canvas.get_window()->set_cursor (*fader_cursor);
1048 case AutomationLineItem:
1049 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1051 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1053 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredAutomationLine.get();
1055 if (is_drawable()) {
1056 track_canvas.get_window()->set_cursor (*fader_cursor);
1061 case RegionViewNameHighlight:
1062 if (is_drawable() && mouse_mode == MouseObject) {
1063 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1067 case StartSelectionTrimItem:
1068 case EndSelectionTrimItem:
1071 case ImageFrameHandleStartItem:
1072 case ImageFrameHandleEndItem:
1073 case MarkerViewHandleStartItem:
1074 case MarkerViewHandleEndItem:
1077 if (is_drawable()) {
1078 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1082 case EditCursorItem:
1083 case PlayheadCursorItem:
1084 if (is_drawable()) {
1085 track_canvas.get_window()->set_cursor (*grabber_cursor);
1089 case RegionViewName:
1091 /* when the name is not an active item, the entire name highlight is for trimming */
1093 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1094 if (mouse_mode == MouseObject && is_drawable()) {
1095 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1101 case AutomationTrackItem:
1102 if (is_drawable()) {
1103 Gdk::Cursor *cursor;
1104 switch (mouse_mode) {
1106 cursor = selector_cursor;
1109 cursor = zoom_cursor;
1112 cursor = cross_hair_cursor;
1116 track_canvas.get_window()->set_cursor (*cursor);
1118 AutomationTimeAxisView* atv;
1119 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1120 clear_entered_track = false;
1121 set_entered_track (atv);
1127 case RangeMarkerBarItem:
1128 case TransportMarkerBarItem:
1131 if (is_drawable()) {
1132 time_canvas.get_window()->set_cursor (*timebar_cursor);
1137 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1140 marker->set_color_rgba (ARDOUR_UI::config()->canvasvar_EnteredMarker.get());
1142 case MeterMarkerItem:
1143 case TempoMarkerItem:
1144 if (is_drawable()) {
1145 time_canvas.get_window()->set_cursor (*timebar_cursor);
1148 case FadeInHandleItem:
1149 case FadeOutHandleItem:
1150 if (mouse_mode == MouseObject) {
1151 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1153 rect->property_fill_color_rgba() = 0;
1154 rect->property_outline_pixels() = 1;
1163 /* second pass to handle entered track status in a comprehensible way.
1166 switch (item_type) {
1168 case AutomationLineItem:
1169 case ControlPointItem:
1170 /* these do not affect the current entered track state */
1171 clear_entered_track = false;
1174 case AutomationTrackItem:
1175 /* handled above already */
1179 set_entered_track (0);
1187 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1196 switch (item_type) {
1197 case ControlPointItem:
1198 cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
1199 if (cp->line.npoints() > 1) {
1200 if (!cp->selected) {
1201 cp->set_visible (false);
1205 if (is_drawable()) {
1206 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1209 hide_verbose_canvas_cursor ();
1212 case RegionViewNameHighlight:
1213 case StartSelectionTrimItem:
1214 case EndSelectionTrimItem:
1215 case EditCursorItem:
1216 case PlayheadCursorItem:
1219 case ImageFrameHandleStartItem:
1220 case ImageFrameHandleEndItem:
1221 case MarkerViewHandleStartItem:
1222 case MarkerViewHandleEndItem:
1225 if (is_drawable()) {
1226 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1231 case AutomationLineItem:
1232 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1234 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1236 line->property_fill_color_rgba() = al->get_line_color();
1238 if (is_drawable()) {
1239 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1243 case RegionViewName:
1244 /* see enter_handler() for notes */
1245 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1246 if (is_drawable() && mouse_mode == MouseObject) {
1247 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1252 case RangeMarkerBarItem:
1253 case TransportMarkerBarItem:
1257 if (is_drawable()) {
1258 time_canvas.get_window()->set_cursor (*timebar_cursor);
1263 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1266 loc = find_location_from_marker (marker, is_start);
1267 if (loc) location_flags_changed (loc, this);
1269 case MeterMarkerItem:
1270 case TempoMarkerItem:
1272 if (is_drawable()) {
1273 time_canvas.get_window()->set_cursor (*timebar_cursor);
1278 case FadeInHandleItem:
1279 case FadeOutHandleItem:
1280 rv = static_cast<RegionView*>(item->get_data ("regionview"));
1282 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1284 rect->property_fill_color_rgba() = rv->get_fill_color();
1285 rect->property_outline_pixels() = 0;
1290 case AutomationTrackItem:
1291 if (is_drawable()) {
1292 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1293 clear_entered_track = true;
1294 Glib::signal_idle().connect (mem_fun(*this, &Editor::left_automation_track));
1306 Editor::left_automation_track ()
1308 if (clear_entered_track) {
1309 set_entered_track (0);
1310 clear_entered_track = false;
1316 Editor::motion_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type, bool from_autoscroll)
1320 /* We call this so that MOTION_NOTIFY events continue to be
1321 delivered to the canvas. We need to do this because we set
1322 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
1323 the density of the events, at the expense of a round-trip
1324 to the server. Given that this will mostly occur on cases
1325 where DISPLAY = :0.0, and given the cost of what the motion
1326 event might do, its a good tradeoff.
1329 track_canvas.get_pointer (x, y);
1331 if (current_stepping_trackview) {
1332 /* don't keep the persistent stepped trackview if the mouse moves */
1333 current_stepping_trackview = 0;
1334 step_timeout.disconnect ();
1337 if (session && session->actively_recording()) {
1338 /* Sorry. no dragging stuff around while we record */
1342 drag_info.item_type = item_type;
1343 drag_info.current_pointer_frame = event_frame (event, &drag_info.current_pointer_x,
1344 &drag_info.current_pointer_y);
1346 if (!from_autoscroll && drag_info.item) {
1347 /* item != 0 is the best test i can think of for dragging.
1349 if (!drag_info.move_threshold_passed) {
1351 bool x_threshold_passed = (abs ((nframes64_t) (drag_info.current_pointer_x - drag_info.grab_x)) > 4LL);
1352 bool y_threshold_passed = (abs ((nframes64_t) (drag_info.current_pointer_y - drag_info.grab_y)) > 4LL);
1354 drag_info.move_threshold_passed = (x_threshold_passed || y_threshold_passed);
1356 // and change the initial grab loc/frame if this drag info wants us to
1358 if (drag_info.want_move_threshold && drag_info.move_threshold_passed) {
1359 drag_info.grab_frame = drag_info.current_pointer_frame;
1360 drag_info.grab_x = drag_info.current_pointer_x;
1361 drag_info.grab_y = drag_info.current_pointer_y;
1362 drag_info.last_pointer_frame = drag_info.grab_frame;
1363 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
1368 switch (item_type) {
1369 case PlayheadCursorItem:
1370 case EditCursorItem:
1372 case ControlPointItem:
1373 case TempoMarkerItem:
1374 case MeterMarkerItem:
1375 case RegionViewNameHighlight:
1376 case StartSelectionTrimItem:
1377 case EndSelectionTrimItem:
1380 case AutomationLineItem:
1381 case FadeInHandleItem:
1382 case FadeOutHandleItem:
1385 case ImageFrameHandleStartItem:
1386 case ImageFrameHandleEndItem:
1387 case MarkerViewHandleStartItem:
1388 case MarkerViewHandleEndItem:
1391 if (drag_info.item && (event->motion.state & Gdk::BUTTON1_MASK ||
1392 (event->motion.state & Gdk::BUTTON2_MASK))) {
1393 if (!from_autoscroll) {
1394 maybe_autoscroll (event);
1396 (this->*(drag_info.motion_callback)) (item, event);
1405 switch (mouse_mode) {
1410 if (drag_info.item && (event->motion.state & GDK_BUTTON1_MASK ||
1411 (event->motion.state & GDK_BUTTON2_MASK))) {
1412 if (!from_autoscroll) {
1413 maybe_autoscroll (event);
1415 (this->*(drag_info.motion_callback)) (item, event);
1426 track_canvas_motion (event);
1427 // drag_info.last_pointer_frame = drag_info.current_pointer_frame;
1435 Editor::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
1437 if (drag_info.item == 0) {
1438 fatal << _("programming error: start_grab called without drag item") << endmsg;
1444 cursor = grabber_cursor;
1447 // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
1449 if (event->button.button == 2) {
1450 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Alt)) {
1451 drag_info.y_constrained = true;
1452 drag_info.x_constrained = false;
1454 drag_info.y_constrained = false;
1455 drag_info.x_constrained = true;
1458 drag_info.x_constrained = false;
1459 drag_info.y_constrained = false;
1462 drag_info.grab_frame = event_frame (event, &drag_info.grab_x, &drag_info.grab_y);
1463 drag_info.last_pointer_frame = drag_info.grab_frame;
1464 drag_info.current_pointer_frame = drag_info.grab_frame;
1465 drag_info.current_pointer_x = drag_info.grab_x;
1466 drag_info.current_pointer_y = drag_info.grab_y;
1467 drag_info.cumulative_x_drag = 0;
1468 drag_info.cumulative_y_drag = 0;
1469 drag_info.first_move = true;
1470 drag_info.move_threshold_passed = false;
1471 drag_info.want_move_threshold = false;
1472 drag_info.pointer_frame_offset = 0;
1473 drag_info.brushing = false;
1474 drag_info.copied_location = 0;
1476 drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK,
1478 event->button.time);
1480 if (session && session->transport_rolling()) {
1481 drag_info.was_rolling = true;
1483 drag_info.was_rolling = false;
1486 switch (snap_type) {
1487 case SnapToRegionStart:
1488 case SnapToRegionEnd:
1489 case SnapToRegionSync:
1490 case SnapToRegionBoundary:
1491 build_region_boundary_cache ();
1499 Editor::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t time)
1501 drag_info.item->ungrab (0);
1502 drag_info.item = new_item;
1505 cursor = grabber_cursor;
1508 drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK, *cursor, time);
1512 Editor::end_grab (ArdourCanvas::Item* item, GdkEvent* event)
1514 bool did_drag = false;
1516 stop_canvas_autoscroll ();
1518 if (drag_info.item == 0) {
1522 drag_info.item->ungrab (event->button.time);
1524 if (drag_info.finished_callback) {
1525 (this->*(drag_info.finished_callback)) (item, event);
1528 did_drag = !drag_info.first_move;
1530 hide_verbose_canvas_cursor();
1533 drag_info.copy = false;
1534 drag_info.motion_callback = 0;
1535 drag_info.finished_callback = 0;
1536 drag_info.last_trackview = 0;
1537 drag_info.last_frame_position = 0;
1538 drag_info.grab_frame = 0;
1539 drag_info.last_pointer_frame = 0;
1540 drag_info.current_pointer_frame = 0;
1541 drag_info.brushing = false;
1543 if (drag_info.copied_location) {
1544 delete drag_info.copied_location;
1545 drag_info.copied_location = 0;
1552 Editor::set_edit_cursor (GdkEvent* event)
1554 nframes_t pointer_frame = event_frame (event);
1556 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1557 if (snap_type != SnapToEditCursor) {
1558 snap_to (pointer_frame);
1562 edit_cursor->set_position (pointer_frame);
1563 edit_cursor_clock.set (pointer_frame);
1567 Editor::set_playhead_cursor (GdkEvent* event)
1569 nframes_t pointer_frame = event_frame (event);
1571 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1572 snap_to (pointer_frame);
1576 session->request_locate (pointer_frame, session->transport_rolling());
1581 Editor::start_fade_in_grab (ArdourCanvas::Item* item, GdkEvent* event)
1583 drag_info.item = item;
1584 drag_info.motion_callback = &Editor::fade_in_drag_motion_callback;
1585 drag_info.finished_callback = &Editor::fade_in_drag_finished_callback;
1589 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
1590 fatal << _("programming error: fade in canvas item has no regionview data pointer!") << endmsg;
1594 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1596 drag_info.pointer_frame_offset = drag_info.grab_frame - ((nframes_t) arv->audio_region()->fade_in()->back()->when + arv->region()->position());
1600 Editor::fade_in_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1602 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1604 nframes_t fade_length;
1606 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1607 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1613 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1617 if (pos < (arv->region()->position() + 64)) {
1618 fade_length = 64; // this should be a minimum defined somewhere
1619 } else if (pos > arv->region()->last_frame()) {
1620 fade_length = arv->region()->length();
1622 fade_length = pos - arv->region()->position();
1624 /* mapover the region selection */
1626 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1628 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1634 tmp->reset_fade_in_shape_width (fade_length);
1637 show_verbose_duration_cursor (arv->region()->position(), arv->region()->position() + fade_length, 10);
1639 drag_info.first_move = false;
1643 Editor::fade_in_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1645 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1647 nframes_t fade_length;
1649 if (drag_info.first_move) return;
1651 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1652 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1657 if (pos < (arv->region()->position() + 64)) {
1658 fade_length = 64; // this should be a minimum defined somewhere
1659 } else if (pos > arv->region()->last_frame()) {
1660 fade_length = arv->region()->length();
1662 fade_length = pos - arv->region()->position();
1665 begin_reversible_command (_("change fade in length"));
1667 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1669 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1675 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
1676 XMLNode &before = alist->get_state();
1678 tmp->audio_region()->set_fade_in_length (fade_length);
1680 XMLNode &after = alist->get_state();
1681 session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
1684 commit_reversible_command ();
1688 Editor::start_fade_out_grab (ArdourCanvas::Item* item, GdkEvent* event)
1690 drag_info.item = item;
1691 drag_info.motion_callback = &Editor::fade_out_drag_motion_callback;
1692 drag_info.finished_callback = &Editor::fade_out_drag_finished_callback;
1696 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
1697 fatal << _("programming error: fade out canvas item has no regionview data pointer!") << endmsg;
1701 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1703 drag_info.pointer_frame_offset = drag_info.grab_frame - (arv->region()->length() - (nframes_t) arv->audio_region()->fade_out()->back()->when + arv->region()->position());
1707 Editor::fade_out_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1709 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1711 nframes_t fade_length;
1713 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1714 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1719 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1723 if (pos > (arv->region()->last_frame() - 64)) {
1724 fade_length = 64; // this should really be a minimum fade defined somewhere
1726 else if (pos < arv->region()->position()) {
1727 fade_length = arv->region()->length();
1730 fade_length = arv->region()->last_frame() - pos;
1733 /* mapover the region selection */
1735 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1737 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1743 tmp->reset_fade_out_shape_width (fade_length);
1746 show_verbose_duration_cursor (arv->region()->last_frame() - fade_length, arv->region()->last_frame(), 10);
1748 drag_info.first_move = false;
1752 Editor::fade_out_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1754 if (drag_info.first_move) return;
1756 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1758 nframes_t fade_length;
1760 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1761 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1767 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1771 if (pos > (arv->region()->last_frame() - 64)) {
1772 fade_length = 64; // this should really be a minimum fade defined somewhere
1774 else if (pos < arv->region()->position()) {
1775 fade_length = arv->region()->length();
1778 fade_length = arv->region()->last_frame() - pos;
1781 begin_reversible_command (_("change fade out length"));
1783 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1785 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1791 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
1792 XMLNode &before = alist->get_state();
1794 tmp->audio_region()->set_fade_out_length (fade_length);
1796 XMLNode &after = alist->get_state();
1797 session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
1800 commit_reversible_command ();
1804 Editor::start_cursor_grab (ArdourCanvas::Item* item, GdkEvent* event)
1806 drag_info.item = item;
1807 drag_info.motion_callback = &Editor::cursor_drag_motion_callback;
1808 drag_info.finished_callback = &Editor::cursor_drag_finished_callback;
1812 if ((drag_info.data = (item->get_data ("cursor"))) == 0) {
1813 fatal << _("programming error: cursor canvas item has no cursor data pointer!") << endmsg;
1817 Cursor* cursor = (Cursor *) drag_info.data;
1819 if (cursor == playhead_cursor) {
1820 _dragging_playhead = true;
1822 if (session && drag_info.was_rolling) {
1823 session->request_stop ();
1826 if (session && session->is_auditioning()) {
1827 session->cancel_audition ();
1831 drag_info.pointer_frame_offset = drag_info.grab_frame - cursor->current_frame;
1833 show_verbose_time_cursor (cursor->current_frame, 10);
1837 Editor::cursor_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1839 Cursor* cursor = (Cursor *) drag_info.data;
1840 nframes_t adjusted_frame;
1842 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1843 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1849 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1850 if (cursor != edit_cursor || snap_type != SnapToEditCursor) {
1851 snap_to (adjusted_frame);
1855 if (adjusted_frame == drag_info.last_pointer_frame) return;
1857 cursor->set_position (adjusted_frame);
1859 if (cursor == edit_cursor) {
1860 edit_cursor_clock.set (cursor->current_frame);
1862 UpdateAllTransportClocks (cursor->current_frame);
1865 show_verbose_time_cursor (cursor->current_frame, 10);
1867 drag_info.last_pointer_frame = adjusted_frame;
1868 drag_info.first_move = false;
1872 Editor::cursor_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1874 if (drag_info.first_move) return;
1876 cursor_drag_motion_callback (item, event);
1878 _dragging_playhead = false;
1880 if (item == &playhead_cursor->canvas_item) {
1882 session->request_locate (playhead_cursor->current_frame, drag_info.was_rolling);
1884 } else if (item == &edit_cursor->canvas_item) {
1885 edit_cursor->set_position (edit_cursor->current_frame);
1886 edit_cursor_clock.set (edit_cursor->current_frame);
1891 Editor::update_marker_drag_item (Location *location)
1893 double x1 = frame_to_pixel (location->start());
1894 double x2 = frame_to_pixel (location->end());
1896 if (location->is_mark()) {
1897 marker_drag_line_points.front().set_x(x1);
1898 marker_drag_line_points.back().set_x(x1);
1899 marker_drag_line->property_points() = marker_drag_line_points;
1902 range_marker_drag_rect->property_x1() = x1;
1903 range_marker_drag_rect->property_x2() = x2;
1908 Editor::start_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
1912 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1913 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
1919 Location *location = find_location_from_marker (marker, is_start);
1921 drag_info.item = item;
1922 drag_info.data = marker;
1923 drag_info.motion_callback = &Editor::marker_drag_motion_callback;
1924 drag_info.finished_callback = &Editor::marker_drag_finished_callback;
1928 drag_info.copied_location = new Location (*location);
1929 drag_info.pointer_frame_offset = drag_info.grab_frame - (is_start ? location->start() : location->end());
1931 update_marker_drag_item (location);
1933 if (location->is_mark()) {
1934 marker_drag_line->show();
1935 marker_drag_line->raise_to_top();
1938 range_marker_drag_rect->show();
1939 range_marker_drag_rect->raise_to_top();
1942 if (is_start) show_verbose_time_cursor (location->start(), 10);
1943 else show_verbose_time_cursor (location->end(), 10);
1947 Editor::marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1950 Marker* marker = (Marker *) drag_info.data;
1951 Location *real_location;
1952 Location *copy_location;
1954 bool move_both = false;
1958 if (drag_info.pointer_frame_offset <= drag_info.current_pointer_frame) {
1959 newframe = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1964 nframes_t next = newframe;
1966 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1967 snap_to (newframe, 0, true);
1970 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
1974 /* call this to find out if its the start or end */
1976 real_location = find_location_from_marker (marker, is_start);
1978 /* use the copy that we're "dragging" around */
1980 copy_location = drag_info.copied_location;
1982 f_delta = copy_location->end() - copy_location->start();
1984 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
1988 if (copy_location->is_mark()) {
1991 copy_location->set_start (newframe);
1995 if (is_start) { // start-of-range marker
1998 copy_location->set_start (newframe);
1999 copy_location->set_end (newframe + f_delta);
2000 } else if (newframe < copy_location->end()) {
2001 copy_location->set_start (newframe);
2003 snap_to (next, 1, true);
2004 copy_location->set_end (next);
2005 copy_location->set_start (newframe);
2008 } else { // end marker
2011 copy_location->set_end (newframe);
2012 copy_location->set_start (newframe - f_delta);
2013 } else if (newframe > copy_location->start()) {
2014 copy_location->set_end (newframe);
2016 } else if (newframe > 0) {
2017 snap_to (next, -1, true);
2018 copy_location->set_start (next);
2019 copy_location->set_end (newframe);
2024 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
2025 drag_info.first_move = false;
2027 update_marker_drag_item (copy_location);
2029 LocationMarkers* lm = find_location_markers (real_location);
2030 lm->set_position (copy_location->start(), copy_location->end());
2032 show_verbose_time_cursor (newframe, 10);
2036 Editor::marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2038 if (drag_info.first_move) {
2039 marker_drag_motion_callback (item, event);
2043 Marker* marker = (Marker *) drag_info.data;
2047 begin_reversible_command ( _("move marker") );
2048 XMLNode &before = session->locations()->get_state();
2050 Location * location = find_location_from_marker (marker, is_start);
2053 if (location->is_mark()) {
2054 location->set_start (drag_info.copied_location->start());
2056 location->set (drag_info.copied_location->start(), drag_info.copied_location->end());
2060 XMLNode &after = session->locations()->get_state();
2061 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
2062 commit_reversible_command ();
2064 marker_drag_line->hide();
2065 range_marker_drag_rect->hide();
2069 Editor::start_meter_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2072 MeterMarker* meter_marker;
2074 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2075 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2079 meter_marker = dynamic_cast<MeterMarker*> (marker);
2081 MetricSection& section (meter_marker->meter());
2083 if (!section.movable()) {
2087 drag_info.item = item;
2088 drag_info.data = marker;
2089 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2090 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2094 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2096 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2100 Editor::start_meter_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2103 MeterMarker* meter_marker;
2105 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2106 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2110 meter_marker = dynamic_cast<MeterMarker*> (marker);
2112 // create a dummy marker for visual representation of moving the copy.
2113 // The actual copying is not done before we reach the finish callback.
2115 snprintf (name, sizeof(name), "%g/%g", meter_marker->meter().beats_per_bar(), meter_marker->meter().note_divisor ());
2116 MeterMarker* new_marker = new MeterMarker(*this, *meter_group, ARDOUR_UI::config()->canvasvar_MeterMarker.get(), name,
2117 *new MeterSection(meter_marker->meter()));
2119 drag_info.item = &new_marker->the_item();
2120 drag_info.copy = true;
2121 drag_info.data = new_marker;
2122 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2123 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2127 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2129 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2133 Editor::meter_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2135 MeterMarker* marker = (MeterMarker *) drag_info.data;
2136 nframes_t adjusted_frame;
2138 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2139 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2145 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2146 snap_to (adjusted_frame);
2149 if (adjusted_frame == drag_info.last_pointer_frame) return;
2151 marker->set_position (adjusted_frame);
2154 drag_info.last_pointer_frame = adjusted_frame;
2155 drag_info.first_move = false;
2157 show_verbose_time_cursor (adjusted_frame, 10);
2161 Editor::meter_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2163 if (drag_info.first_move) return;
2165 meter_marker_drag_motion_callback (drag_info.item, event);
2167 MeterMarker* marker = (MeterMarker *) drag_info.data;
2170 TempoMap& map (session->tempo_map());
2171 map.bbt_time (drag_info.last_pointer_frame, when);
2173 if (drag_info.copy == true) {
2174 begin_reversible_command (_("copy meter mark"));
2175 XMLNode &before = map.get_state();
2176 map.add_meter (marker->meter(), when);
2177 XMLNode &after = map.get_state();
2178 session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2179 commit_reversible_command ();
2181 // delete the dummy marker we used for visual representation of copying.
2182 // a new visual marker will show up automatically.
2185 begin_reversible_command (_("move meter mark"));
2186 XMLNode &before = map.get_state();
2187 map.move_meter (marker->meter(), when);
2188 XMLNode &after = map.get_state();
2189 session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2190 commit_reversible_command ();
2195 Editor::start_tempo_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2198 TempoMarker* tempo_marker;
2200 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2201 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2205 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2206 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2210 MetricSection& section (tempo_marker->tempo());
2212 if (!section.movable()) {
2216 drag_info.item = item;
2217 drag_info.data = marker;
2218 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2219 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2223 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2224 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2228 Editor::start_tempo_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2231 TempoMarker* tempo_marker;
2233 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2234 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2238 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2239 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2243 // create a dummy marker for visual representation of moving the copy.
2244 // The actual copying is not done before we reach the finish callback.
2246 snprintf (name, sizeof (name), "%.2f", tempo_marker->tempo().beats_per_minute());
2247 TempoMarker* new_marker = new TempoMarker(*this, *tempo_group, ARDOUR_UI::config()->canvasvar_TempoMarker.get(), name,
2248 *new TempoSection(tempo_marker->tempo()));
2250 drag_info.item = &new_marker->the_item();
2251 drag_info.copy = true;
2252 drag_info.data = new_marker;
2253 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2254 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2258 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2260 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2264 Editor::tempo_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2266 TempoMarker* marker = (TempoMarker *) drag_info.data;
2267 nframes_t adjusted_frame;
2269 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2270 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2276 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2277 snap_to (adjusted_frame);
2280 if (adjusted_frame == drag_info.last_pointer_frame) return;
2282 /* OK, we've moved far enough to make it worth actually move the thing. */
2284 marker->set_position (adjusted_frame);
2286 show_verbose_time_cursor (adjusted_frame, 10);
2288 drag_info.last_pointer_frame = adjusted_frame;
2289 drag_info.first_move = false;
2293 Editor::tempo_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2295 if (drag_info.first_move) return;
2297 tempo_marker_drag_motion_callback (drag_info.item, event);
2299 TempoMarker* marker = (TempoMarker *) drag_info.data;
2302 TempoMap& map (session->tempo_map());
2303 map.bbt_time (drag_info.last_pointer_frame, when);
2305 if (drag_info.copy == true) {
2306 begin_reversible_command (_("copy tempo mark"));
2307 XMLNode &before = map.get_state();
2308 map.add_tempo (marker->tempo(), when);
2309 XMLNode &after = map.get_state();
2310 session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2311 commit_reversible_command ();
2313 // delete the dummy marker we used for visual representation of copying.
2314 // a new visual marker will show up automatically.
2317 begin_reversible_command (_("move tempo mark"));
2318 XMLNode &before = map.get_state();
2319 map.move_tempo (marker->tempo(), when);
2320 XMLNode &after = map.get_state();
2321 session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2322 commit_reversible_command ();
2327 Editor::remove_gain_control_point (ArdourCanvas::Item*item, GdkEvent* event)
2329 ControlPoint* control_point;
2331 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2332 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2336 // We shouldn't remove the first or last gain point
2337 if (control_point->line.is_last_point(*control_point) ||
2338 control_point->line.is_first_point(*control_point)) {
2342 control_point->line.remove_point (*control_point);
2346 Editor::remove_control_point (ArdourCanvas::Item*item, GdkEvent* event)
2348 ControlPoint* control_point;
2350 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2351 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2355 control_point->line.remove_point (*control_point);
2359 Editor::start_control_point_grab (ArdourCanvas::Item* item, GdkEvent* event)
2361 ControlPoint* control_point;
2363 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2364 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2368 drag_info.item = item;
2369 drag_info.data = control_point;
2370 drag_info.motion_callback = &Editor::control_point_drag_motion_callback;
2371 drag_info.finished_callback = &Editor::control_point_drag_finished_callback;
2373 start_grab (event, fader_cursor);
2375 control_point->line.start_drag (control_point, drag_info.grab_frame, 0);
2377 float fraction = 1.0 - ((control_point->get_y() - control_point->line.y_position()) / control_point->line.height());
2378 set_verbose_canvas_cursor (control_point->line.get_verbose_cursor_string (fraction),
2379 drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
2381 show_verbose_canvas_cursor ();
2385 Editor::control_point_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2387 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2389 double cx = drag_info.current_pointer_x;
2390 double cy = drag_info.current_pointer_y;
2392 drag_info.cumulative_x_drag = cx - drag_info.grab_x ;
2393 drag_info.cumulative_y_drag = cy - drag_info.grab_y ;
2395 if (drag_info.x_constrained) {
2396 cx = drag_info.grab_x;
2398 if (drag_info.y_constrained) {
2399 cy = drag_info.grab_y;
2402 cp->line.parent_group().w2i (cx, cy);
2406 cy = min ((double) (cp->line.y_position() + cp->line.height()), cy);
2408 //translate cx to frames
2409 nframes_t cx_frames = unit_to_frame (cx);
2411 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier()) && !drag_info.x_constrained) {
2412 snap_to (cx_frames);
2415 float const fraction = 1.0 - ((cy - cp->line.y_position()) / cp->line.height());
2419 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
2425 cp->line.point_drag (*cp, cx_frames , fraction, push);
2427 set_verbose_canvas_cursor_text (cp->line.get_verbose_cursor_string (fraction));
2429 drag_info.first_move = false;
2433 Editor::control_point_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2435 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2437 if (drag_info.first_move) {
2441 if ((event->type == GDK_BUTTON_RELEASE) && (event->button.button == 1) && Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
2442 reset_point_selection ();
2446 control_point_drag_motion_callback (item, event);
2448 cp->line.end_drag (cp);
2452 Editor::start_line_grab_from_regionview (ArdourCanvas::Item* item, GdkEvent* event)
2454 switch (mouse_mode) {
2456 assert(dynamic_cast<AudioRegionView*>(clicked_regionview));
2457 start_line_grab (dynamic_cast<AudioRegionView*>(clicked_regionview)->get_gain_line(), event);
2465 Editor::start_line_grab_from_line (ArdourCanvas::Item* item, GdkEvent* event)
2469 if ((al = reinterpret_cast<AutomationLine*> (item->get_data ("line"))) == 0) {
2470 fatal << _("programming error: line canvas item has no line pointer!") << endmsg;
2474 start_line_grab (al, event);
2478 Editor::start_line_grab (AutomationLine* line, GdkEvent* event)
2482 nframes_t frame_within_region;
2484 /* need to get x coordinate in terms of parent (TimeAxisItemView)
2488 cx = event->button.x;
2489 cy = event->button.y;
2490 line->parent_group().w2i (cx, cy);
2491 frame_within_region = (nframes_t) floor (cx * frames_per_unit);
2493 if (!line->control_points_adjacent (frame_within_region, current_line_drag_info.before,
2494 current_line_drag_info.after)) {
2495 /* no adjacent points */
2499 drag_info.item = &line->grab_item();
2500 drag_info.data = line;
2501 drag_info.motion_callback = &Editor::line_drag_motion_callback;
2502 drag_info.finished_callback = &Editor::line_drag_finished_callback;
2504 start_grab (event, fader_cursor);
2506 double const fraction = 1.0 - ((cy - line->y_position()) / line->height());
2508 line->start_drag (0, drag_info.grab_frame, fraction);
2510 set_verbose_canvas_cursor (line->get_verbose_cursor_string (fraction),
2511 drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
2512 show_verbose_canvas_cursor ();
2516 Editor::line_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2518 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
2519 double cx = drag_info.current_pointer_x;
2520 double cy = drag_info.current_pointer_y;
2522 line->parent_group().w2i (cx, cy);
2524 double const fraction = 1.0 - ((cy - line->y_position()) / line->height());
2528 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
2534 line->line_drag (current_line_drag_info.before, current_line_drag_info.after, fraction, push);
2536 set_verbose_canvas_cursor_text (line->get_verbose_cursor_string (fraction));
2540 Editor::line_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2542 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
2543 line_drag_motion_callback (item, event);
2548 Editor::start_region_grab (ArdourCanvas::Item* item, GdkEvent* event)
2550 if (selection->regions.empty() || clicked_regionview == 0) {
2554 drag_info.copy = false;
2555 drag_info.item = item;
2556 drag_info.data = clicked_regionview;
2557 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2558 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2563 TimeAxisView* tvp = clicked_axisview;
2564 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2566 if (tv && tv->is_track()) {
2567 speed = tv->get_diskstream()->speed();
2570 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2571 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2572 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2573 // we want a move threshold
2574 drag_info.want_move_threshold = true;
2576 show_verbose_time_cursor (drag_info.last_frame_position, 10);
2578 begin_reversible_command (_("move region(s)"));
2582 Editor::start_region_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2584 if (selection->regions.empty() || clicked_regionview == 0) {
2588 drag_info.copy = true;
2589 drag_info.item = item;
2590 drag_info.data = clicked_regionview;
2594 TimeAxisView* tv = &clicked_regionview->get_time_axis_view();
2595 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
2598 if (rtv && rtv->is_track()) {
2599 speed = rtv->get_diskstream()->speed();
2602 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2603 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2604 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2605 // we want a move threshold
2606 drag_info.want_move_threshold = true;
2607 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2608 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2609 show_verbose_time_cursor (drag_info.last_frame_position, 10);
2613 Editor::start_region_brush_grab (ArdourCanvas::Item* item, GdkEvent* event)
2615 if (selection->regions.empty() || clicked_regionview == 0) {
2619 drag_info.copy = false;
2620 drag_info.item = item;
2621 drag_info.data = clicked_regionview;
2622 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2623 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2628 TimeAxisView* tvp = clicked_axisview;
2629 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2631 if (tv && tv->is_track()) {
2632 speed = tv->get_diskstream()->speed();
2635 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2636 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2637 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2638 // we want a move threshold
2639 drag_info.want_move_threshold = true;
2640 drag_info.brushing = true;
2642 begin_reversible_command (_("Drag region brush"));
2646 Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2650 RegionView* rv = reinterpret_cast<RegionView*> (drag_info.data);
2651 nframes_t pending_region_position = 0;
2652 int32_t pointer_y_span = 0, canvas_pointer_y_span = 0, original_pointer_order;
2653 int32_t visible_y_high = 0, visible_y_low = 512; //high meaning higher numbered.. not the height on the screen
2654 bool clamp_y_axis = false;
2655 vector<int32_t> height_list(512) ;
2656 vector<int32_t>::iterator j;
2658 if (drag_info.copy && drag_info.move_threshold_passed && drag_info.want_move_threshold) {
2660 drag_info.want_move_threshold = false; // don't copy again
2662 /* duplicate the region(s) */
2664 vector<RegionView*> new_regionviews;
2666 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
2672 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
2673 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
2676 nrv = new AudioRegionView (*arv);
2678 nrv = new MidiRegionView (*mrv);
2683 nrv->get_canvas_group()->show ();
2685 new_regionviews.push_back (nrv);
2688 if (new_regionviews.empty()) {
2692 /* reset selection to new regionviews */
2694 selection->set (new_regionviews);
2696 /* reset drag_info data to reflect the fact that we are dragging the copies */
2698 drag_info.data = new_regionviews.front();
2700 swap_grab (new_regionviews.front()->get_canvas_group (), 0, event->motion.time);
2703 /* Which trackview is this ? */
2705 TimeAxisView* tvp = trackview_by_y_position (drag_info.current_pointer_y);
2706 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2708 /* The region motion is only processed if the pointer is over
2712 if (!tv || !tv->is_track()) {
2713 /* To make sure we hide the verbose canvas cursor when the mouse is
2714 not held over a track.
2716 hide_verbose_canvas_cursor ();
2720 original_pointer_order = drag_info.last_trackview->order;
2722 /************************************************************
2724 ************************************************************/
2726 if (drag_info.brushing) {
2727 clamp_y_axis = true;
2732 if ((pointer_y_span = (drag_info.last_trackview->order - tv->order)) != 0) {
2734 int32_t children = 0, numtracks = 0;
2735 // XXX hard coding track limit, oh my, so very very bad
2736 bitset <1024> tracks (0x00);
2737 /* get a bitmask representing the visible tracks */
2739 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2740 TimeAxisView *tracklist_timeview;
2741 tracklist_timeview = (*i);
2742 RouteTimeAxisView* rtv2 = dynamic_cast<RouteTimeAxisView*>(tracklist_timeview);
2743 TimeAxisView::Children children_list;
2745 /* zeroes are audio tracks. ones are other types. */
2747 if (!rtv2->hidden()) {
2749 if (visible_y_high < rtv2->order) {
2750 visible_y_high = rtv2->order;
2752 if (visible_y_low > rtv2->order) {
2753 visible_y_low = rtv2->order;
2756 if (!rtv2->is_track()) {
2757 tracks = tracks |= (0x01 << rtv2->order);
2760 height_list[rtv2->order] = (*i)->height;
2762 if ((children_list = rtv2->get_child_list()).size() > 0) {
2763 for (TimeAxisView::Children::iterator j = children_list.begin(); j != children_list.end(); ++j) {
2764 tracks = tracks |= (0x01 << (rtv2->order + children));
2765 height_list[rtv2->order + children] = (*j)->height;
2773 /* find the actual span according to the canvas */
2775 canvas_pointer_y_span = pointer_y_span;
2776 if (drag_info.last_trackview->order >= tv->order) {
2778 for (y = tv->order; y < drag_info.last_trackview->order; y++) {
2779 if (height_list[y] == 0 ) {
2780 canvas_pointer_y_span--;
2785 for (y = drag_info.last_trackview->order;y <= tv->order; y++) {
2786 if ( height_list[y] == 0 ) {
2787 canvas_pointer_y_span++;
2792 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
2793 RegionView* rv2 = (*i);
2794 double ix1, ix2, iy1, iy2;
2797 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
2798 rv2->get_canvas_group()->i2w (ix1, iy1);
2799 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
2800 RouteTimeAxisView* rtv2 = dynamic_cast<RouteTimeAxisView*>(tvp2);
2802 if (rtv2->order != original_pointer_order) {
2803 /* this isn't the pointer track */
2805 if (canvas_pointer_y_span > 0) {
2807 /* moving up the canvas */
2808 if ((rtv2->order - canvas_pointer_y_span) >= visible_y_low) {
2810 int32_t visible_tracks = 0;
2811 while (visible_tracks < canvas_pointer_y_span ) {
2814 while (height_list[rtv2->order - (visible_tracks - n)] == 0) {
2815 /* we're passing through a hidden track */
2820 if (tracks[rtv2->order - (canvas_pointer_y_span - n)] != 0x00) {
2821 clamp_y_axis = true;
2825 clamp_y_axis = true;
2828 } else if (canvas_pointer_y_span < 0) {
2830 /*moving down the canvas*/
2832 if ((rtv2->order - (canvas_pointer_y_span - n)) <= visible_y_high) { // we will overflow
2835 int32_t visible_tracks = 0;
2837 while (visible_tracks > canvas_pointer_y_span ) {
2840 while (height_list[rtv2->order - (visible_tracks - n)] == 0) {
2844 if ( tracks[rtv2->order - ( canvas_pointer_y_span - n)] != 0x00) {
2845 clamp_y_axis = true;
2850 clamp_y_axis = true;
2856 /* this is the pointer's track */
2857 if ((rtv2->order - pointer_y_span) > visible_y_high) { // we will overflow
2858 clamp_y_axis = true;
2859 } else if ((rtv2->order - pointer_y_span) < visible_y_low) { // we will underflow
2860 clamp_y_axis = true;
2868 } else if (drag_info.last_trackview == tv) {
2869 clamp_y_axis = true;
2873 if (!clamp_y_axis) {
2874 drag_info.last_trackview = tv;
2877 /************************************************************
2879 ************************************************************/
2881 /* compute the amount of pointer motion in frames, and where
2882 the region would be if we moved it by that much.
2885 if (drag_info.move_threshold_passed) {
2887 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2889 nframes_t sync_frame;
2890 nframes_t sync_offset;
2893 pending_region_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2895 sync_offset = rv->region()->sync_offset (sync_dir);
2896 sync_frame = rv->region()->adjust_to_sync (pending_region_position);
2898 /* we snap if the snap modifier is not enabled.
2901 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2902 snap_to (sync_frame);
2905 if (sync_frame - sync_offset <= sync_frame) {
2906 pending_region_position = sync_frame - (sync_dir*sync_offset);
2908 pending_region_position = 0;
2912 pending_region_position = 0;
2915 if (pending_region_position > max_frames - rv->region()->length()) {
2916 pending_region_position = drag_info.last_frame_position;
2919 // printf ("3: pending_region_position= %lu %lu\n", pending_region_position, drag_info.last_frame_position );
2921 if (pending_region_position != drag_info.last_frame_position && !drag_info.x_constrained) {
2923 /* now compute the canvas unit distance we need to move the regiondrag_info.last_trackview->order
2924 to make it appear at the new location.
2927 if (pending_region_position > drag_info.last_frame_position) {
2928 x_delta = ((double) (pending_region_position - drag_info.last_frame_position) / frames_per_unit);
2930 x_delta = -((double) (drag_info.last_frame_position - pending_region_position) / frames_per_unit);
2933 drag_info.last_frame_position = pending_region_position;
2940 /* threshold not passed */
2945 /*************************************************************
2947 ************************************************************/
2949 if (x_delta == 0 && (pointer_y_span == 0)) {
2950 /* haven't reached next snap point, and we're not switching
2951 trackviews. nothing to do.
2958 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
2960 RegionView* rv2 = (*i);
2962 // If any regionview is at zero, we need to know so we can stop further leftward motion.
2964 double ix1, ix2, iy1, iy2;
2965 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
2966 rv2->get_canvas_group()->i2w (ix1, iy1);
2975 /*************************************************************
2977 ************************************************************/
2981 if (drag_info.first_move) {
2982 if (drag_info.move_threshold_passed) {
2993 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
2994 const list<RegionView*>& layered_regions = selection->regions.by_layer();
2996 for (list<RegionView*>::const_iterator i = layered_regions.begin(); i != layered_regions.end(); ++i) {
2998 RegionView* rv = (*i);
2999 double ix1, ix2, iy1, iy2;
3000 int32_t temp_pointer_y_span = pointer_y_span;
3002 /* get item BBox, which will be relative to parent. so we have
3003 to query on a child, then convert to world coordinates using
3007 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3008 rv->get_canvas_group()->i2w (ix1, iy1);
3009 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3010 RouteTimeAxisView* canvas_rtv = dynamic_cast<RouteTimeAxisView*>(tvp2);
3011 RouteTimeAxisView* temp_rtv;
3013 if ((pointer_y_span != 0) && !clamp_y_axis) {
3016 for (j = height_list.begin(); j!= height_list.end(); j++) {
3017 if (x == canvas_rtv->order) {
3018 /* we found the track the region is on */
3019 if (x != original_pointer_order) {
3020 /*this isn't from the same track we're dragging from */
3021 temp_pointer_y_span = canvas_pointer_y_span;
3023 while (temp_pointer_y_span > 0) {
3024 /* we're moving up canvas-wise,
3025 so we need to find the next track height
3027 if (j != height_list.begin()) {
3030 if (x != original_pointer_order) {
3031 /* we're not from the dragged track, so ignore hidden tracks. */
3033 temp_pointer_y_span++;
3037 temp_pointer_y_span--;
3039 while (temp_pointer_y_span < 0) {
3041 if (x != original_pointer_order) {
3043 temp_pointer_y_span--;
3047 if (j != height_list.end()) {
3050 temp_pointer_y_span++;
3052 /* find out where we'll be when we move and set height accordingly */
3054 tvp2 = trackview_by_y_position (iy1 + y_delta);
3055 temp_rtv = dynamic_cast<RouteTimeAxisView*>(tvp2);
3056 rv->set_y_position_and_height (0, temp_rtv->height);
3058 /* if you un-comment the following, the region colours will follow the track colours whilst dragging,
3059 personally, i think this can confuse things, but never mind.
3062 //const GdkColor& col (temp_rtv->view->get_region_color());
3063 //rv->set_color (const_cast<GdkColor&>(col));
3070 /* prevent the regionview from being moved to before
3071 the zero position on the canvas.
3076 if (-x_delta > ix1) {
3079 } else if ((x_delta > 0) && (rv->region()->last_frame() > max_frames - x_delta)) {
3080 x_delta = max_frames - rv->region()->last_frame();
3084 if (drag_info.first_move) {
3086 /* hide any dependent views */
3088 rv->get_time_axis_view().hide_dependent_views (*rv);
3090 /* this is subtle. raising the regionview itself won't help,
3091 because raise_to_top() just puts the item on the top of
3092 its parent's stack. so, we need to put the trackview canvas_display group
3093 on the top, since its parent is the whole canvas.
3096 rv->get_canvas_group()->raise_to_top();
3097 rv->get_time_axis_view().canvas_display->raise_to_top();
3098 cursor_group->raise_to_top();
3100 rv->fake_set_opaque (true);
3103 if (drag_info.brushing) {
3104 mouse_brush_insert_region (rv, pending_region_position);
3106 rv->move (x_delta, y_delta);
3109 } /* foreach region */
3113 if (drag_info.first_move && drag_info.move_threshold_passed) {
3114 cursor_group->raise_to_top();
3115 drag_info.first_move = false;
3118 if (x_delta != 0 && !drag_info.brushing) {
3119 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3124 Editor::region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3127 RegionView* rv = reinterpret_cast<RegionView *> (drag_info.data);
3128 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
3129 bool nocommit = true;
3131 RouteTimeAxisView* rtv;
3132 bool regionview_y_movement;
3133 bool regionview_x_movement;
3134 vector<RegionView*> copies;
3136 /* first_move is set to false if the regionview has been moved in the
3140 if (drag_info.first_move) {
3147 /* The regionview has been moved at some stage during the grab so we need
3148 to account for any mouse movement between this event and the last one.
3151 region_drag_motion_callback (item, event);
3153 if (drag_info.brushing) {
3154 /* all changes were made during motion event handlers */
3156 if (drag_info.copy) {
3157 for (list<RegionView*>::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
3158 copies.push_back (*i);
3165 /* adjust for track speed */
3168 rtv = dynamic_cast<RouteTimeAxisView*> (drag_info.last_trackview);
3169 if (rtv && rtv->get_diskstream()) {
3170 speed = rtv->get_diskstream()->speed();
3173 regionview_x_movement = (drag_info.last_frame_position != (nframes_t) (rv->region()->position()/speed));
3174 regionview_y_movement = (drag_info.last_trackview != &rv->get_time_axis_view());
3176 //printf ("last_frame: %s position is %lu %g\n", rv->get_time_axis_view().name().c_str(), drag_info.last_frame_position, speed);
3177 //printf ("last_rackview: %s \n", drag_info.last_trackview->name().c_str());
3181 if (drag_info.copy) {
3182 if (drag_info.x_constrained) {
3183 op_string = _("fixed time region copy");
3185 op_string = _("region copy");
3188 if (drag_info.x_constrained) {
3189 op_string = _("fixed time region drag");
3191 op_string = _("region drag");
3195 begin_reversible_command (op_string);
3197 if (regionview_y_movement) {
3199 /* moved to a different audio track. */
3201 vector<RegionView*> new_selection;
3203 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ) {
3205 RegionView* rv = (*i);
3207 double ix1, ix2, iy1, iy2;
3209 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3210 rv->get_canvas_group()->i2w (ix1, iy1);
3211 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3212 RouteTimeAxisView* rtv2 = dynamic_cast<RouteTimeAxisView*>(tvp2);
3214 boost::shared_ptr<Playlist> from_playlist = rv->region()->playlist();
3215 boost::shared_ptr<Playlist> to_playlist = rtv2->playlist();
3217 where = (nframes_t) (unit_to_frame (ix1) * speed);
3218 boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region()));
3220 /* undo the previous hide_dependent_views so that xfades don't
3221 disappear on copying regions
3224 rv->get_time_axis_view().reveal_dependent_views (*rv);
3226 if (!drag_info.copy) {
3228 /* the region that used to be in the old playlist is not
3229 moved to the new one - we make a copy of it. as a result,
3230 any existing editor for the region should no longer be
3234 rv->hide_region_editor();
3235 rv->fake_set_opaque (false);
3237 session->add_command (new MementoCommand<Playlist>(*from_playlist, &from_playlist->get_state(), 0));
3238 from_playlist->remove_region ((rv->region()));
3239 session->add_command (new MementoCommand<Playlist>(*from_playlist, 0, &from_playlist->get_state()));
3243 /* the regionview we dragged around is a temporary copy, queue it for deletion */
3245 copies.push_back (rv);
3248 latest_regionview = 0;
3250 sigc::connection c = rtv2->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3251 session->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
3252 to_playlist->add_region (new_region, where);
3253 session->add_command (new MementoCommand<Playlist>(*to_playlist, 0, &to_playlist->get_state()));
3256 if (latest_regionview) {
3257 new_selection.push_back (latest_regionview);
3260 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
3261 was selected in all of them, then removing it from the playlist will have removed all
3262 trace of it from the selection (i.e. there were N regions selected, we removed 1,
3263 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
3264 corresponding regionview, and the selection is now empty).
3266 this could have invalidated any and all iterators into the region selection.
3268 the heuristic we use here is: if the region selection is empty, break out of the loop
3269 here. if the region selection is not empty, then restart the loop because we know that
3270 we must have removed at least the region(view) we've just been working on as well as any
3271 that we processed on previous iterations.
3273 EXCEPT .... if we are doing a copy drag, then the selection hasn't been modified and
3274 we can just iterate.
3277 if (drag_info.copy) {
3280 if (selection->regions.empty()) {
3283 i = selection->regions.by_layer().begin();
3288 selection->set (new_selection);
3292 /* motion within a single track */
3294 list<RegionView*> regions = selection->regions.by_layer();
3296 if (drag_info.copy) {
3297 selection->clear_regions();
3300 for (list<RegionView*>::iterator i = regions.begin(); i != regions.end(); ++i) {
3304 if (!rv->region()->can_move()) {
3308 if (regionview_x_movement) {
3309 double ownspeed = 1.0;
3310 rtv = dynamic_cast<RouteTimeAxisView*> (&(rv->get_time_axis_view()));
3312 if (rtv && rtv->get_diskstream()) {
3313 ownspeed = rtv->get_diskstream()->speed();
3316 /* base the new region position on the current position of the regionview.*/
3318 double ix1, ix2, iy1, iy2;
3320 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3321 rv->get_canvas_group()->i2w (ix1, iy1);
3322 where = (nframes_t) (unit_to_frame (ix1) * ownspeed);
3326 where = rv->region()->position();
3329 boost::shared_ptr<Playlist> to_playlist = rv->region()->playlist();
3331 assert (to_playlist);
3335 session->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
3337 if (drag_info.copy) {
3339 boost::shared_ptr<Region> newregion;
3340 boost::shared_ptr<Region> ar;
3341 boost::shared_ptr<Region> mr;
3343 if ((ar = boost::dynamic_pointer_cast<AudioRegion>(rv->region())) != 0) {
3344 newregion = RegionFactory::create (ar);
3345 } else if ((mr = boost::dynamic_pointer_cast<MidiRegion>(rv->region())) != 0) {
3346 newregion = RegionFactory::create (mr);
3351 latest_regionview = 0;
3352 sigc::connection c = rtv->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3353 to_playlist->add_region (newregion, (nframes_t) (where * rtv->get_diskstream()->speed()));
3356 if (latest_regionview) {
3357 rtv->reveal_dependent_views (*latest_regionview);
3358 selection->add (latest_regionview);
3363 /* just change the model */
3365 rv->region()->set_position (where, (void*) this);
3371 session->add_command (new MementoCommand<Playlist>(*to_playlist, 0, &to_playlist->get_state()));
3373 if (drag_info.copy) {
3374 copies.push_back (rv);
3382 commit_reversible_command ();
3385 for (vector<RegionView*>::iterator x = copies.begin(); x != copies.end(); ++x) {
3391 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
3393 /* Either add to or set the set the region selection, unless
3394 this is an alignment click (control used)
3397 if (Keyboard::modifier_state_contains (event->state, Keyboard::Control)) {
3398 TimeAxisView* tv = &rv.get_time_axis_view();
3399 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
3401 if (rtv && rtv->is_track()) {
3402 speed = rtv->get_diskstream()->speed();
3405 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Alt))) {
3407 align_region (rv.region(), SyncPoint, (nframes_t) (edit_cursor->current_frame * speed));
3409 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Shift))) {
3411 align_region (rv.region(), End, (nframes_t) (edit_cursor->current_frame * speed));
3415 align_region (rv.region(), Start, (nframes_t) (edit_cursor->current_frame * speed));
3421 Editor::show_verbose_time_cursor (nframes_t frame, double offset, double xpos, double ypos)
3427 nframes_t frame_rate;
3434 switch (Profile->get_small_screen() ? ARDOUR_UI::instance()->primary_clock.mode () : ARDOUR_UI::instance()->secondary_clock.mode ()) {
3435 case AudioClock::BBT:
3436 session->bbt_time (frame, bbt);
3437 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, bbt.bars, bbt.beats, bbt.ticks);
3440 case AudioClock::SMPTE:
3441 session->smpte_time (frame, smpte);
3442 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
3445 case AudioClock::MinSec:
3446 /* XXX this is copied from show_verbose_duration_cursor() */
3447 frame_rate = session->frame_rate();
3448 hours = frame / (frame_rate * 3600);
3449 frame = frame % (frame_rate * 3600);
3450 mins = frame / (frame_rate * 60);
3451 frame = frame % (frame_rate * 60);
3452 secs = (float) frame / (float) frame_rate;
3453 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
3457 snprintf (buf, sizeof(buf), "%u", frame);
3461 if (xpos >= 0 && ypos >=0) {
3462 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
3465 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
3467 show_verbose_canvas_cursor ();
3471 Editor::show_verbose_duration_cursor (nframes_t start, nframes_t end, double offset, double xpos, double ypos)
3478 nframes_t distance, frame_rate;
3480 Meter meter_at_start(session->tempo_map().meter_at(start));
3486 switch (ARDOUR_UI::instance()->secondary_clock.mode ()) {
3487 case AudioClock::BBT:
3488 session->bbt_time (start, sbbt);
3489 session->bbt_time (end, ebbt);
3492 /* XXX this computation won't work well if the
3493 user makes a selection that spans any meter changes.
3496 ebbt.bars -= sbbt.bars;
3497 if (ebbt.beats >= sbbt.beats) {
3498 ebbt.beats -= sbbt.beats;
3501 ebbt.beats = int(meter_at_start.beats_per_bar()) + ebbt.beats - sbbt.beats;
3503 if (ebbt.ticks >= sbbt.ticks) {
3504 ebbt.ticks -= sbbt.ticks;
3507 ebbt.ticks = int(Meter::ticks_per_beat) + ebbt.ticks - sbbt.ticks;
3510 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, ebbt.bars, ebbt.beats, ebbt.ticks);
3513 case AudioClock::SMPTE:
3514 session->smpte_duration (end - start, smpte);
3515 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
3518 case AudioClock::MinSec:
3519 /* XXX this stuff should be elsewhere.. */
3520 distance = end - start;
3521 frame_rate = session->frame_rate();
3522 hours = distance / (frame_rate * 3600);
3523 distance = distance % (frame_rate * 3600);
3524 mins = distance / (frame_rate * 60);
3525 distance = distance % (frame_rate * 60);
3526 secs = (float) distance / (float) frame_rate;
3527 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
3531 snprintf (buf, sizeof(buf), "%u", end - start);
3535 if (xpos >= 0 && ypos >=0) {
3536 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
3539 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
3541 show_verbose_canvas_cursor ();
3545 Editor::collect_new_region_view (RegionView* rv)
3547 latest_regionview = rv;
3551 Editor::start_selection_grab (ArdourCanvas::Item* item, GdkEvent* event)
3553 if (clicked_regionview == 0) {
3557 /* lets try to create new Region for the selection */
3559 vector<boost::shared_ptr<AudioRegion> > new_regions;
3560 create_region_from_selection (new_regions);
3562 if (new_regions.empty()) {
3566 /* XXX fix me one day to use all new regions */
3568 boost::shared_ptr<Region> region (new_regions.front());
3570 /* add it to the current stream/playlist.
3572 tricky: the streamview for the track will add a new regionview. we will
3573 catch the signal it sends when it creates the regionview to
3574 set the regionview we want to then drag.
3577 latest_regionview = 0;
3578 sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3580 /* A selection grab currently creates two undo/redo operations, one for
3581 creating the new region and another for moving it.
3584 begin_reversible_command (_("selection grab"));
3586 boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
3588 XMLNode *before = &(playlist->get_state());
3589 clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
3590 XMLNode *after = &(playlist->get_state());
3591 session->add_command(new MementoCommand<Playlist>(*playlist, before, after));
3593 commit_reversible_command ();
3597 if (latest_regionview == 0) {
3598 /* something went wrong */
3602 /* we need to deselect all other regionviews, and select this one
3603 i'm ignoring undo stuff, because the region creation will take care of it */
3604 selection->set (latest_regionview);
3606 drag_info.item = latest_regionview->get_canvas_group();
3607 drag_info.data = latest_regionview;
3608 drag_info.motion_callback = &Editor::region_drag_motion_callback;
3609 drag_info.finished_callback = &Editor::region_drag_finished_callback;
3613 drag_info.last_trackview = clicked_axisview;
3614 drag_info.last_frame_position = latest_regionview->region()->position();
3615 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
3617 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3621 Editor::cancel_selection ()
3623 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
3624 (*i)->hide_selection ();
3626 begin_reversible_command (_("cancel selection"));
3627 selection->clear ();
3628 clicked_selection = 0;
3629 commit_reversible_command ();
3633 Editor::start_selection_op (ArdourCanvas::Item* item, GdkEvent* event, SelectionOp op)
3635 nframes_t start = 0;
3642 drag_info.item = item;
3643 drag_info.motion_callback = &Editor::drag_selection;
3644 drag_info.finished_callback = &Editor::end_selection_op;
3649 case CreateSelection:
3650 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
3651 drag_info.copy = true;
3653 drag_info.copy = false;
3655 start_grab (event, selector_cursor);
3658 case SelectionStartTrim:
3659 if (clicked_axisview) {
3660 clicked_axisview->order_selection_trims (item, true);
3662 start_grab (event, trimmer_cursor);
3663 start = selection->time[clicked_selection].start;
3664 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
3667 case SelectionEndTrim:
3668 if (clicked_axisview) {
3669 clicked_axisview->order_selection_trims (item, false);
3671 start_grab (event, trimmer_cursor);
3672 end = selection->time[clicked_selection].end;
3673 drag_info.pointer_frame_offset = drag_info.grab_frame - end;
3677 start = selection->time[clicked_selection].start;
3679 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
3683 if (selection_op == SelectionMove) {
3684 show_verbose_time_cursor(start, 10);
3686 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
3691 Editor::drag_selection (ArdourCanvas::Item* item, GdkEvent* event)
3693 nframes_t start = 0;
3696 nframes_t pending_position;
3698 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
3699 pending_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
3701 pending_position = 0;
3704 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3705 snap_to (pending_position);
3708 /* only alter selection if the current frame is
3709 different from the last frame position (adjusted)
3712 if (pending_position == drag_info.last_pointer_frame) return;
3714 switch (selection_op) {
3715 case CreateSelection:
3717 if (drag_info.first_move) {
3718 snap_to (drag_info.grab_frame);
3721 if (pending_position < drag_info.grab_frame) {
3722 start = pending_position;
3723 end = drag_info.grab_frame;
3725 end = pending_position;
3726 start = drag_info.grab_frame;
3729 /* first drag: Either add to the selection
3730 or create a new selection->
3733 if (drag_info.first_move) {
3735 begin_reversible_command (_("range selection"));
3737 if (drag_info.copy) {
3738 /* adding to the selection */
3739 clicked_selection = selection->add (start, end);
3740 drag_info.copy = false;
3742 /* new selection-> */
3743 clicked_selection = selection->set (clicked_axisview, start, end);
3748 case SelectionStartTrim:
3750 if (drag_info.first_move) {
3751 begin_reversible_command (_("trim selection start"));
3754 start = selection->time[clicked_selection].start;
3755 end = selection->time[clicked_selection].end;
3757 if (pending_position > end) {
3760 start = pending_position;
3764 case SelectionEndTrim:
3766 if (drag_info.first_move) {
3767 begin_reversible_command (_("trim selection end"));
3770 start = selection->time[clicked_selection].start;
3771 end = selection->time[clicked_selection].end;
3773 if (pending_position < start) {
3776 end = pending_position;
3783 if (drag_info.first_move) {
3784 begin_reversible_command (_("move selection"));
3787 start = selection->time[clicked_selection].start;
3788 end = selection->time[clicked_selection].end;
3790 length = end - start;
3792 start = pending_position;
3795 end = start + length;
3800 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
3801 start_canvas_autoscroll (1);
3805 selection->replace (clicked_selection, start, end);
3808 drag_info.last_pointer_frame = pending_position;
3809 drag_info.first_move = false;
3811 if (selection_op == SelectionMove) {
3812 show_verbose_time_cursor(start, 10);
3814 show_verbose_time_cursor(pending_position, 10);
3819 Editor::end_selection_op (ArdourCanvas::Item* item, GdkEvent* event)
3821 if (!drag_info.first_move) {
3822 drag_selection (item, event);
3823 /* XXX this is not object-oriented programming at all. ick */
3824 if (selection->time.consolidate()) {
3825 selection->TimeChanged ();
3827 commit_reversible_command ();
3829 /* just a click, no pointer movement.*/
3831 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
3833 selection->clear_time();
3838 /* XXX what happens if its a music selection? */
3839 session->set_audio_range (selection->time);
3840 stop_canvas_autoscroll ();
3844 Editor::start_trim (ArdourCanvas::Item* item, GdkEvent* event)
3847 TimeAxisView* tvp = clicked_axisview;
3848 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
3850 if (tv && tv->is_track()) {
3851 speed = tv->get_diskstream()->speed();
3854 nframes_t region_start = (nframes_t) (clicked_regionview->region()->position() / speed);
3855 nframes_t region_end = (nframes_t) (clicked_regionview->region()->last_frame() / speed);
3856 nframes_t region_length = (nframes_t) (clicked_regionview->region()->length() / speed);
3858 //drag_info.item = clicked_regionview->get_name_highlight();
3859 drag_info.item = item;
3860 drag_info.motion_callback = &Editor::trim_motion_callback;
3861 drag_info.finished_callback = &Editor::trim_finished_callback;
3863 start_grab (event, trimmer_cursor);
3865 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
3866 trim_op = ContentsTrim;
3868 /* These will get overridden for a point trim.*/
3869 if (drag_info.current_pointer_frame < (region_start + region_length/2)) {
3870 /* closer to start */
3871 trim_op = StartTrim;
3872 } else if (drag_info.current_pointer_frame > (region_end - region_length/2)) {
3880 show_verbose_time_cursor(region_start, 10);
3883 show_verbose_time_cursor(region_end, 10);
3886 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
3892 Editor::trim_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
3894 RegionView* rv = clicked_regionview;
3895 nframes_t frame_delta = 0;
3896 bool left_direction;
3897 bool obey_snap = !Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier());
3899 /* snap modifier works differently here..
3900 its' current state has to be passed to the
3901 various trim functions in order to work properly
3905 TimeAxisView* tvp = clicked_axisview;
3906 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
3907 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
3909 if (tv && tv->is_track()) {
3910 speed = tv->get_diskstream()->speed();
3913 if (drag_info.last_pointer_frame > drag_info.current_pointer_frame) {
3914 left_direction = true;
3916 left_direction = false;
3920 snap_to (drag_info.current_pointer_frame);
3923 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
3927 if (drag_info.first_move) {
3933 trim_type = "Region start trim";
3936 trim_type = "Region end trim";
3939 trim_type = "Region content trim";
3943 begin_reversible_command (trim_type);
3945 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3946 (*i)->fake_set_opaque(false);
3947 (*i)->region()->freeze ();
3949 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
3951 arv->temporarily_hide_envelope ();
3953 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
3954 insert_result = motion_frozen_playlists.insert (pl);
3955 if (insert_result.second) {
3956 session->add_command(new MementoCommand<Playlist>(*pl, &pl->get_state(), 0));
3961 if (left_direction) {
3962 frame_delta = (drag_info.last_pointer_frame - drag_info.current_pointer_frame);
3964 frame_delta = (drag_info.current_pointer_frame - drag_info.last_pointer_frame);
3969 if ((left_direction == false) && (drag_info.current_pointer_frame <= rv->region()->first_frame()/speed)) {
3972 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3973 single_start_trim (**i, frame_delta, left_direction, obey_snap);
3979 if ((left_direction == true) && (drag_info.current_pointer_frame > (nframes_t) (rv->region()->last_frame()/speed))) {
3982 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3983 single_end_trim (**i, frame_delta, left_direction, obey_snap);
3990 bool swap_direction = false;
3992 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
3993 swap_direction = true;
3996 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
3997 i != selection->regions.by_layer().end(); ++i)
3999 single_contents_trim (**i, frame_delta, left_direction, swap_direction, obey_snap);
4007 show_verbose_time_cursor((nframes_t) (rv->region()->position()/speed), 10);
4010 show_verbose_time_cursor((nframes_t) (rv->region()->last_frame()/speed), 10);
4013 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4017 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4018 drag_info.first_move = false;
4022 Editor::single_contents_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool swap_direction, bool obey_snap)
4024 boost::shared_ptr<Region> region (rv.region());
4026 if (region->locked()) {
4030 nframes_t new_bound;
4033 TimeAxisView* tvp = clicked_axisview;
4034 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4036 if (tv && tv->is_track()) {
4037 speed = tv->get_diskstream()->speed();
4040 if (left_direction) {
4041 if (swap_direction) {
4042 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4044 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4047 if (swap_direction) {
4048 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4050 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4055 snap_to (new_bound);
4057 region->trim_start ((nframes_t) (new_bound * speed), this);
4058 rv.region_changed (StartChanged);
4062 Editor::single_start_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool obey_snap)
4064 boost::shared_ptr<Region> region (rv.region());
4066 if (region->locked()) {
4070 nframes_t new_bound;
4073 TimeAxisView* tvp = clicked_axisview;
4074 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4076 if (tv && tv->is_track()) {
4077 speed = tv->get_diskstream()->speed();
4080 if (left_direction) {
4081 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4083 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4087 snap_to (new_bound, (left_direction ? 0 : 1));
4090 region->trim_front ((nframes_t) (new_bound * speed), this);
4092 rv.region_changed (Change (LengthChanged|PositionChanged|StartChanged));
4096 Editor::single_end_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool obey_snap)
4098 boost::shared_ptr<Region> region (rv.region());
4100 if (region->locked()) {
4104 nframes_t new_bound;
4107 TimeAxisView* tvp = clicked_axisview;
4108 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4110 if (tv && tv->is_track()) {
4111 speed = tv->get_diskstream()->speed();
4114 if (left_direction) {
4115 new_bound = (nframes_t) ((region->last_frame() + 1)/speed) - frame_delta;
4117 new_bound = (nframes_t) ((region->last_frame() + 1)/speed) + frame_delta;
4121 snap_to (new_bound);
4123 region->trim_end ((nframes_t) (new_bound * speed), this);
4124 rv.region_changed (LengthChanged);
4128 Editor::trim_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
4130 if (!drag_info.first_move) {
4131 trim_motion_callback (item, event);
4133 if (!clicked_regionview->get_selected()) {
4134 thaw_region_after_trim (*clicked_regionview);
4137 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4138 i != selection->regions.by_layer().end(); ++i)
4140 thaw_region_after_trim (**i);
4141 (*i)->fake_set_opaque (true);
4145 for (set<boost::shared_ptr<Playlist> >::iterator p = motion_frozen_playlists.begin(); p != motion_frozen_playlists.end(); ++p) {
4147 session->add_command (new MementoCommand<Playlist>(*(*p).get(), 0, &(*p)->get_state()));
4150 motion_frozen_playlists.clear ();
4152 commit_reversible_command();
4154 /* no mouse movement */
4160 Editor::point_trim (GdkEvent* event)
4162 RegionView* rv = clicked_regionview;
4163 nframes_t new_bound = drag_info.current_pointer_frame;
4165 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4166 snap_to (new_bound);
4169 /* Choose action dependant on which button was pressed */
4170 switch (event->button.button) {
4172 trim_op = StartTrim;
4173 begin_reversible_command (_("Start point trim"));
4175 if (rv->get_selected()) {
4177 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4178 i != selection->regions.by_layer().end(); ++i)
4180 if (!(*i)->region()->locked()) {
4181 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4182 XMLNode &before = pl->get_state();
4183 (*i)->region()->trim_front (new_bound, this);
4184 XMLNode &after = pl->get_state();
4185 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4191 if (!rv->region()->locked()) {
4192 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
4193 XMLNode &before = pl->get_state();
4194 rv->region()->trim_front (new_bound, this);
4195 XMLNode &after = pl->get_state();
4196 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4200 commit_reversible_command();
4205 begin_reversible_command (_("End point trim"));
4207 if (rv->get_selected()) {
4209 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
4211 if (!(*i)->region()->locked()) {
4212 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4213 XMLNode &before = pl->get_state();
4214 (*i)->region()->trim_end (new_bound, this);
4215 XMLNode &after = pl->get_state();
4216 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4222 if (!rv->region()->locked()) {
4223 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
4224 XMLNode &before = pl->get_state();
4225 rv->region()->trim_end (new_bound, this);
4226 XMLNode &after = pl->get_state();
4227 session->add_command (new MementoCommand<Playlist>(*pl.get(), &before, &after));
4231 commit_reversible_command();
4240 Editor::thaw_region_after_trim (RegionView& rv)
4242 boost::shared_ptr<Region> region (rv.region());
4244 if (region->locked()) {
4248 region->thaw (_("trimmed region"));
4249 XMLNode &after = region->playlist()->get_state();
4250 session->add_command (new MementoCommand<Playlist>(*(region->playlist()), 0, &after));
4252 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(&rv);
4254 arv->unhide_envelope ();
4258 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* event)
4263 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
4264 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
4268 Location* location = find_location_from_marker (marker, is_start);
4269 location->set_hidden (true, this);
4274 Editor::start_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event, RangeMarkerOp op)
4280 drag_info.item = item;
4281 drag_info.motion_callback = &Editor::drag_range_markerbar_op;
4282 drag_info.finished_callback = &Editor::end_range_markerbar_op;
4284 range_marker_op = op;
4286 if (!temp_location) {
4287 temp_location = new Location;
4291 case CreateRangeMarker:
4292 case CreateTransportMarker:
4294 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
4295 drag_info.copy = true;
4297 drag_info.copy = false;
4299 start_grab (event, selector_cursor);
4303 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4308 Editor::drag_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4310 nframes_t start = 0;
4312 ArdourCanvas::SimpleRect *crect = (range_marker_op == CreateRangeMarker) ? range_bar_drag_rect: transport_bar_drag_rect;
4314 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4315 snap_to (drag_info.current_pointer_frame);
4318 /* only alter selection if the current frame is
4319 different from the last frame position.
4322 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4324 switch (range_marker_op) {
4325 case CreateRangeMarker:
4326 case CreateTransportMarker:
4327 if (drag_info.first_move) {
4328 snap_to (drag_info.grab_frame);
4331 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4332 start = drag_info.current_pointer_frame;
4333 end = drag_info.grab_frame;
4335 end = drag_info.current_pointer_frame;
4336 start = drag_info.grab_frame;
4339 /* first drag: Either add to the selection
4340 or create a new selection.
4343 if (drag_info.first_move) {
4345 temp_location->set (start, end);
4349 update_marker_drag_item (temp_location);
4350 range_marker_drag_rect->show();
4351 range_marker_drag_rect->raise_to_top();
4357 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
4358 start_canvas_autoscroll (1);
4362 temp_location->set (start, end);
4364 double x1 = frame_to_pixel (start);
4365 double x2 = frame_to_pixel (end);
4366 crect->property_x1() = x1;
4367 crect->property_x2() = x2;
4369 update_marker_drag_item (temp_location);
4372 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4373 drag_info.first_move = false;
4375 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4380 Editor::end_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4382 Location * newloc = 0;
4385 if (!drag_info.first_move) {
4386 drag_range_markerbar_op (item, event);
4388 switch (range_marker_op) {
4389 case CreateRangeMarker:
4391 begin_reversible_command (_("new range marker"));
4392 XMLNode &before = session->locations()->get_state();
4393 session->locations()->next_available_name(rangename,"unnamed");
4394 newloc = new Location(temp_location->start(), temp_location->end(), rangename, Location::IsRangeMarker);
4395 session->locations()->add (newloc, true);
4396 XMLNode &after = session->locations()->get_state();
4397 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
4398 commit_reversible_command ();
4400 range_bar_drag_rect->hide();
4401 range_marker_drag_rect->hide();
4405 case CreateTransportMarker:
4406 // popup menu to pick loop or punch
4407 new_transport_marker_context_menu (&event->button, item);
4412 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
4414 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
4419 start = session->locations()->first_mark_before (drag_info.grab_frame);
4420 end = session->locations()->first_mark_after (drag_info.grab_frame);
4422 if (end == max_frames) {
4423 end = session->current_end_frame ();
4427 start = session->current_start_frame ();
4430 switch (mouse_mode) {
4432 /* find the two markers on either side and then make the selection from it */
4433 select_all_within (start, end, 0.0f, FLT_MAX, track_views, Selection::Set);
4437 /* find the two markers on either side of the click and make the range out of it */
4438 selection->set (0, start, end);
4447 stop_canvas_autoscroll ();
4453 Editor::start_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4455 drag_info.item = item;
4456 drag_info.motion_callback = &Editor::drag_mouse_zoom;
4457 drag_info.finished_callback = &Editor::end_mouse_zoom;
4459 start_grab (event, zoom_cursor);
4461 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4465 Editor::drag_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4470 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4471 snap_to (drag_info.current_pointer_frame);
4473 if (drag_info.first_move) {
4474 snap_to (drag_info.grab_frame);
4478 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4480 /* base start and end on initial click position */
4481 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4482 start = drag_info.current_pointer_frame;
4483 end = drag_info.grab_frame;
4485 end = drag_info.current_pointer_frame;
4486 start = drag_info.grab_frame;
4491 if (drag_info.first_move) {
4493 zoom_rect->raise_to_top();
4496 reposition_zoom_rect(start, end);
4498 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4499 drag_info.first_move = false;
4501 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4506 Editor::end_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4508 if (!drag_info.first_move) {
4509 drag_mouse_zoom (item, event);
4511 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
4512 temporal_zoom_by_frame (drag_info.grab_frame, drag_info.last_pointer_frame, "mouse zoom");
4514 temporal_zoom_by_frame (drag_info.last_pointer_frame, drag_info.grab_frame, "mouse zoom");
4517 temporal_zoom_to_frame (false, drag_info.grab_frame);
4519 temporal_zoom_step (false);
4520 center_screen (drag_info.grab_frame);
4528 Editor::reposition_zoom_rect (nframes_t start, nframes_t end)
4530 double x1 = frame_to_pixel (start);
4531 double x2 = frame_to_pixel (end);
4532 double y2 = full_canvas_height - 1.0;
4534 zoom_rect->property_x1() = x1;
4535 zoom_rect->property_y1() = 1.0;
4536 zoom_rect->property_x2() = x2;
4537 zoom_rect->property_y2() = y2;
4541 Editor::start_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4543 drag_info.item = item;
4544 drag_info.motion_callback = &Editor::drag_rubberband_select;
4545 drag_info.finished_callback = &Editor::end_rubberband_select;
4547 start_grab (event, cross_hair_cursor);
4549 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4553 Editor::drag_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4560 /* use a bigger drag threshold than the default */
4562 if (abs ((int) (drag_info.current_pointer_frame - drag_info.grab_frame)) < 8) {
4566 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4567 if (drag_info.first_move) {
4568 snap_to (drag_info.grab_frame);
4570 snap_to (drag_info.current_pointer_frame);
4573 /* base start and end on initial click position */
4575 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4576 start = drag_info.current_pointer_frame;
4577 end = drag_info.grab_frame;
4579 end = drag_info.current_pointer_frame;
4580 start = drag_info.grab_frame;
4583 if (drag_info.current_pointer_y < drag_info.grab_y) {
4584 y1 = drag_info.current_pointer_y;
4585 y2 = drag_info.grab_y;
4587 y2 = drag_info.current_pointer_y;
4588 y1 = drag_info.grab_y;
4592 if (start != end || y1 != y2) {
4594 double x1 = frame_to_pixel (start);
4595 double x2 = frame_to_pixel (end);
4597 rubberband_rect->property_x1() = x1;
4598 rubberband_rect->property_y1() = y1;
4599 rubberband_rect->property_x2() = x2;
4600 rubberband_rect->property_y2() = y2;
4602 rubberband_rect->show();
4603 rubberband_rect->raise_to_top();
4605 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4606 drag_info.first_move = false;
4608 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4613 Editor::end_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4615 if (!drag_info.first_move) {
4617 drag_rubberband_select (item, event);
4620 if (drag_info.current_pointer_y < drag_info.grab_y) {
4621 y1 = drag_info.current_pointer_y;
4622 y2 = drag_info.grab_y;
4625 y2 = drag_info.current_pointer_y;
4626 y1 = drag_info.grab_y;
4630 Selection::Operation op = Keyboard::selection_type (event->button.state);
4633 begin_reversible_command (_("rubberband selection"));
4635 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
4636 commit = select_all_within (drag_info.grab_frame, drag_info.last_pointer_frame, y1, y2, track_views, op);
4638 commit = select_all_within (drag_info.last_pointer_frame, drag_info.grab_frame, y1, y2, track_views, op);
4642 commit_reversible_command ();
4646 selection->clear_tracks();
4647 selection->clear_regions();
4648 selection->clear_points ();
4649 selection->clear_lines ();
4652 rubberband_rect->hide();
4657 Editor::mouse_rename_region (ArdourCanvas::Item* item, GdkEvent* event)
4659 using namespace Gtkmm2ext;
4661 ArdourPrompter prompter (false);
4663 prompter.set_prompt (_("Name for region:"));
4664 prompter.set_initial_text (clicked_regionview->region()->name());
4665 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
4666 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
4667 prompter.show_all ();
4668 switch (prompter.run ()) {
4669 case Gtk::RESPONSE_ACCEPT:
4671 prompter.get_result(str);
4673 clicked_regionview->region()->set_name (str);
4681 Editor::start_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
4683 drag_info.item = item;
4684 drag_info.motion_callback = &Editor::time_fx_motion;
4685 drag_info.finished_callback = &Editor::end_time_fx;
4689 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4693 Editor::time_fx_motion (ArdourCanvas::Item *item, GdkEvent* event)
4695 RegionView* rv = clicked_regionview;
4697 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4698 snap_to (drag_info.current_pointer_frame);
4701 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
4705 if (drag_info.current_pointer_frame > rv->region()->position()) {
4706 rv->get_time_axis_view().show_timestretch (rv->region()->position(), drag_info.current_pointer_frame);
4709 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4710 drag_info.first_move = false;
4712 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4716 Editor::end_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
4718 clicked_regionview->get_time_axis_view().hide_timestretch ();
4720 if (drag_info.first_move) {
4724 nframes_t newlen = drag_info.last_pointer_frame - clicked_regionview->region()->position();
4725 float percentage = (float) ((double) newlen - (double) clicked_regionview->region()->length()) / ((double) newlen) * 100.0f;
4727 begin_reversible_command (_("timestretch"));
4729 if (run_timestretch (selection->regions, percentage) == 0) {
4730 session->commit_reversible_command ();
4735 Editor::mouse_brush_insert_region (RegionView* rv, nframes_t pos)
4737 /* no brushing without a useful snap setting */
4740 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
4743 switch (snap_mode) {
4745 return; /* can't work because it allows region to be placed anywhere */
4750 switch (snap_type) {
4753 case SnapToEditCursor:
4760 /* don't brush a copy over the original */
4762 if (pos == rv->region()->position()) {
4766 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&arv->get_time_axis_view());
4768 if (rtv == 0 || !rtv->is_track()) {
4772 boost::shared_ptr<Playlist> playlist = rtv->playlist();
4773 double speed = rtv->get_diskstream()->speed();
4775 XMLNode &before = playlist->get_state();
4776 playlist->add_region (boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (arv->audio_region())), (nframes_t) (pos * speed));
4777 XMLNode &after = playlist->get_state();
4778 session->add_command(new MementoCommand<Playlist>(*playlist.get(), &before, &after));
4780 // playlist is frozen, so we have to update manually
4782 playlist->Modified(); /* EMIT SIGNAL */
4786 Editor::track_height_step_timeout ()
4789 struct timeval delta;
4791 gettimeofday (&now, 0);
4792 timersub (&now, &last_track_height_step_timestamp, &delta);
4794 if (delta.tv_sec * 1000000 + delta.tv_usec > 250000) { /* milliseconds */
4795 current_stepping_trackview = 0;