2 Copyright (C) 2000-2001 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
28 #include <pbd/error.h>
29 #include <gtkmm2ext/utils.h>
30 #include <pbd/memento_command.h>
32 #include "ardour_ui.h"
34 #include "time_axis_view.h"
35 #include "audio_time_axis.h"
36 #include "audio_region_view.h"
37 #include "midi_region_view.h"
39 #include "streamview.h"
40 #include "region_gain_line.h"
41 #include "automation_time_axis.h"
42 #include "control_point.h"
45 #include "selection.h"
48 #include "rgb_macros.h"
50 #include <ardour/types.h>
51 #include <ardour/profile.h>
52 #include <ardour/route.h>
53 #include <ardour/audio_track.h>
54 #include <ardour/audio_diskstream.h>
55 #include <ardour/playlist.h>
56 #include <ardour/audioplaylist.h>
57 #include <ardour/audioregion.h>
58 #include <ardour/midi_region.h>
59 #include <ardour/dB.h>
60 #include <ardour/utils.h>
61 #include <ardour/region_factory.h>
68 using namespace ARDOUR;
72 using namespace Editing;
75 Editor::event_frame (GdkEvent* event, double* pcx, double* pcy)
89 switch (event->type) {
90 case GDK_BUTTON_RELEASE:
91 case GDK_BUTTON_PRESS:
92 case GDK_2BUTTON_PRESS:
93 case GDK_3BUTTON_PRESS:
94 track_canvas.w2c(event->button.x, event->button.y, *pcx, *pcy);
96 case GDK_MOTION_NOTIFY:
97 track_canvas.w2c(event->motion.x, event->motion.y, *pcx, *pcy);
99 case GDK_ENTER_NOTIFY:
100 case GDK_LEAVE_NOTIFY:
101 track_canvas.w2c(event->crossing.x, event->crossing.y, *pcx, *pcy);
104 case GDK_KEY_RELEASE:
105 // track_canvas.w2c(event->key.x, event->key.y, *pcx, *pcy);
108 warning << string_compose (_("Editor::event_frame() used on unhandled event type %1"), event->type) << endmsg;
112 /* note that pixel_to_frame() never returns less than zero, so even if the pixel
113 position is negative (as can be the case with motion events in particular),
114 the frame location is always positive.
117 return pixel_to_frame (*pcx);
121 Editor::mouse_mode_toggled (MouseMode m)
123 if (ignore_mouse_mode_toggle) {
129 if (mouse_select_button.get_active()) {
135 if (mouse_move_button.get_active()) {
141 if (mouse_gain_button.get_active()) {
147 if (mouse_zoom_button.get_active()) {
153 if (mouse_timefx_button.get_active()) {
159 if (mouse_audition_button.get_active()) {
165 if (mouse_note_button.get_active()) {
176 Editor::set_mouse_mode (MouseMode m, bool force)
178 if (drag_info.item) {
182 if (!force && m == mouse_mode) {
190 if (mouse_mode != MouseRange) {
192 /* in all modes except range, hide the range selection,
193 show the object (region) selection.
196 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
197 (*i)->set_should_show_selection (true);
199 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
200 (*i)->hide_selection ();
206 in range mode,show the range selection.
209 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
210 if ((*i)->get_selected()) {
211 (*i)->show_selection (selection->time);
216 /* XXX the hack of unsetting all other buttongs should go
217 away once GTK2 allows us to use regular radio buttons drawn like
218 normal buttons, rather than my silly GroupedButton hack.
221 ignore_mouse_mode_toggle = true;
223 switch (mouse_mode) {
225 mouse_select_button.set_active (true);
226 current_canvas_cursor = selector_cursor;
230 mouse_move_button.set_active (true);
231 current_canvas_cursor = grabber_cursor;
235 mouse_gain_button.set_active (true);
236 current_canvas_cursor = cross_hair_cursor;
240 mouse_zoom_button.set_active (true);
241 current_canvas_cursor = zoom_cursor;
245 mouse_timefx_button.set_active (true);
246 current_canvas_cursor = time_fx_cursor; // just use playhead
250 mouse_audition_button.set_active (true);
251 current_canvas_cursor = speaker_cursor;
255 mouse_note_button.set_active (true);
256 current_canvas_cursor = note_cursor;
260 ignore_mouse_mode_toggle = false;
263 track_canvas.get_window()->set_cursor(*current_canvas_cursor);
268 Editor::step_mouse_mode (bool next)
270 switch (current_mouse_mode()) {
272 if (next) set_mouse_mode (MouseRange);
273 else set_mouse_mode (MouseTimeFX);
277 if (next) set_mouse_mode (MouseZoom);
278 else set_mouse_mode (MouseObject);
282 if (next) set_mouse_mode (MouseGain);
283 else set_mouse_mode (MouseRange);
287 if (next) set_mouse_mode (MouseTimeFX);
288 else set_mouse_mode (MouseZoom);
292 if (next) set_mouse_mode (MouseAudition);
293 else set_mouse_mode (MouseGain);
297 if (next) set_mouse_mode (MouseObject);
298 else set_mouse_mode (MouseTimeFX);
302 if (next) set_mouse_mode (MouseObject);
303 else set_mouse_mode (MouseAudition);
309 Editor::button_selection (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
313 /* in object/audition/timefx mode, any button press sets
314 the selection if the object can be selected. this is a
315 bit of hack, because we want to avoid this if the
316 mouse operation is a region alignment.
318 note: not dbl-click or triple-click
321 if (((mouse_mode != MouseObject) &&
322 (mouse_mode != MouseAudition || item_type != RegionItem) &&
323 (mouse_mode != MouseTimeFX || item_type != RegionItem) &&
324 (mouse_mode != MouseRange)) ||
326 (event->type != GDK_BUTTON_PRESS && event->type != GDK_BUTTON_RELEASE || event->button.button > 3)) {
331 if (event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) {
333 if ((event->button.state & Keyboard::RelevantModifierKeyMask) && event->button.button != 1) {
335 /* almost no selection action on modified button-2 or button-3 events */
337 if (item_type != RegionItem && event->button.button != 2) {
343 Selection::Operation op = Keyboard::selection_type (event->button.state);
344 bool press = (event->type == GDK_BUTTON_PRESS);
346 // begin_reversible_command (_("select on click"));
350 case RegionViewNameHighlight:
352 case FadeInHandleItem:
354 case FadeOutHandleItem:
356 if (mouse_mode != MouseRange) {
357 commit = set_selected_regionview_from_click (press, op, true);
358 } else if (event->type == GDK_BUTTON_PRESS) {
359 commit = set_selected_track_from_click (press, op, false);
363 case CrossfadeViewItem:
364 commit = set_selected_track_from_click (press, op, false);
367 case ControlPointItem:
368 commit = set_selected_track_from_click (press, op, true);
369 if (mouse_mode != MouseRange) {
370 commit |= set_selected_control_point_from_click (op, false);
375 /* for context click or range selection, select track */
376 if (event->button.button == 3) {
377 commit = set_selected_track_from_click (press, op, true);
378 } else if (event->type == GDK_BUTTON_PRESS && mouse_mode == MouseRange) {
379 commit = set_selected_track_from_click (press, op, false);
383 case AutomationTrackItem:
384 commit = set_selected_track_from_click (press, op, true);
392 // commit_reversible_command ();
397 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
399 track_canvas.grab_focus();
401 if (session && session->actively_recording()) {
405 button_selection (item, event, item_type);
407 if (drag_info.item == 0 &&
408 (Keyboard::is_delete_event (&event->button) ||
409 Keyboard::is_context_menu_event (&event->button) ||
410 Keyboard::is_edit_event (&event->button))) {
412 /* handled by button release */
416 switch (event->button.button) {
419 if (event->type == GDK_BUTTON_PRESS) {
421 if (drag_info.item) {
422 drag_info.item->ungrab (event->button.time);
425 /* single mouse clicks on any of these item types operate
426 independent of mouse mode, mostly because they are
427 not on the main track canvas or because we want
433 case PlayheadCursorItem:
434 start_cursor_grab (item, event);
438 if (Keyboard::modifier_state_equals (event->button.state,
439 Keyboard::ModifierMask(Keyboard::Control|Keyboard::Shift))) {
440 hide_marker (item, event);
442 start_marker_grab (item, event);
446 case TempoMarkerItem:
447 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
448 start_tempo_marker_copy_grab (item, event);
450 start_tempo_marker_grab (item, event);
454 case MeterMarkerItem:
455 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
456 start_meter_marker_copy_grab (item, event);
458 start_meter_marker_grab (item, event);
468 case RangeMarkerBarItem:
469 start_range_markerbar_op (item, event, CreateRangeMarker);
473 case TransportMarkerBarItem:
474 start_range_markerbar_op (item, event, CreateTransportMarker);
483 switch (mouse_mode) {
486 case StartSelectionTrimItem:
487 start_selection_op (item, event, SelectionStartTrim);
490 case EndSelectionTrimItem:
491 start_selection_op (item, event, SelectionEndTrim);
495 if (Keyboard::modifier_state_contains
496 (event->button.state, Keyboard::ModifierMask(Keyboard::Alt))) {
497 // contains and not equals because I can't use alt as a modifier alone.
498 start_selection_grab (item, event);
499 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
500 /* grab selection for moving */
501 start_selection_op (item, event, SelectionMove);
504 /* this was debated, but decided the more common action was to
505 make a new selection */
506 start_selection_op (item, event, CreateSelection);
511 start_selection_op (item, event, CreateSelection);
517 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::Control|Keyboard::Alt)) &&
518 event->type == GDK_BUTTON_PRESS) {
520 start_rubberband_select (item, event);
522 } else if (event->type == GDK_BUTTON_PRESS) {
525 case FadeInHandleItem:
526 start_fade_in_grab (item, event);
529 case FadeOutHandleItem:
530 start_fade_out_grab (item, event);
534 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
535 start_region_copy_grab (item, event);
536 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
537 start_region_brush_grab (item, event);
539 start_region_grab (item, event);
543 case RegionViewNameHighlight:
544 start_trim (item, event);
549 /* rename happens on edit clicks */
550 start_trim (clicked_regionview->get_name_highlight(), event);
554 case ControlPointItem:
555 start_control_point_grab (item, event);
559 case AutomationLineItem:
560 start_line_grab_from_line (item, event);
565 case AutomationTrackItem:
566 start_rubberband_select (item, event);
570 case ImageFrameHandleStartItem:
571 imageframe_start_handle_op(item, event) ;
574 case ImageFrameHandleEndItem:
575 imageframe_end_handle_op(item, event) ;
578 case MarkerViewHandleStartItem:
579 markerview_item_start_handle_op(item, event) ;
582 case MarkerViewHandleEndItem:
583 markerview_item_end_handle_op(item, event) ;
587 start_markerview_grab(item, event) ;
590 start_imageframe_grab(item, event) ;
608 // start_line_grab_from_regionview (item, event);
612 start_line_grab_from_line (item, event);
615 case ControlPointItem:
616 start_control_point_grab (item, event);
627 case ControlPointItem:
628 start_control_point_grab (item, event);
631 case AutomationLineItem:
632 start_line_grab_from_line (item, event);
636 // XXX need automation mode to identify which
638 // start_line_grab_from_regionview (item, event);
648 if (event->type == GDK_BUTTON_PRESS) {
649 start_mouse_zoom (item, event);
656 if (item_type == RegionItem) {
657 start_time_fx (item, event);
662 /* handled in release */
671 switch (mouse_mode) {
673 if (event->type == GDK_BUTTON_PRESS) {
676 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
677 start_region_copy_grab (item, event);
679 start_region_grab (item, event);
683 case ControlPointItem:
684 start_control_point_grab (item, event);
695 case RegionViewNameHighlight:
696 start_trim (item, event);
701 start_trim (clicked_regionview->get_name_highlight(), event);
712 if (event->type == GDK_BUTTON_PRESS) {
713 /* relax till release */
720 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
721 temporal_zoom_session();
723 temporal_zoom_to_frame (true, event_frame(event));
746 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
748 nframes_t where = event_frame (event, 0, 0);
750 /* no action if we're recording */
752 if (session && session->actively_recording()) {
756 /* first, see if we're finishing a drag ... */
758 if (drag_info.item) {
759 if (end_grab (item, event)) {
760 /* grab dragged, so do nothing else */
765 button_selection (item, event, item_type);
767 /* edit events get handled here */
769 if (drag_info.item == 0 && Keyboard::is_edit_event (&event->button)) {
775 case TempoMarkerItem:
776 edit_tempo_marker (item);
779 case MeterMarkerItem:
780 edit_meter_marker (item);
784 if (clicked_regionview->name_active()) {
785 return mouse_rename_region (item, event);
795 /* context menu events get handled here */
797 if (Keyboard::is_context_menu_event (&event->button)) {
799 if (drag_info.item == 0) {
801 /* no matter which button pops up the context menu, tell the menu
802 widget to use button 1 to drive menu selection.
807 case FadeInHandleItem:
809 case FadeOutHandleItem:
810 popup_fade_context_menu (1, event->button.time, item, item_type);
815 case RegionViewNameHighlight:
818 case AutomationTrackItem:
819 case CrossfadeViewItem:
820 popup_track_context_menu (1, event->button.time, where);
824 case RangeMarkerBarItem:
825 case TransportMarkerBarItem:
828 popup_ruler_menu (pixel_to_frame(event->button.x), item_type);
832 marker_context_menu (&event->button, item);
835 case TempoMarkerItem:
836 tm_marker_context_menu (&event->button, item);
839 case MeterMarkerItem:
840 tm_marker_context_menu (&event->button, item);
845 popup_imageframe_edit_menu(1, event->button.time, item, true) ;
847 case ImageFrameTimeAxisItem:
848 popup_imageframe_edit_menu(1, event->button.time, item, false) ;
851 popup_marker_time_axis_edit_menu(1, event->button.time, item, true) ;
853 case MarkerTimeAxisItem:
854 popup_marker_time_axis_edit_menu(1, event->button.time, item, false) ;
866 /* delete events get handled here */
868 if (drag_info.item == 0 && Keyboard::is_delete_event (&event->button)) {
871 case TempoMarkerItem:
872 remove_tempo_marker (item);
875 case MeterMarkerItem:
876 remove_meter_marker (item);
880 remove_marker (*item, event);
884 if (mouse_mode == MouseObject) {
885 remove_clicked_region ();
889 case ControlPointItem:
890 if (mouse_mode == MouseGain) {
891 remove_gain_control_point (item, event);
893 remove_control_point (item, event);
903 switch (event->button.button) {
907 /* see comments in button_press_handler */
909 case PlayheadCursorItem:
912 case AutomationLineItem:
913 case StartSelectionTrimItem:
914 case EndSelectionTrimItem:
918 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
919 snap_to (where, 0, true);
921 mouse_add_new_marker (where);
925 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
928 mouse_add_new_tempo_event (where);
932 mouse_add_new_meter_event (pixel_to_frame (event->button.x));
940 switch (mouse_mode) {
943 case AutomationTrackItem:
944 dynamic_cast<AutomationTimeAxisView*>(clicked_axisview)->add_automation_event
958 // Gain only makes sense for audio regions
960 if (!dynamic_cast<AudioRegionView*>(clicked_regionview)) {
966 dynamic_cast<AudioRegionView*>(clicked_regionview)->add_gain_point_event (item, event);
970 case AutomationTrackItem:
971 dynamic_cast<AutomationTimeAxisView*>(clicked_axisview)->
972 add_automation_event (item, event, where, event->button.y);
983 audition_selected_region ();
1000 switch (mouse_mode) {
1004 // x_style_paste (where, 1.0);
1024 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1030 switch (item_type) {
1031 case ControlPointItem:
1032 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1033 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1034 cp->set_visible (true);
1038 at_y = cp->get_y ();
1039 cp->item()->i2w (at_x, at_y);
1043 fraction = 1.0 - (cp->get_y() / cp->line().height());
1045 set_verbose_canvas_cursor (cp->line().get_verbose_cursor_string (fraction), at_x, at_y);
1046 show_verbose_canvas_cursor ();
1048 if (is_drawable()) {
1049 track_canvas.get_window()->set_cursor (*fader_cursor);
1055 if (mouse_mode == MouseGain) {
1056 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1058 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredGainLine.get();
1059 if (is_drawable()) {
1060 track_canvas.get_window()->set_cursor (*fader_cursor);
1065 case AutomationLineItem:
1066 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1068 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1070 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredAutomationLine.get();
1072 if (is_drawable()) {
1073 track_canvas.get_window()->set_cursor (*fader_cursor);
1078 case RegionViewNameHighlight:
1079 if (is_drawable() && mouse_mode == MouseObject) {
1080 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1084 case StartSelectionTrimItem:
1085 case EndSelectionTrimItem:
1088 case ImageFrameHandleStartItem:
1089 case ImageFrameHandleEndItem:
1090 case MarkerViewHandleStartItem:
1091 case MarkerViewHandleEndItem:
1094 if (is_drawable()) {
1095 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1099 case EditCursorItem:
1100 case PlayheadCursorItem:
1101 if (is_drawable()) {
1102 track_canvas.get_window()->set_cursor (*grabber_cursor);
1106 case RegionViewName:
1108 /* when the name is not an active item, the entire name highlight is for trimming */
1110 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1111 if (mouse_mode == MouseObject && is_drawable()) {
1112 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1118 case AutomationTrackItem:
1119 if (is_drawable()) {
1120 Gdk::Cursor *cursor;
1121 switch (mouse_mode) {
1123 cursor = selector_cursor;
1126 cursor = zoom_cursor;
1129 cursor = cross_hair_cursor;
1133 track_canvas.get_window()->set_cursor (*cursor);
1135 AutomationTimeAxisView* atv;
1136 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1137 clear_entered_track = false;
1138 set_entered_track (atv);
1144 case RangeMarkerBarItem:
1145 case TransportMarkerBarItem:
1148 if (is_drawable()) {
1149 time_canvas.get_window()->set_cursor (*timebar_cursor);
1154 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1157 marker->set_color_rgba (ARDOUR_UI::config()->canvasvar_EnteredMarker.get());
1159 case MeterMarkerItem:
1160 case TempoMarkerItem:
1161 if (is_drawable()) {
1162 time_canvas.get_window()->set_cursor (*timebar_cursor);
1165 case FadeInHandleItem:
1166 case FadeOutHandleItem:
1167 if (mouse_mode == MouseObject) {
1168 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1170 rect->property_fill_color_rgba() = 0;
1171 rect->property_outline_pixels() = 1;
1180 /* second pass to handle entered track status in a comprehensible way.
1183 switch (item_type) {
1185 case AutomationLineItem:
1186 case ControlPointItem:
1187 /* these do not affect the current entered track state */
1188 clear_entered_track = false;
1191 case AutomationTrackItem:
1192 /* handled above already */
1196 set_entered_track (0);
1204 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1213 switch (item_type) {
1214 case ControlPointItem:
1215 cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
1216 if (cp->line().the_list()->interpolation() != AutomationList::Discrete) {
1217 if (cp->line().npoints() > 1 && !cp->selected()) {
1218 cp->set_visible (false);
1222 if (is_drawable()) {
1223 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1226 hide_verbose_canvas_cursor ();
1229 case RegionViewNameHighlight:
1230 case StartSelectionTrimItem:
1231 case EndSelectionTrimItem:
1232 case EditCursorItem:
1233 case PlayheadCursorItem:
1236 case ImageFrameHandleStartItem:
1237 case ImageFrameHandleEndItem:
1238 case MarkerViewHandleStartItem:
1239 case MarkerViewHandleEndItem:
1242 if (is_drawable()) {
1243 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1248 case AutomationLineItem:
1249 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1251 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1253 line->property_fill_color_rgba() = al->get_line_color();
1255 if (is_drawable()) {
1256 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1260 case RegionViewName:
1261 /* see enter_handler() for notes */
1262 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1263 if (is_drawable() && mouse_mode == MouseObject) {
1264 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1269 case RangeMarkerBarItem:
1270 case TransportMarkerBarItem:
1274 if (is_drawable()) {
1275 time_canvas.get_window()->set_cursor (*timebar_cursor);
1280 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1283 loc = find_location_from_marker (marker, is_start);
1284 if (loc) location_flags_changed (loc, this);
1286 case MeterMarkerItem:
1287 case TempoMarkerItem:
1289 if (is_drawable()) {
1290 time_canvas.get_window()->set_cursor (*timebar_cursor);
1295 case FadeInHandleItem:
1296 case FadeOutHandleItem:
1297 rv = static_cast<RegionView*>(item->get_data ("regionview"));
1299 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1301 rect->property_fill_color_rgba() = rv->get_fill_color();
1302 rect->property_outline_pixels() = 0;
1307 case AutomationTrackItem:
1308 if (is_drawable()) {
1309 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1310 clear_entered_track = true;
1311 Glib::signal_idle().connect (mem_fun(*this, &Editor::left_automation_track));
1323 Editor::left_automation_track ()
1325 if (clear_entered_track) {
1326 set_entered_track (0);
1327 clear_entered_track = false;
1333 Editor::motion_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type, bool from_autoscroll)
1337 /* We call this so that MOTION_NOTIFY events continue to be
1338 delivered to the canvas. We need to do this because we set
1339 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
1340 the density of the events, at the expense of a round-trip
1341 to the server. Given that this will mostly occur on cases
1342 where DISPLAY = :0.0, and given the cost of what the motion
1343 event might do, its a good tradeoff.
1346 track_canvas.get_pointer (x, y);
1348 if (current_stepping_trackview) {
1349 /* don't keep the persistent stepped trackview if the mouse moves */
1350 current_stepping_trackview = 0;
1351 step_timeout.disconnect ();
1354 if (session && session->actively_recording()) {
1355 /* Sorry. no dragging stuff around while we record */
1359 drag_info.item_type = item_type;
1360 drag_info.current_pointer_frame = event_frame (event, &drag_info.current_pointer_x,
1361 &drag_info.current_pointer_y);
1363 if (!from_autoscroll && drag_info.item) {
1364 /* item != 0 is the best test i can think of for dragging.
1366 if (!drag_info.move_threshold_passed) {
1368 bool x_threshold_passed = (abs ((nframes64_t) (drag_info.current_pointer_x - drag_info.grab_x)) > 4LL);
1369 bool y_threshold_passed = (abs ((nframes64_t) (drag_info.current_pointer_y - drag_info.grab_y)) > 4LL);
1371 drag_info.move_threshold_passed = (x_threshold_passed || y_threshold_passed);
1373 // and change the initial grab loc/frame if this drag info wants us to
1375 if (drag_info.want_move_threshold && drag_info.move_threshold_passed) {
1376 drag_info.grab_frame = drag_info.current_pointer_frame;
1377 drag_info.grab_x = drag_info.current_pointer_x;
1378 drag_info.grab_y = drag_info.current_pointer_y;
1379 drag_info.last_pointer_frame = drag_info.grab_frame;
1380 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
1385 switch (item_type) {
1386 case PlayheadCursorItem:
1387 case EditCursorItem:
1389 case ControlPointItem:
1390 case TempoMarkerItem:
1391 case MeterMarkerItem:
1392 case RegionViewNameHighlight:
1393 case StartSelectionTrimItem:
1394 case EndSelectionTrimItem:
1397 case AutomationLineItem:
1398 case FadeInHandleItem:
1399 case FadeOutHandleItem:
1402 case ImageFrameHandleStartItem:
1403 case ImageFrameHandleEndItem:
1404 case MarkerViewHandleStartItem:
1405 case MarkerViewHandleEndItem:
1408 if (drag_info.item && (event->motion.state & Gdk::BUTTON1_MASK ||
1409 (event->motion.state & Gdk::BUTTON2_MASK))) {
1410 if (!from_autoscroll) {
1411 maybe_autoscroll (event);
1413 (this->*(drag_info.motion_callback)) (item, event);
1422 switch (mouse_mode) {
1427 if (drag_info.item && (event->motion.state & GDK_BUTTON1_MASK ||
1428 (event->motion.state & GDK_BUTTON2_MASK))) {
1429 if (!from_autoscroll) {
1430 maybe_autoscroll (event);
1432 (this->*(drag_info.motion_callback)) (item, event);
1443 track_canvas_motion (event);
1444 // drag_info.last_pointer_frame = drag_info.current_pointer_frame;
1452 Editor::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
1454 if (drag_info.item == 0) {
1455 fatal << _("programming error: start_grab called without drag item") << endmsg;
1461 cursor = grabber_cursor;
1464 // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
1466 if (event->button.button == 2) {
1467 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Alt)) {
1468 drag_info.y_constrained = true;
1469 drag_info.x_constrained = false;
1471 drag_info.y_constrained = false;
1472 drag_info.x_constrained = true;
1475 drag_info.x_constrained = false;
1476 drag_info.y_constrained = false;
1479 drag_info.grab_frame = event_frame (event, &drag_info.grab_x, &drag_info.grab_y);
1480 drag_info.last_pointer_frame = drag_info.grab_frame;
1481 drag_info.current_pointer_frame = drag_info.grab_frame;
1482 drag_info.current_pointer_x = drag_info.grab_x;
1483 drag_info.current_pointer_y = drag_info.grab_y;
1484 drag_info.cumulative_x_drag = 0;
1485 drag_info.cumulative_y_drag = 0;
1486 drag_info.first_move = true;
1487 drag_info.move_threshold_passed = false;
1488 drag_info.want_move_threshold = false;
1489 drag_info.pointer_frame_offset = 0;
1490 drag_info.brushing = false;
1491 drag_info.copied_location = 0;
1493 drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK,
1495 event->button.time);
1497 if (session && session->transport_rolling()) {
1498 drag_info.was_rolling = true;
1500 drag_info.was_rolling = false;
1503 switch (snap_type) {
1504 case SnapToRegionStart:
1505 case SnapToRegionEnd:
1506 case SnapToRegionSync:
1507 case SnapToRegionBoundary:
1508 build_region_boundary_cache ();
1516 Editor::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t time)
1518 drag_info.item->ungrab (0);
1519 drag_info.item = new_item;
1522 cursor = grabber_cursor;
1525 drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK, *cursor, time);
1529 Editor::end_grab (ArdourCanvas::Item* item, GdkEvent* event)
1531 bool did_drag = false;
1533 stop_canvas_autoscroll ();
1535 if (drag_info.item == 0) {
1539 drag_info.item->ungrab (event->button.time);
1541 if (drag_info.finished_callback) {
1542 (this->*(drag_info.finished_callback)) (item, event);
1545 did_drag = !drag_info.first_move;
1547 hide_verbose_canvas_cursor();
1550 drag_info.copy = false;
1551 drag_info.motion_callback = 0;
1552 drag_info.finished_callback = 0;
1553 drag_info.last_trackview = 0;
1554 drag_info.last_frame_position = 0;
1555 drag_info.grab_frame = 0;
1556 drag_info.last_pointer_frame = 0;
1557 drag_info.current_pointer_frame = 0;
1558 drag_info.brushing = false;
1560 if (drag_info.copied_location) {
1561 delete drag_info.copied_location;
1562 drag_info.copied_location = 0;
1569 Editor::set_edit_cursor (GdkEvent* event)
1571 nframes_t pointer_frame = event_frame (event);
1573 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1574 if (snap_type != SnapToEditCursor) {
1575 snap_to (pointer_frame);
1579 edit_cursor->set_position (pointer_frame);
1580 edit_cursor_clock.set (pointer_frame);
1584 Editor::set_playhead_cursor (GdkEvent* event)
1586 nframes_t pointer_frame = event_frame (event);
1588 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1589 snap_to (pointer_frame);
1593 session->request_locate (pointer_frame, session->transport_rolling());
1598 Editor::start_fade_in_grab (ArdourCanvas::Item* item, GdkEvent* event)
1600 drag_info.item = item;
1601 drag_info.motion_callback = &Editor::fade_in_drag_motion_callback;
1602 drag_info.finished_callback = &Editor::fade_in_drag_finished_callback;
1606 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
1607 fatal << _("programming error: fade in canvas item has no regionview data pointer!") << endmsg;
1611 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1613 drag_info.pointer_frame_offset = drag_info.grab_frame - ((nframes_t) arv->audio_region()->fade_in()->back()->when + arv->region()->position());
1617 Editor::fade_in_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1619 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1621 nframes_t fade_length;
1623 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1624 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1630 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1634 if (pos < (arv->region()->position() + 64)) {
1635 fade_length = 64; // this should be a minimum defined somewhere
1636 } else if (pos > arv->region()->last_frame()) {
1637 fade_length = arv->region()->length();
1639 fade_length = pos - arv->region()->position();
1641 /* mapover the region selection */
1643 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1645 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1651 tmp->reset_fade_in_shape_width (fade_length);
1654 show_verbose_duration_cursor (arv->region()->position(), arv->region()->position() + fade_length, 10);
1656 drag_info.first_move = false;
1660 Editor::fade_in_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1662 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1664 nframes_t fade_length;
1666 if (drag_info.first_move) return;
1668 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1669 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1674 if (pos < (arv->region()->position() + 64)) {
1675 fade_length = 64; // this should be a minimum defined somewhere
1676 } else if (pos > arv->region()->last_frame()) {
1677 fade_length = arv->region()->length();
1679 fade_length = pos - arv->region()->position();
1682 begin_reversible_command (_("change fade in length"));
1684 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1686 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1692 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
1693 XMLNode &before = alist->get_state();
1695 tmp->audio_region()->set_fade_in_length (fade_length);
1697 XMLNode &after = alist->get_state();
1698 session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
1701 commit_reversible_command ();
1705 Editor::start_fade_out_grab (ArdourCanvas::Item* item, GdkEvent* event)
1707 drag_info.item = item;
1708 drag_info.motion_callback = &Editor::fade_out_drag_motion_callback;
1709 drag_info.finished_callback = &Editor::fade_out_drag_finished_callback;
1713 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
1714 fatal << _("programming error: fade out canvas item has no regionview data pointer!") << endmsg;
1718 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1720 drag_info.pointer_frame_offset = drag_info.grab_frame - (arv->region()->length() - (nframes_t) arv->audio_region()->fade_out()->back()->when + arv->region()->position());
1724 Editor::fade_out_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1726 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1728 nframes_t fade_length;
1730 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1731 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1736 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1740 if (pos > (arv->region()->last_frame() - 64)) {
1741 fade_length = 64; // this should really be a minimum fade defined somewhere
1743 else if (pos < arv->region()->position()) {
1744 fade_length = arv->region()->length();
1747 fade_length = arv->region()->last_frame() - pos;
1750 /* mapover the region selection */
1752 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1754 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1760 tmp->reset_fade_out_shape_width (fade_length);
1763 show_verbose_duration_cursor (arv->region()->last_frame() - fade_length, arv->region()->last_frame(), 10);
1765 drag_info.first_move = false;
1769 Editor::fade_out_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1771 if (drag_info.first_move) return;
1773 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1775 nframes_t fade_length;
1777 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1778 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1784 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1788 if (pos > (arv->region()->last_frame() - 64)) {
1789 fade_length = 64; // this should really be a minimum fade defined somewhere
1791 else if (pos < arv->region()->position()) {
1792 fade_length = arv->region()->length();
1795 fade_length = arv->region()->last_frame() - pos;
1798 begin_reversible_command (_("change fade out length"));
1800 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1802 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1808 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
1809 XMLNode &before = alist->get_state();
1811 tmp->audio_region()->set_fade_out_length (fade_length);
1813 XMLNode &after = alist->get_state();
1814 session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
1817 commit_reversible_command ();
1821 Editor::start_cursor_grab (ArdourCanvas::Item* item, GdkEvent* event)
1823 drag_info.item = item;
1824 drag_info.motion_callback = &Editor::cursor_drag_motion_callback;
1825 drag_info.finished_callback = &Editor::cursor_drag_finished_callback;
1829 if ((drag_info.data = (item->get_data ("cursor"))) == 0) {
1830 fatal << _("programming error: cursor canvas item has no cursor data pointer!") << endmsg;
1834 Cursor* cursor = (Cursor *) drag_info.data;
1836 if (cursor == playhead_cursor) {
1837 _dragging_playhead = true;
1839 if (session && drag_info.was_rolling) {
1840 session->request_stop ();
1843 if (session && session->is_auditioning()) {
1844 session->cancel_audition ();
1848 drag_info.pointer_frame_offset = drag_info.grab_frame - cursor->current_frame;
1850 show_verbose_time_cursor (cursor->current_frame, 10);
1854 Editor::cursor_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1856 Cursor* cursor = (Cursor *) drag_info.data;
1857 nframes_t adjusted_frame;
1859 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1860 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1866 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1867 if (cursor != edit_cursor || snap_type != SnapToEditCursor) {
1868 snap_to (adjusted_frame);
1872 if (adjusted_frame == drag_info.last_pointer_frame) return;
1874 cursor->set_position (adjusted_frame);
1876 if (cursor == edit_cursor) {
1877 edit_cursor_clock.set (cursor->current_frame);
1879 UpdateAllTransportClocks (cursor->current_frame);
1882 show_verbose_time_cursor (cursor->current_frame, 10);
1884 drag_info.last_pointer_frame = adjusted_frame;
1885 drag_info.first_move = false;
1889 Editor::cursor_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1891 if (drag_info.first_move) return;
1893 cursor_drag_motion_callback (item, event);
1895 _dragging_playhead = false;
1897 if (item == &playhead_cursor->canvas_item) {
1899 session->request_locate (playhead_cursor->current_frame, drag_info.was_rolling);
1901 } else if (item == &edit_cursor->canvas_item) {
1902 edit_cursor->set_position (edit_cursor->current_frame);
1903 edit_cursor_clock.set (edit_cursor->current_frame);
1908 Editor::update_marker_drag_item (Location *location)
1910 double x1 = frame_to_pixel (location->start());
1911 double x2 = frame_to_pixel (location->end());
1913 if (location->is_mark()) {
1914 marker_drag_line_points.front().set_x(x1);
1915 marker_drag_line_points.back().set_x(x1);
1916 marker_drag_line->property_points() = marker_drag_line_points;
1919 range_marker_drag_rect->property_x1() = x1;
1920 range_marker_drag_rect->property_x2() = x2;
1925 Editor::start_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
1929 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1930 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
1936 Location *location = find_location_from_marker (marker, is_start);
1938 drag_info.item = item;
1939 drag_info.data = marker;
1940 drag_info.motion_callback = &Editor::marker_drag_motion_callback;
1941 drag_info.finished_callback = &Editor::marker_drag_finished_callback;
1945 drag_info.copied_location = new Location (*location);
1946 drag_info.pointer_frame_offset = drag_info.grab_frame - (is_start ? location->start() : location->end());
1948 update_marker_drag_item (location);
1950 if (location->is_mark()) {
1951 marker_drag_line->show();
1952 marker_drag_line->raise_to_top();
1955 range_marker_drag_rect->show();
1956 range_marker_drag_rect->raise_to_top();
1959 if (is_start) show_verbose_time_cursor (location->start(), 10);
1960 else show_verbose_time_cursor (location->end(), 10);
1964 Editor::marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1967 Marker* marker = (Marker *) drag_info.data;
1968 Location *real_location;
1969 Location *copy_location;
1971 bool move_both = false;
1975 if (drag_info.pointer_frame_offset <= drag_info.current_pointer_frame) {
1976 newframe = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1981 nframes_t next = newframe;
1983 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1984 snap_to (newframe, 0, true);
1987 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
1991 /* call this to find out if its the start or end */
1993 real_location = find_location_from_marker (marker, is_start);
1995 /* use the copy that we're "dragging" around */
1997 copy_location = drag_info.copied_location;
1999 f_delta = copy_location->end() - copy_location->start();
2001 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
2005 if (copy_location->is_mark()) {
2008 copy_location->set_start (newframe);
2012 if (is_start) { // start-of-range marker
2015 copy_location->set_start (newframe);
2016 copy_location->set_end (newframe + f_delta);
2017 } else if (newframe < copy_location->end()) {
2018 copy_location->set_start (newframe);
2020 snap_to (next, 1, true);
2021 copy_location->set_end (next);
2022 copy_location->set_start (newframe);
2025 } else { // end marker
2028 copy_location->set_end (newframe);
2029 copy_location->set_start (newframe - f_delta);
2030 } else if (newframe > copy_location->start()) {
2031 copy_location->set_end (newframe);
2033 } else if (newframe > 0) {
2034 snap_to (next, -1, true);
2035 copy_location->set_start (next);
2036 copy_location->set_end (newframe);
2041 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
2042 drag_info.first_move = false;
2044 update_marker_drag_item (copy_location);
2046 LocationMarkers* lm = find_location_markers (real_location);
2047 lm->set_position (copy_location->start(), copy_location->end());
2049 show_verbose_time_cursor (newframe, 10);
2053 Editor::marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2055 if (drag_info.first_move) {
2056 marker_drag_motion_callback (item, event);
2060 Marker* marker = (Marker *) drag_info.data;
2064 begin_reversible_command ( _("move marker") );
2065 XMLNode &before = session->locations()->get_state();
2067 Location * location = find_location_from_marker (marker, is_start);
2070 if (location->is_mark()) {
2071 location->set_start (drag_info.copied_location->start());
2073 location->set (drag_info.copied_location->start(), drag_info.copied_location->end());
2077 XMLNode &after = session->locations()->get_state();
2078 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
2079 commit_reversible_command ();
2081 marker_drag_line->hide();
2082 range_marker_drag_rect->hide();
2086 Editor::start_meter_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2089 MeterMarker* meter_marker;
2091 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2092 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2096 meter_marker = dynamic_cast<MeterMarker*> (marker);
2098 MetricSection& section (meter_marker->meter());
2100 if (!section.movable()) {
2104 drag_info.item = item;
2105 drag_info.data = marker;
2106 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2107 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2111 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2113 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2117 Editor::start_meter_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2120 MeterMarker* meter_marker;
2122 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2123 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2127 meter_marker = dynamic_cast<MeterMarker*> (marker);
2129 // create a dummy marker for visual representation of moving the copy.
2130 // The actual copying is not done before we reach the finish callback.
2132 snprintf (name, sizeof(name), "%g/%g", meter_marker->meter().beats_per_bar(), meter_marker->meter().note_divisor ());
2133 MeterMarker* new_marker = new MeterMarker(*this, *meter_group, ARDOUR_UI::config()->canvasvar_MeterMarker.get(), name,
2134 *new MeterSection(meter_marker->meter()));
2136 drag_info.item = &new_marker->the_item();
2137 drag_info.copy = true;
2138 drag_info.data = new_marker;
2139 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2140 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2144 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2146 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2150 Editor::meter_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2152 MeterMarker* marker = (MeterMarker *) drag_info.data;
2153 nframes_t adjusted_frame;
2155 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2156 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2162 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2163 snap_to (adjusted_frame);
2166 if (adjusted_frame == drag_info.last_pointer_frame) return;
2168 marker->set_position (adjusted_frame);
2171 drag_info.last_pointer_frame = adjusted_frame;
2172 drag_info.first_move = false;
2174 show_verbose_time_cursor (adjusted_frame, 10);
2178 Editor::meter_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2180 if (drag_info.first_move) return;
2182 meter_marker_drag_motion_callback (drag_info.item, event);
2184 MeterMarker* marker = (MeterMarker *) drag_info.data;
2187 TempoMap& map (session->tempo_map());
2188 map.bbt_time (drag_info.last_pointer_frame, when);
2190 if (drag_info.copy == true) {
2191 begin_reversible_command (_("copy meter mark"));
2192 XMLNode &before = map.get_state();
2193 map.add_meter (marker->meter(), when);
2194 XMLNode &after = map.get_state();
2195 session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2196 commit_reversible_command ();
2198 // delete the dummy marker we used for visual representation of copying.
2199 // a new visual marker will show up automatically.
2202 begin_reversible_command (_("move meter mark"));
2203 XMLNode &before = map.get_state();
2204 map.move_meter (marker->meter(), when);
2205 XMLNode &after = map.get_state();
2206 session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2207 commit_reversible_command ();
2212 Editor::start_tempo_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2215 TempoMarker* tempo_marker;
2217 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2218 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2222 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2223 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2227 MetricSection& section (tempo_marker->tempo());
2229 if (!section.movable()) {
2233 drag_info.item = item;
2234 drag_info.data = marker;
2235 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2236 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2240 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2241 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2245 Editor::start_tempo_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2248 TempoMarker* tempo_marker;
2250 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2251 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2255 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2256 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2260 // create a dummy marker for visual representation of moving the copy.
2261 // The actual copying is not done before we reach the finish callback.
2263 snprintf (name, sizeof (name), "%.2f", tempo_marker->tempo().beats_per_minute());
2264 TempoMarker* new_marker = new TempoMarker(*this, *tempo_group, ARDOUR_UI::config()->canvasvar_TempoMarker.get(), name,
2265 *new TempoSection(tempo_marker->tempo()));
2267 drag_info.item = &new_marker->the_item();
2268 drag_info.copy = true;
2269 drag_info.data = new_marker;
2270 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2271 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2275 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2277 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2281 Editor::tempo_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2283 TempoMarker* marker = (TempoMarker *) drag_info.data;
2284 nframes_t adjusted_frame;
2286 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2287 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2293 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2294 snap_to (adjusted_frame);
2297 if (adjusted_frame == drag_info.last_pointer_frame) return;
2299 /* OK, we've moved far enough to make it worth actually move the thing. */
2301 marker->set_position (adjusted_frame);
2303 show_verbose_time_cursor (adjusted_frame, 10);
2305 drag_info.last_pointer_frame = adjusted_frame;
2306 drag_info.first_move = false;
2310 Editor::tempo_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2312 if (drag_info.first_move) return;
2314 tempo_marker_drag_motion_callback (drag_info.item, event);
2316 TempoMarker* marker = (TempoMarker *) drag_info.data;
2319 TempoMap& map (session->tempo_map());
2320 map.bbt_time (drag_info.last_pointer_frame, when);
2322 if (drag_info.copy == true) {
2323 begin_reversible_command (_("copy tempo mark"));
2324 XMLNode &before = map.get_state();
2325 map.add_tempo (marker->tempo(), when);
2326 XMLNode &after = map.get_state();
2327 session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2328 commit_reversible_command ();
2330 // delete the dummy marker we used for visual representation of copying.
2331 // a new visual marker will show up automatically.
2334 begin_reversible_command (_("move tempo mark"));
2335 XMLNode &before = map.get_state();
2336 map.move_tempo (marker->tempo(), when);
2337 XMLNode &after = map.get_state();
2338 session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2339 commit_reversible_command ();
2344 Editor::remove_gain_control_point (ArdourCanvas::Item*item, GdkEvent* event)
2346 ControlPoint* control_point;
2348 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2349 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2353 // We shouldn't remove the first or last gain point
2354 if (control_point->line().is_last_point(*control_point) ||
2355 control_point->line().is_first_point(*control_point)) {
2359 control_point->line().remove_point (*control_point);
2363 Editor::remove_control_point (ArdourCanvas::Item* item, GdkEvent* event)
2365 ControlPoint* control_point;
2367 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2368 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2372 control_point->line().remove_point (*control_point);
2376 Editor::start_control_point_grab (ArdourCanvas::Item* item, GdkEvent* event)
2378 ControlPoint* control_point;
2380 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2381 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2385 drag_info.item = item;
2386 drag_info.data = control_point;
2387 drag_info.motion_callback = &Editor::control_point_drag_motion_callback;
2388 drag_info.finished_callback = &Editor::control_point_drag_finished_callback;
2390 start_grab (event, fader_cursor);
2392 control_point->line().start_drag (control_point, drag_info.grab_frame, 0);
2394 double fraction = 1.0 - ((control_point->get_y() - control_point->line().y_position()) / (double)control_point->line().height());
2395 set_verbose_canvas_cursor (control_point->line().get_verbose_cursor_string (fraction),
2396 drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
2398 show_verbose_canvas_cursor ();
2402 Editor::control_point_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2404 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2406 double cx = drag_info.current_pointer_x;
2407 double cy = drag_info.current_pointer_y;
2409 drag_info.cumulative_x_drag = cx - drag_info.grab_x ;
2410 drag_info.cumulative_y_drag = cy - drag_info.grab_y ;
2412 if (drag_info.x_constrained) {
2413 cx = drag_info.grab_x;
2415 if (drag_info.y_constrained) {
2416 cy = drag_info.grab_y;
2419 cp->line().parent_group().w2i (cx, cy);
2423 cy = min ((double) (cp->line().y_position() + cp->line().height()), cy);
2425 //translate cx to frames
2426 nframes_t cx_frames = unit_to_frame (cx);
2428 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier()) && !drag_info.x_constrained) {
2429 snap_to (cx_frames);
2432 const double fraction = 1.0 - ((cy - cp->line().y_position()) / (double)cp->line().height());
2436 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
2442 cp->line().point_drag (*cp, cx_frames , fraction, push);
2444 set_verbose_canvas_cursor_text (cp->line().get_verbose_cursor_string (fraction));
2446 drag_info.first_move = false;
2450 Editor::control_point_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2452 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2454 if (drag_info.first_move) {
2458 if ((event->type == GDK_BUTTON_RELEASE) && (event->button.button == 1) && Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
2459 reset_point_selection ();
2463 control_point_drag_motion_callback (item, event);
2465 cp->line().end_drag (cp);
2469 Editor::start_line_grab_from_regionview (ArdourCanvas::Item* item, GdkEvent* event)
2471 switch (mouse_mode) {
2473 assert(dynamic_cast<AudioRegionView*>(clicked_regionview));
2474 start_line_grab (dynamic_cast<AudioRegionView*>(clicked_regionview)->get_gain_line(), event);
2482 Editor::start_line_grab_from_line (ArdourCanvas::Item* item, GdkEvent* event)
2486 if ((al = reinterpret_cast<AutomationLine*> (item->get_data ("line"))) == 0) {
2487 fatal << _("programming error: line canvas item has no line pointer!") << endmsg;
2491 start_line_grab (al, event);
2495 Editor::start_line_grab (AutomationLine* line, GdkEvent* event)
2499 nframes_t frame_within_region;
2501 /* need to get x coordinate in terms of parent (TimeAxisItemView)
2505 cx = event->button.x;
2506 cy = event->button.y;
2507 line->parent_group().w2i (cx, cy);
2508 frame_within_region = (nframes_t) floor (cx * frames_per_unit);
2510 if (!line->control_points_adjacent (frame_within_region, current_line_drag_info.before,
2511 current_line_drag_info.after)) {
2512 /* no adjacent points */
2516 drag_info.item = &line->grab_item();
2517 drag_info.data = line;
2518 drag_info.motion_callback = &Editor::line_drag_motion_callback;
2519 drag_info.finished_callback = &Editor::line_drag_finished_callback;
2521 start_grab (event, fader_cursor);
2523 const double fraction = 1.0 - ((cy - line->y_position()) / (double)line->height());
2525 line->start_drag (0, drag_info.grab_frame, fraction);
2527 set_verbose_canvas_cursor (line->get_verbose_cursor_string (fraction),
2528 drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
2529 show_verbose_canvas_cursor ();
2533 Editor::line_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2535 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
2536 double cx = drag_info.current_pointer_x;
2537 double cy = drag_info.current_pointer_y;
2539 line->parent_group().w2i (cx, cy);
2541 const double fraction = 1.0 - ((cy - line->y_position()) / (double)line->height());
2545 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
2551 line->line_drag (current_line_drag_info.before, current_line_drag_info.after, fraction, push);
2553 set_verbose_canvas_cursor_text (line->get_verbose_cursor_string (fraction));
2557 Editor::line_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2559 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
2560 line_drag_motion_callback (item, event);
2565 Editor::start_region_grab (ArdourCanvas::Item* item, GdkEvent* event)
2567 if (selection->regions.empty() || clicked_regionview == 0) {
2571 drag_info.copy = false;
2572 drag_info.item = item;
2573 drag_info.data = clicked_regionview;
2574 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2575 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2580 TimeAxisView* tvp = clicked_axisview;
2581 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2583 if (tv && tv->is_track()) {
2584 speed = tv->get_diskstream()->speed();
2587 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2588 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2589 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2590 // we want a move threshold
2591 drag_info.want_move_threshold = true;
2593 show_verbose_time_cursor (drag_info.last_frame_position, 10);
2595 begin_reversible_command (_("move region(s)"));
2599 Editor::start_region_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2601 if (selection->regions.empty() || clicked_regionview == 0) {
2605 drag_info.copy = true;
2606 drag_info.item = item;
2607 drag_info.data = clicked_regionview;
2611 TimeAxisView* tv = &clicked_regionview->get_time_axis_view();
2612 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
2615 if (rtv && rtv->is_track()) {
2616 speed = rtv->get_diskstream()->speed();
2619 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2620 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2621 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2622 // we want a move threshold
2623 drag_info.want_move_threshold = true;
2624 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2625 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2626 show_verbose_time_cursor (drag_info.last_frame_position, 10);
2630 Editor::start_region_brush_grab (ArdourCanvas::Item* item, GdkEvent* event)
2632 if (selection->regions.empty() || clicked_regionview == 0) {
2636 drag_info.copy = false;
2637 drag_info.item = item;
2638 drag_info.data = clicked_regionview;
2639 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2640 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2645 TimeAxisView* tvp = clicked_axisview;
2646 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2648 if (tv && tv->is_track()) {
2649 speed = tv->get_diskstream()->speed();
2652 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2653 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2654 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2655 // we want a move threshold
2656 drag_info.want_move_threshold = true;
2657 drag_info.brushing = true;
2659 begin_reversible_command (_("Drag region brush"));
2663 Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2667 RegionView* rv = reinterpret_cast<RegionView*> (drag_info.data);
2668 nframes_t pending_region_position = 0;
2669 int32_t pointer_y_span = 0, canvas_pointer_y_span = 0, original_pointer_order;
2670 int32_t visible_y_high = 0, visible_y_low = 512; //high meaning higher numbered.. not the height on the screen
2671 bool clamp_y_axis = false;
2672 vector<int32_t> height_list(512) ;
2673 vector<int32_t>::iterator j;
2675 if (drag_info.copy && drag_info.move_threshold_passed && drag_info.want_move_threshold) {
2677 drag_info.want_move_threshold = false; // don't copy again
2679 /* duplicate the region(s) */
2681 vector<RegionView*> new_regionviews;
2683 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
2689 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
2690 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
2693 nrv = new AudioRegionView (*arv);
2695 nrv = new MidiRegionView (*mrv);
2700 nrv->get_canvas_group()->show ();
2702 new_regionviews.push_back (nrv);
2705 if (new_regionviews.empty()) {
2709 /* reset selection to new regionviews */
2711 selection->set (new_regionviews);
2713 /* reset drag_info data to reflect the fact that we are dragging the copies */
2715 drag_info.data = new_regionviews.front();
2717 swap_grab (new_regionviews.front()->get_canvas_group (), 0, event->motion.time);
2720 /* Which trackview is this ? */
2722 TimeAxisView* tvp = trackview_by_y_position (drag_info.current_pointer_y);
2723 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2725 /* The region motion is only processed if the pointer is over
2729 if (!tv || !tv->is_track()) {
2730 /* To make sure we hide the verbose canvas cursor when the mouse is
2731 not held over a track.
2733 hide_verbose_canvas_cursor ();
2737 original_pointer_order = drag_info.last_trackview->order;
2739 /************************************************************
2741 ************************************************************/
2743 if (drag_info.brushing) {
2744 clamp_y_axis = true;
2749 if ((pointer_y_span = (drag_info.last_trackview->order - tv->order)) != 0) {
2751 int32_t children = 0, numtracks = 0;
2752 // XXX hard coding track limit, oh my, so very very bad
2753 bitset <1024> tracks (0x00);
2754 /* get a bitmask representing the visible tracks */
2756 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2757 TimeAxisView *tracklist_timeview;
2758 tracklist_timeview = (*i);
2759 RouteTimeAxisView* rtv2 = dynamic_cast<RouteTimeAxisView*>(tracklist_timeview);
2760 TimeAxisView::Children children_list;
2762 /* zeroes are audio tracks. ones are other types. */
2764 if (!rtv2->hidden()) {
2766 if (visible_y_high < rtv2->order) {
2767 visible_y_high = rtv2->order;
2769 if (visible_y_low > rtv2->order) {
2770 visible_y_low = rtv2->order;
2773 if (!rtv2->is_track()) {
2774 tracks = tracks |= (0x01 << rtv2->order);
2777 height_list[rtv2->order] = (*i)->height;
2779 if ((children_list = rtv2->get_child_list()).size() > 0) {
2780 for (TimeAxisView::Children::iterator j = children_list.begin(); j != children_list.end(); ++j) {
2781 tracks = tracks |= (0x01 << (rtv2->order + children));
2782 height_list[rtv2->order + children] = (*j)->height;
2790 /* find the actual span according to the canvas */
2792 canvas_pointer_y_span = pointer_y_span;
2793 if (drag_info.last_trackview->order >= tv->order) {
2795 for (y = tv->order; y < drag_info.last_trackview->order; y++) {
2796 if (height_list[y] == 0 ) {
2797 canvas_pointer_y_span--;
2802 for (y = drag_info.last_trackview->order;y <= tv->order; y++) {
2803 if ( height_list[y] == 0 ) {
2804 canvas_pointer_y_span++;
2809 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
2810 RegionView* rv2 = (*i);
2811 double ix1, ix2, iy1, iy2;
2814 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
2815 rv2->get_canvas_group()->i2w (ix1, iy1);
2816 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
2817 RouteTimeAxisView* rtv2 = dynamic_cast<RouteTimeAxisView*>(tvp2);
2819 if (rtv2->order != original_pointer_order) {
2820 /* this isn't the pointer track */
2822 if (canvas_pointer_y_span > 0) {
2824 /* moving up the canvas */
2825 if ((rtv2->order - canvas_pointer_y_span) >= visible_y_low) {
2827 int32_t visible_tracks = 0;
2828 while (visible_tracks < canvas_pointer_y_span ) {
2831 while (height_list[rtv2->order - (visible_tracks - n)] == 0) {
2832 /* we're passing through a hidden track */
2837 if (tracks[rtv2->order - (canvas_pointer_y_span - n)] != 0x00) {
2838 clamp_y_axis = true;
2842 clamp_y_axis = true;
2845 } else if (canvas_pointer_y_span < 0) {
2847 /*moving down the canvas*/
2849 if ((rtv2->order - (canvas_pointer_y_span - n)) <= visible_y_high) { // we will overflow
2852 int32_t visible_tracks = 0;
2854 while (visible_tracks > canvas_pointer_y_span ) {
2857 while (height_list[rtv2->order - (visible_tracks - n)] == 0) {
2861 if ( tracks[rtv2->order - ( canvas_pointer_y_span - n)] != 0x00) {
2862 clamp_y_axis = true;
2867 clamp_y_axis = true;
2873 /* this is the pointer's track */
2874 if ((rtv2->order - pointer_y_span) > visible_y_high) { // we will overflow
2875 clamp_y_axis = true;
2876 } else if ((rtv2->order - pointer_y_span) < visible_y_low) { // we will underflow
2877 clamp_y_axis = true;
2885 } else if (drag_info.last_trackview == tv) {
2886 clamp_y_axis = true;
2890 if (!clamp_y_axis) {
2891 drag_info.last_trackview = tv;
2894 /************************************************************
2896 ************************************************************/
2898 /* compute the amount of pointer motion in frames, and where
2899 the region would be if we moved it by that much.
2902 if (drag_info.move_threshold_passed) {
2904 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2906 nframes_t sync_frame;
2907 nframes_t sync_offset;
2910 pending_region_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2912 sync_offset = rv->region()->sync_offset (sync_dir);
2913 sync_frame = rv->region()->adjust_to_sync (pending_region_position);
2915 /* we snap if the snap modifier is not enabled.
2918 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2919 snap_to (sync_frame);
2922 if (sync_frame - sync_offset <= sync_frame) {
2923 pending_region_position = sync_frame - (sync_dir*sync_offset);
2925 pending_region_position = 0;
2929 pending_region_position = 0;
2932 if (pending_region_position > max_frames - rv->region()->length()) {
2933 pending_region_position = drag_info.last_frame_position;
2936 // printf ("3: pending_region_position= %lu %lu\n", pending_region_position, drag_info.last_frame_position );
2938 if (pending_region_position != drag_info.last_frame_position && !drag_info.x_constrained) {
2940 /* now compute the canvas unit distance we need to move the regiondrag_info.last_trackview->order
2941 to make it appear at the new location.
2944 if (pending_region_position > drag_info.last_frame_position) {
2945 x_delta = ((double) (pending_region_position - drag_info.last_frame_position) / frames_per_unit);
2947 x_delta = -((double) (drag_info.last_frame_position - pending_region_position) / frames_per_unit);
2950 drag_info.last_frame_position = pending_region_position;
2957 /* threshold not passed */
2962 /*************************************************************
2964 ************************************************************/
2966 if (x_delta == 0 && (pointer_y_span == 0)) {
2967 /* haven't reached next snap point, and we're not switching
2968 trackviews. nothing to do.
2975 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
2977 RegionView* rv2 = (*i);
2979 // If any regionview is at zero, we need to know so we can stop further leftward motion.
2981 double ix1, ix2, iy1, iy2;
2982 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
2983 rv2->get_canvas_group()->i2w (ix1, iy1);
2992 /*************************************************************
2994 ************************************************************/
2998 if (drag_info.first_move) {
2999 if (drag_info.move_threshold_passed) {
3010 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
3011 const list<RegionView*>& layered_regions = selection->regions.by_layer();
3013 for (list<RegionView*>::const_iterator i = layered_regions.begin(); i != layered_regions.end(); ++i) {
3015 RegionView* rv = (*i);
3016 double ix1, ix2, iy1, iy2;
3017 int32_t temp_pointer_y_span = pointer_y_span;
3019 /* get item BBox, which will be relative to parent. so we have
3020 to query on a child, then convert to world coordinates using
3024 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3025 rv->get_canvas_group()->i2w (ix1, iy1);
3026 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3027 RouteTimeAxisView* canvas_rtv = dynamic_cast<RouteTimeAxisView*>(tvp2);
3028 RouteTimeAxisView* temp_rtv;
3030 if ((pointer_y_span != 0) && !clamp_y_axis) {
3033 for (j = height_list.begin(); j!= height_list.end(); j++) {
3034 if (x == canvas_rtv->order) {
3035 /* we found the track the region is on */
3036 if (x != original_pointer_order) {
3037 /*this isn't from the same track we're dragging from */
3038 temp_pointer_y_span = canvas_pointer_y_span;
3040 while (temp_pointer_y_span > 0) {
3041 /* we're moving up canvas-wise,
3042 so we need to find the next track height
3044 if (j != height_list.begin()) {
3047 if (x != original_pointer_order) {
3048 /* we're not from the dragged track, so ignore hidden tracks. */
3050 temp_pointer_y_span++;
3054 temp_pointer_y_span--;
3056 while (temp_pointer_y_span < 0) {
3058 if (x != original_pointer_order) {
3060 temp_pointer_y_span--;
3064 if (j != height_list.end()) {
3067 temp_pointer_y_span++;
3069 /* find out where we'll be when we move and set height accordingly */
3071 tvp2 = trackview_by_y_position (iy1 + y_delta);
3072 temp_rtv = dynamic_cast<RouteTimeAxisView*>(tvp2);
3073 rv->set_y_position_and_height (0, temp_rtv->height);
3075 /* if you un-comment the following, the region colours will follow the track colours whilst dragging,
3076 personally, i think this can confuse things, but never mind.
3079 //const GdkColor& col (temp_rtv->view->get_region_color());
3080 //rv->set_color (const_cast<GdkColor&>(col));
3087 /* prevent the regionview from being moved to before
3088 the zero position on the canvas.
3093 if (-x_delta > ix1) {
3096 } else if ((x_delta > 0) && (rv->region()->last_frame() > max_frames - x_delta)) {
3097 x_delta = max_frames - rv->region()->last_frame();
3101 if (drag_info.first_move) {
3103 /* hide any dependent views */
3105 rv->get_time_axis_view().hide_dependent_views (*rv);
3107 /* this is subtle. raising the regionview itself won't help,
3108 because raise_to_top() just puts the item on the top of
3109 its parent's stack. so, we need to put the trackview canvas_display group
3110 on the top, since its parent is the whole canvas.
3113 rv->get_canvas_group()->raise_to_top();
3114 rv->get_time_axis_view().canvas_display->raise_to_top();
3115 cursor_group->raise_to_top();
3117 rv->fake_set_opaque (true);
3120 if (drag_info.brushing) {
3121 mouse_brush_insert_region (rv, pending_region_position);
3123 rv->move (x_delta, y_delta);
3126 } /* foreach region */
3130 if (drag_info.first_move && drag_info.move_threshold_passed) {
3131 cursor_group->raise_to_top();
3132 drag_info.first_move = false;
3135 if (x_delta != 0 && !drag_info.brushing) {
3136 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3141 Editor::region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3144 RegionView* rv = reinterpret_cast<RegionView *> (drag_info.data);
3145 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
3146 bool nocommit = true;
3148 RouteTimeAxisView* rtv;
3149 bool regionview_y_movement;
3150 bool regionview_x_movement;
3151 vector<RegionView*> copies;
3153 /* first_move is set to false if the regionview has been moved in the
3157 if (drag_info.first_move) {
3164 /* The regionview has been moved at some stage during the grab so we need
3165 to account for any mouse movement between this event and the last one.
3168 region_drag_motion_callback (item, event);
3170 if (drag_info.brushing) {
3171 /* all changes were made during motion event handlers */
3173 if (drag_info.copy) {
3174 for (list<RegionView*>::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
3175 copies.push_back (*i);
3182 /* adjust for track speed */
3185 rtv = dynamic_cast<RouteTimeAxisView*> (drag_info.last_trackview);
3186 if (rtv && rtv->get_diskstream()) {
3187 speed = rtv->get_diskstream()->speed();
3190 regionview_x_movement = (drag_info.last_frame_position != (nframes_t) (rv->region()->position()/speed));
3191 regionview_y_movement = (drag_info.last_trackview != &rv->get_time_axis_view());
3193 //printf ("last_frame: %s position is %lu %g\n", rv->get_time_axis_view().name().c_str(), drag_info.last_frame_position, speed);
3194 //printf ("last_rackview: %s \n", drag_info.last_trackview->name().c_str());
3198 if (drag_info.copy) {
3199 if (drag_info.x_constrained) {
3200 op_string = _("fixed time region copy");
3202 op_string = _("region copy");
3205 if (drag_info.x_constrained) {
3206 op_string = _("fixed time region drag");
3208 op_string = _("region drag");
3212 begin_reversible_command (op_string);
3214 if (regionview_y_movement) {
3216 /* moved to a different audio track. */
3218 vector<RegionView*> new_selection;
3220 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ) {
3222 RegionView* rv = (*i);
3224 double ix1, ix2, iy1, iy2;
3226 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3227 rv->get_canvas_group()->i2w (ix1, iy1);
3228 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3229 RouteTimeAxisView* rtv2 = dynamic_cast<RouteTimeAxisView*>(tvp2);
3231 boost::shared_ptr<Playlist> from_playlist = rv->region()->playlist();
3232 boost::shared_ptr<Playlist> to_playlist = rtv2->playlist();
3234 where = (nframes_t) (unit_to_frame (ix1) * speed);
3235 boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region()));
3237 /* undo the previous hide_dependent_views so that xfades don't
3238 disappear on copying regions
3241 rv->get_time_axis_view().reveal_dependent_views (*rv);
3243 if (!drag_info.copy) {
3245 /* the region that used to be in the old playlist is not
3246 moved to the new one - we make a copy of it. as a result,
3247 any existing editor for the region should no longer be
3251 rv->hide_region_editor();
3252 rv->fake_set_opaque (false);
3254 session->add_command (new MementoCommand<Playlist>(*from_playlist, &from_playlist->get_state(), 0));
3255 from_playlist->remove_region ((rv->region()));
3256 session->add_command (new MementoCommand<Playlist>(*from_playlist, 0, &from_playlist->get_state()));
3260 /* the regionview we dragged around is a temporary copy, queue it for deletion */
3262 copies.push_back (rv);
3265 latest_regionview = 0;
3267 sigc::connection c = rtv2->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3268 session->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
3269 to_playlist->add_region (new_region, where);
3270 session->add_command (new MementoCommand<Playlist>(*to_playlist, 0, &to_playlist->get_state()));
3273 if (latest_regionview) {
3274 new_selection.push_back (latest_regionview);
3277 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
3278 was selected in all of them, then removing it from the playlist will have removed all
3279 trace of it from the selection (i.e. there were N regions selected, we removed 1,
3280 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
3281 corresponding regionview, and the selection is now empty).
3283 this could have invalidated any and all iterators into the region selection.
3285 the heuristic we use here is: if the region selection is empty, break out of the loop
3286 here. if the region selection is not empty, then restart the loop because we know that
3287 we must have removed at least the region(view) we've just been working on as well as any
3288 that we processed on previous iterations.
3290 EXCEPT .... if we are doing a copy drag, then the selection hasn't been modified and
3291 we can just iterate.
3294 if (drag_info.copy) {
3297 if (selection->regions.empty()) {
3300 i = selection->regions.by_layer().begin();
3305 selection->set (new_selection);
3309 /* motion within a single track */
3311 list<RegionView*> regions = selection->regions.by_layer();
3313 if (drag_info.copy) {
3314 selection->clear_regions();
3317 for (list<RegionView*>::iterator i = regions.begin(); i != regions.end(); ++i) {
3321 if (!rv->region()->can_move()) {
3325 if (regionview_x_movement) {
3326 double ownspeed = 1.0;
3327 rtv = dynamic_cast<RouteTimeAxisView*> (&(rv->get_time_axis_view()));
3329 if (rtv && rtv->get_diskstream()) {
3330 ownspeed = rtv->get_diskstream()->speed();
3333 /* base the new region position on the current position of the regionview.*/
3335 double ix1, ix2, iy1, iy2;
3337 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3338 rv->get_canvas_group()->i2w (ix1, iy1);
3339 where = (nframes_t) (unit_to_frame (ix1) * ownspeed);
3343 where = rv->region()->position();
3346 boost::shared_ptr<Playlist> to_playlist = rv->region()->playlist();
3348 assert (to_playlist);
3352 session->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
3354 if (drag_info.copy) {
3356 boost::shared_ptr<Region> newregion;
3357 boost::shared_ptr<Region> ar;
3358 boost::shared_ptr<Region> mr;
3360 if ((ar = boost::dynamic_pointer_cast<AudioRegion>(rv->region())) != 0) {
3361 newregion = RegionFactory::create (ar);
3362 } else if ((mr = boost::dynamic_pointer_cast<MidiRegion>(rv->region())) != 0) {
3363 newregion = RegionFactory::create (mr);
3368 latest_regionview = 0;
3369 sigc::connection c = rtv->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3370 to_playlist->add_region (newregion, (nframes_t) (where * rtv->get_diskstream()->speed()));
3373 if (latest_regionview) {
3374 rtv->reveal_dependent_views (*latest_regionview);
3375 selection->add (latest_regionview);
3380 /* just change the model */
3382 rv->region()->set_position (where, (void*) this);
3388 session->add_command (new MementoCommand<Playlist>(*to_playlist, 0, &to_playlist->get_state()));
3390 if (drag_info.copy) {
3391 copies.push_back (rv);
3399 commit_reversible_command ();
3402 for (vector<RegionView*>::iterator x = copies.begin(); x != copies.end(); ++x) {
3408 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
3410 /* Either add to or set the set the region selection, unless
3411 this is an alignment click (control used)
3414 if (Keyboard::modifier_state_contains (event->state, Keyboard::Control)) {
3415 TimeAxisView* tv = &rv.get_time_axis_view();
3416 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
3418 if (rtv && rtv->is_track()) {
3419 speed = rtv->get_diskstream()->speed();
3422 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Alt))) {
3424 align_region (rv.region(), SyncPoint, (nframes_t) (edit_cursor->current_frame * speed));
3426 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Shift))) {
3428 align_region (rv.region(), End, (nframes_t) (edit_cursor->current_frame * speed));
3432 align_region (rv.region(), Start, (nframes_t) (edit_cursor->current_frame * speed));
3438 Editor::show_verbose_time_cursor (nframes_t frame, double offset, double xpos, double ypos)
3444 nframes_t frame_rate;
3451 switch (Profile->get_small_screen() ? ARDOUR_UI::instance()->primary_clock.mode () : ARDOUR_UI::instance()->secondary_clock.mode ()) {
3452 case AudioClock::BBT:
3453 session->bbt_time (frame, bbt);
3454 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, bbt.bars, bbt.beats, bbt.ticks);
3457 case AudioClock::SMPTE:
3458 session->smpte_time (frame, smpte);
3459 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
3462 case AudioClock::MinSec:
3463 /* XXX this is copied from show_verbose_duration_cursor() */
3464 frame_rate = session->frame_rate();
3465 hours = frame / (frame_rate * 3600);
3466 frame = frame % (frame_rate * 3600);
3467 mins = frame / (frame_rate * 60);
3468 frame = frame % (frame_rate * 60);
3469 secs = (float) frame / (float) frame_rate;
3470 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
3474 snprintf (buf, sizeof(buf), "%u", frame);
3478 if (xpos >= 0 && ypos >=0) {
3479 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
3482 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
3484 show_verbose_canvas_cursor ();
3488 Editor::show_verbose_duration_cursor (nframes_t start, nframes_t end, double offset, double xpos, double ypos)
3495 nframes_t distance, frame_rate;
3497 Meter meter_at_start(session->tempo_map().meter_at(start));
3503 switch (ARDOUR_UI::instance()->secondary_clock.mode ()) {
3504 case AudioClock::BBT:
3505 session->bbt_time (start, sbbt);
3506 session->bbt_time (end, ebbt);
3509 /* XXX this computation won't work well if the
3510 user makes a selection that spans any meter changes.
3513 ebbt.bars -= sbbt.bars;
3514 if (ebbt.beats >= sbbt.beats) {
3515 ebbt.beats -= sbbt.beats;
3518 ebbt.beats = int(meter_at_start.beats_per_bar()) + ebbt.beats - sbbt.beats;
3520 if (ebbt.ticks >= sbbt.ticks) {
3521 ebbt.ticks -= sbbt.ticks;
3524 ebbt.ticks = int(Meter::ticks_per_beat) + ebbt.ticks - sbbt.ticks;
3527 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, ebbt.bars, ebbt.beats, ebbt.ticks);
3530 case AudioClock::SMPTE:
3531 session->smpte_duration (end - start, smpte);
3532 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
3535 case AudioClock::MinSec:
3536 /* XXX this stuff should be elsewhere.. */
3537 distance = end - start;
3538 frame_rate = session->frame_rate();
3539 hours = distance / (frame_rate * 3600);
3540 distance = distance % (frame_rate * 3600);
3541 mins = distance / (frame_rate * 60);
3542 distance = distance % (frame_rate * 60);
3543 secs = (float) distance / (float) frame_rate;
3544 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
3548 snprintf (buf, sizeof(buf), "%u", end - start);
3552 if (xpos >= 0 && ypos >=0) {
3553 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
3556 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
3558 show_verbose_canvas_cursor ();
3562 Editor::collect_new_region_view (RegionView* rv)
3564 latest_regionview = rv;
3568 Editor::start_selection_grab (ArdourCanvas::Item* item, GdkEvent* event)
3570 if (clicked_regionview == 0) {
3574 /* lets try to create new Region for the selection */
3576 vector<boost::shared_ptr<AudioRegion> > new_regions;
3577 create_region_from_selection (new_regions);
3579 if (new_regions.empty()) {
3583 /* XXX fix me one day to use all new regions */
3585 boost::shared_ptr<Region> region (new_regions.front());
3587 /* add it to the current stream/playlist.
3589 tricky: the streamview for the track will add a new regionview. we will
3590 catch the signal it sends when it creates the regionview to
3591 set the regionview we want to then drag.
3594 latest_regionview = 0;
3595 sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3597 /* A selection grab currently creates two undo/redo operations, one for
3598 creating the new region and another for moving it.
3601 begin_reversible_command (_("selection grab"));
3603 boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
3605 XMLNode *before = &(playlist->get_state());
3606 clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
3607 XMLNode *after = &(playlist->get_state());
3608 session->add_command(new MementoCommand<Playlist>(*playlist, before, after));
3610 commit_reversible_command ();
3614 if (latest_regionview == 0) {
3615 /* something went wrong */
3619 /* we need to deselect all other regionviews, and select this one
3620 i'm ignoring undo stuff, because the region creation will take care of it */
3621 selection->set (latest_regionview);
3623 drag_info.item = latest_regionview->get_canvas_group();
3624 drag_info.data = latest_regionview;
3625 drag_info.motion_callback = &Editor::region_drag_motion_callback;
3626 drag_info.finished_callback = &Editor::region_drag_finished_callback;
3630 drag_info.last_trackview = clicked_axisview;
3631 drag_info.last_frame_position = latest_regionview->region()->position();
3632 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
3634 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3638 Editor::cancel_selection ()
3640 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
3641 (*i)->hide_selection ();
3643 begin_reversible_command (_("cancel selection"));
3644 selection->clear ();
3645 clicked_selection = 0;
3646 commit_reversible_command ();
3650 Editor::start_selection_op (ArdourCanvas::Item* item, GdkEvent* event, SelectionOp op)
3652 nframes_t start = 0;
3659 drag_info.item = item;
3660 drag_info.motion_callback = &Editor::drag_selection;
3661 drag_info.finished_callback = &Editor::end_selection_op;
3666 case CreateSelection:
3667 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
3668 drag_info.copy = true;
3670 drag_info.copy = false;
3672 start_grab (event, selector_cursor);
3675 case SelectionStartTrim:
3676 if (clicked_axisview) {
3677 clicked_axisview->order_selection_trims (item, true);
3679 start_grab (event, trimmer_cursor);
3680 start = selection->time[clicked_selection].start;
3681 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
3684 case SelectionEndTrim:
3685 if (clicked_axisview) {
3686 clicked_axisview->order_selection_trims (item, false);
3688 start_grab (event, trimmer_cursor);
3689 end = selection->time[clicked_selection].end;
3690 drag_info.pointer_frame_offset = drag_info.grab_frame - end;
3694 start = selection->time[clicked_selection].start;
3696 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
3700 if (selection_op == SelectionMove) {
3701 show_verbose_time_cursor(start, 10);
3703 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
3708 Editor::drag_selection (ArdourCanvas::Item* item, GdkEvent* event)
3710 nframes_t start = 0;
3713 nframes_t pending_position;
3715 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
3716 pending_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
3718 pending_position = 0;
3721 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3722 snap_to (pending_position);
3725 /* only alter selection if the current frame is
3726 different from the last frame position (adjusted)
3729 if (pending_position == drag_info.last_pointer_frame) return;
3731 switch (selection_op) {
3732 case CreateSelection:
3734 if (drag_info.first_move) {
3735 snap_to (drag_info.grab_frame);
3738 if (pending_position < drag_info.grab_frame) {
3739 start = pending_position;
3740 end = drag_info.grab_frame;
3742 end = pending_position;
3743 start = drag_info.grab_frame;
3746 /* first drag: Either add to the selection
3747 or create a new selection->
3750 if (drag_info.first_move) {
3752 begin_reversible_command (_("range selection"));
3754 if (drag_info.copy) {
3755 /* adding to the selection */
3756 clicked_selection = selection->add (start, end);
3757 drag_info.copy = false;
3759 /* new selection-> */
3760 clicked_selection = selection->set (clicked_axisview, start, end);
3765 case SelectionStartTrim:
3767 if (drag_info.first_move) {
3768 begin_reversible_command (_("trim selection start"));
3771 start = selection->time[clicked_selection].start;
3772 end = selection->time[clicked_selection].end;
3774 if (pending_position > end) {
3777 start = pending_position;
3781 case SelectionEndTrim:
3783 if (drag_info.first_move) {
3784 begin_reversible_command (_("trim selection end"));
3787 start = selection->time[clicked_selection].start;
3788 end = selection->time[clicked_selection].end;
3790 if (pending_position < start) {
3793 end = pending_position;
3800 if (drag_info.first_move) {
3801 begin_reversible_command (_("move selection"));
3804 start = selection->time[clicked_selection].start;
3805 end = selection->time[clicked_selection].end;
3807 length = end - start;
3809 start = pending_position;
3812 end = start + length;
3817 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
3818 start_canvas_autoscroll (1);
3822 selection->replace (clicked_selection, start, end);
3825 drag_info.last_pointer_frame = pending_position;
3826 drag_info.first_move = false;
3828 if (selection_op == SelectionMove) {
3829 show_verbose_time_cursor(start, 10);
3831 show_verbose_time_cursor(pending_position, 10);
3836 Editor::end_selection_op (ArdourCanvas::Item* item, GdkEvent* event)
3838 if (!drag_info.first_move) {
3839 drag_selection (item, event);
3840 /* XXX this is not object-oriented programming at all. ick */
3841 if (selection->time.consolidate()) {
3842 selection->TimeChanged ();
3844 commit_reversible_command ();
3846 /* just a click, no pointer movement.*/
3848 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
3850 selection->clear_time();
3855 /* XXX what happens if its a music selection? */
3856 session->set_audio_range (selection->time);
3857 stop_canvas_autoscroll ();
3861 Editor::start_trim (ArdourCanvas::Item* item, GdkEvent* event)
3864 TimeAxisView* tvp = clicked_axisview;
3865 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
3867 if (tv && tv->is_track()) {
3868 speed = tv->get_diskstream()->speed();
3871 nframes_t region_start = (nframes_t) (clicked_regionview->region()->position() / speed);
3872 nframes_t region_end = (nframes_t) (clicked_regionview->region()->last_frame() / speed);
3873 nframes_t region_length = (nframes_t) (clicked_regionview->region()->length() / speed);
3875 //drag_info.item = clicked_regionview->get_name_highlight();
3876 drag_info.item = item;
3877 drag_info.motion_callback = &Editor::trim_motion_callback;
3878 drag_info.finished_callback = &Editor::trim_finished_callback;
3880 start_grab (event, trimmer_cursor);
3882 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
3883 trim_op = ContentsTrim;
3885 /* These will get overridden for a point trim.*/
3886 if (drag_info.current_pointer_frame < (region_start + region_length/2)) {
3887 /* closer to start */
3888 trim_op = StartTrim;
3889 } else if (drag_info.current_pointer_frame > (region_end - region_length/2)) {
3897 show_verbose_time_cursor(region_start, 10);
3900 show_verbose_time_cursor(region_end, 10);
3903 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
3909 Editor::trim_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
3911 RegionView* rv = clicked_regionview;
3912 nframes_t frame_delta = 0;
3913 bool left_direction;
3914 bool obey_snap = !Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier());
3916 /* snap modifier works differently here..
3917 its' current state has to be passed to the
3918 various trim functions in order to work properly
3922 TimeAxisView* tvp = clicked_axisview;
3923 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
3924 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
3926 if (tv && tv->is_track()) {
3927 speed = tv->get_diskstream()->speed();
3930 if (drag_info.last_pointer_frame > drag_info.current_pointer_frame) {
3931 left_direction = true;
3933 left_direction = false;
3937 snap_to (drag_info.current_pointer_frame);
3940 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
3944 if (drag_info.first_move) {
3950 trim_type = "Region start trim";
3953 trim_type = "Region end trim";
3956 trim_type = "Region content trim";
3960 begin_reversible_command (trim_type);
3962 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3963 (*i)->fake_set_opaque(false);
3964 (*i)->region()->freeze ();
3966 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
3968 arv->temporarily_hide_envelope ();
3970 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
3971 insert_result = motion_frozen_playlists.insert (pl);
3972 if (insert_result.second) {
3973 session->add_command(new MementoCommand<Playlist>(*pl, &pl->get_state(), 0));
3978 if (left_direction) {
3979 frame_delta = (drag_info.last_pointer_frame - drag_info.current_pointer_frame);
3981 frame_delta = (drag_info.current_pointer_frame - drag_info.last_pointer_frame);
3986 if ((left_direction == false) && (drag_info.current_pointer_frame <= rv->region()->first_frame()/speed)) {
3989 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3990 single_start_trim (**i, frame_delta, left_direction, obey_snap);
3996 if ((left_direction == true) && (drag_info.current_pointer_frame > (nframes_t) (rv->region()->last_frame()/speed))) {
3999 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4000 single_end_trim (**i, frame_delta, left_direction, obey_snap);
4007 bool swap_direction = false;
4009 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
4010 swap_direction = true;
4013 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4014 i != selection->regions.by_layer().end(); ++i)
4016 single_contents_trim (**i, frame_delta, left_direction, swap_direction, obey_snap);
4024 show_verbose_time_cursor((nframes_t) (rv->region()->position()/speed), 10);
4027 show_verbose_time_cursor((nframes_t) (rv->region()->last_frame()/speed), 10);
4030 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4034 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4035 drag_info.first_move = false;
4039 Editor::single_contents_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool swap_direction, bool obey_snap)
4041 boost::shared_ptr<Region> region (rv.region());
4043 if (region->locked()) {
4047 nframes_t new_bound;
4050 TimeAxisView* tvp = clicked_axisview;
4051 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4053 if (tv && tv->is_track()) {
4054 speed = tv->get_diskstream()->speed();
4057 if (left_direction) {
4058 if (swap_direction) {
4059 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4061 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4064 if (swap_direction) {
4065 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4067 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4072 snap_to (new_bound);
4074 region->trim_start ((nframes_t) (new_bound * speed), this);
4075 rv.region_changed (StartChanged);
4079 Editor::single_start_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool obey_snap)
4081 boost::shared_ptr<Region> region (rv.region());
4083 if (region->locked()) {
4087 nframes_t new_bound;
4090 TimeAxisView* tvp = clicked_axisview;
4091 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4093 if (tv && tv->is_track()) {
4094 speed = tv->get_diskstream()->speed();
4097 if (left_direction) {
4098 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4100 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4104 snap_to (new_bound, (left_direction ? 0 : 1));
4107 region->trim_front ((nframes_t) (new_bound * speed), this);
4109 rv.region_changed (Change (LengthChanged|PositionChanged|StartChanged));
4113 Editor::single_end_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool obey_snap)
4115 boost::shared_ptr<Region> region (rv.region());
4117 if (region->locked()) {
4121 nframes_t new_bound;
4124 TimeAxisView* tvp = clicked_axisview;
4125 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4127 if (tv && tv->is_track()) {
4128 speed = tv->get_diskstream()->speed();
4131 if (left_direction) {
4132 new_bound = (nframes_t) ((region->last_frame() + 1)/speed) - frame_delta;
4134 new_bound = (nframes_t) ((region->last_frame() + 1)/speed) + frame_delta;
4138 snap_to (new_bound);
4140 region->trim_end ((nframes_t) (new_bound * speed), this);
4141 rv.region_changed (LengthChanged);
4145 Editor::trim_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
4147 if (!drag_info.first_move) {
4148 trim_motion_callback (item, event);
4150 if (!clicked_regionview->get_selected()) {
4151 thaw_region_after_trim (*clicked_regionview);
4154 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4155 i != selection->regions.by_layer().end(); ++i)
4157 thaw_region_after_trim (**i);
4158 (*i)->fake_set_opaque (true);
4162 for (set<boost::shared_ptr<Playlist> >::iterator p = motion_frozen_playlists.begin(); p != motion_frozen_playlists.end(); ++p) {
4164 session->add_command (new MementoCommand<Playlist>(*(*p).get(), 0, &(*p)->get_state()));
4167 motion_frozen_playlists.clear ();
4169 commit_reversible_command();
4171 /* no mouse movement */
4177 Editor::point_trim (GdkEvent* event)
4179 RegionView* rv = clicked_regionview;
4180 nframes_t new_bound = drag_info.current_pointer_frame;
4182 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4183 snap_to (new_bound);
4186 /* Choose action dependant on which button was pressed */
4187 switch (event->button.button) {
4189 trim_op = StartTrim;
4190 begin_reversible_command (_("Start point trim"));
4192 if (rv->get_selected()) {
4194 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4195 i != selection->regions.by_layer().end(); ++i)
4197 if (!(*i)->region()->locked()) {
4198 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4199 XMLNode &before = pl->get_state();
4200 (*i)->region()->trim_front (new_bound, this);
4201 XMLNode &after = pl->get_state();
4202 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4208 if (!rv->region()->locked()) {
4209 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
4210 XMLNode &before = pl->get_state();
4211 rv->region()->trim_front (new_bound, this);
4212 XMLNode &after = pl->get_state();
4213 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4217 commit_reversible_command();
4222 begin_reversible_command (_("End point trim"));
4224 if (rv->get_selected()) {
4226 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
4228 if (!(*i)->region()->locked()) {
4229 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4230 XMLNode &before = pl->get_state();
4231 (*i)->region()->trim_end (new_bound, this);
4232 XMLNode &after = pl->get_state();
4233 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4239 if (!rv->region()->locked()) {
4240 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
4241 XMLNode &before = pl->get_state();
4242 rv->region()->trim_end (new_bound, this);
4243 XMLNode &after = pl->get_state();
4244 session->add_command (new MementoCommand<Playlist>(*pl.get(), &before, &after));
4248 commit_reversible_command();
4257 Editor::thaw_region_after_trim (RegionView& rv)
4259 boost::shared_ptr<Region> region (rv.region());
4261 if (region->locked()) {
4265 region->thaw (_("trimmed region"));
4266 XMLNode &after = region->playlist()->get_state();
4267 session->add_command (new MementoCommand<Playlist>(*(region->playlist()), 0, &after));
4269 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(&rv);
4271 arv->unhide_envelope ();
4275 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* event)
4280 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
4281 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
4285 Location* location = find_location_from_marker (marker, is_start);
4286 location->set_hidden (true, this);
4291 Editor::start_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event, RangeMarkerOp op)
4297 drag_info.item = item;
4298 drag_info.motion_callback = &Editor::drag_range_markerbar_op;
4299 drag_info.finished_callback = &Editor::end_range_markerbar_op;
4301 range_marker_op = op;
4303 if (!temp_location) {
4304 temp_location = new Location;
4308 case CreateRangeMarker:
4309 case CreateTransportMarker:
4311 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
4312 drag_info.copy = true;
4314 drag_info.copy = false;
4316 start_grab (event, selector_cursor);
4320 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4325 Editor::drag_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4327 nframes_t start = 0;
4329 ArdourCanvas::SimpleRect *crect = (range_marker_op == CreateRangeMarker) ? range_bar_drag_rect: transport_bar_drag_rect;
4331 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4332 snap_to (drag_info.current_pointer_frame);
4335 /* only alter selection if the current frame is
4336 different from the last frame position.
4339 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4341 switch (range_marker_op) {
4342 case CreateRangeMarker:
4343 case CreateTransportMarker:
4344 if (drag_info.first_move) {
4345 snap_to (drag_info.grab_frame);
4348 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4349 start = drag_info.current_pointer_frame;
4350 end = drag_info.grab_frame;
4352 end = drag_info.current_pointer_frame;
4353 start = drag_info.grab_frame;
4356 /* first drag: Either add to the selection
4357 or create a new selection.
4360 if (drag_info.first_move) {
4362 temp_location->set (start, end);
4366 update_marker_drag_item (temp_location);
4367 range_marker_drag_rect->show();
4368 range_marker_drag_rect->raise_to_top();
4374 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
4375 start_canvas_autoscroll (1);
4379 temp_location->set (start, end);
4381 double x1 = frame_to_pixel (start);
4382 double x2 = frame_to_pixel (end);
4383 crect->property_x1() = x1;
4384 crect->property_x2() = x2;
4386 update_marker_drag_item (temp_location);
4389 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4390 drag_info.first_move = false;
4392 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4397 Editor::end_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4399 Location * newloc = 0;
4402 if (!drag_info.first_move) {
4403 drag_range_markerbar_op (item, event);
4405 switch (range_marker_op) {
4406 case CreateRangeMarker:
4408 begin_reversible_command (_("new range marker"));
4409 XMLNode &before = session->locations()->get_state();
4410 session->locations()->next_available_name(rangename,"unnamed");
4411 newloc = new Location(temp_location->start(), temp_location->end(), rangename, Location::IsRangeMarker);
4412 session->locations()->add (newloc, true);
4413 XMLNode &after = session->locations()->get_state();
4414 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
4415 commit_reversible_command ();
4417 range_bar_drag_rect->hide();
4418 range_marker_drag_rect->hide();
4422 case CreateTransportMarker:
4423 // popup menu to pick loop or punch
4424 new_transport_marker_context_menu (&event->button, item);
4429 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
4431 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
4436 start = session->locations()->first_mark_before (drag_info.grab_frame);
4437 end = session->locations()->first_mark_after (drag_info.grab_frame);
4439 if (end == max_frames) {
4440 end = session->current_end_frame ();
4444 start = session->current_start_frame ();
4447 switch (mouse_mode) {
4449 /* find the two markers on either side and then make the selection from it */
4450 select_all_within (start, end, 0.0f, FLT_MAX, track_views, Selection::Set);
4454 /* find the two markers on either side of the click and make the range out of it */
4455 selection->set (0, start, end);
4464 stop_canvas_autoscroll ();
4470 Editor::start_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4472 drag_info.item = item;
4473 drag_info.motion_callback = &Editor::drag_mouse_zoom;
4474 drag_info.finished_callback = &Editor::end_mouse_zoom;
4476 start_grab (event, zoom_cursor);
4478 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4482 Editor::drag_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4487 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4488 snap_to (drag_info.current_pointer_frame);
4490 if (drag_info.first_move) {
4491 snap_to (drag_info.grab_frame);
4495 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4497 /* base start and end on initial click position */
4498 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4499 start = drag_info.current_pointer_frame;
4500 end = drag_info.grab_frame;
4502 end = drag_info.current_pointer_frame;
4503 start = drag_info.grab_frame;
4508 if (drag_info.first_move) {
4510 zoom_rect->raise_to_top();
4513 reposition_zoom_rect(start, end);
4515 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4516 drag_info.first_move = false;
4518 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4523 Editor::end_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4525 if (!drag_info.first_move) {
4526 drag_mouse_zoom (item, event);
4528 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
4529 temporal_zoom_by_frame (drag_info.grab_frame, drag_info.last_pointer_frame, "mouse zoom");
4531 temporal_zoom_by_frame (drag_info.last_pointer_frame, drag_info.grab_frame, "mouse zoom");
4534 temporal_zoom_to_frame (false, drag_info.grab_frame);
4536 temporal_zoom_step (false);
4537 center_screen (drag_info.grab_frame);
4545 Editor::reposition_zoom_rect (nframes_t start, nframes_t end)
4547 double x1 = frame_to_pixel (start);
4548 double x2 = frame_to_pixel (end);
4549 double y2 = full_canvas_height - 1.0;
4551 zoom_rect->property_x1() = x1;
4552 zoom_rect->property_y1() = 1.0;
4553 zoom_rect->property_x2() = x2;
4554 zoom_rect->property_y2() = y2;
4558 Editor::start_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4560 drag_info.item = item;
4561 drag_info.motion_callback = &Editor::drag_rubberband_select;
4562 drag_info.finished_callback = &Editor::end_rubberband_select;
4564 start_grab (event, cross_hair_cursor);
4566 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4570 Editor::drag_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4577 /* use a bigger drag threshold than the default */
4579 if (abs ((int) (drag_info.current_pointer_frame - drag_info.grab_frame)) < 8) {
4583 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4584 if (drag_info.first_move) {
4585 snap_to (drag_info.grab_frame);
4587 snap_to (drag_info.current_pointer_frame);
4590 /* base start and end on initial click position */
4592 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4593 start = drag_info.current_pointer_frame;
4594 end = drag_info.grab_frame;
4596 end = drag_info.current_pointer_frame;
4597 start = drag_info.grab_frame;
4600 if (drag_info.current_pointer_y < drag_info.grab_y) {
4601 y1 = drag_info.current_pointer_y;
4602 y2 = drag_info.grab_y;
4604 y2 = drag_info.current_pointer_y;
4605 y1 = drag_info.grab_y;
4609 if (start != end || y1 != y2) {
4611 double x1 = frame_to_pixel (start);
4612 double x2 = frame_to_pixel (end);
4614 rubberband_rect->property_x1() = x1;
4615 rubberband_rect->property_y1() = y1;
4616 rubberband_rect->property_x2() = x2;
4617 rubberband_rect->property_y2() = y2;
4619 rubberband_rect->show();
4620 rubberband_rect->raise_to_top();
4622 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4623 drag_info.first_move = false;
4625 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4630 Editor::end_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4632 if (!drag_info.first_move) {
4634 drag_rubberband_select (item, event);
4637 if (drag_info.current_pointer_y < drag_info.grab_y) {
4638 y1 = drag_info.current_pointer_y;
4639 y2 = drag_info.grab_y;
4642 y2 = drag_info.current_pointer_y;
4643 y1 = drag_info.grab_y;
4647 Selection::Operation op = Keyboard::selection_type (event->button.state);
4650 begin_reversible_command (_("rubberband selection"));
4652 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
4653 commit = select_all_within (drag_info.grab_frame, drag_info.last_pointer_frame, y1, y2, track_views, op);
4655 commit = select_all_within (drag_info.last_pointer_frame, drag_info.grab_frame, y1, y2, track_views, op);
4659 commit_reversible_command ();
4663 selection->clear_tracks();
4664 selection->clear_regions();
4665 selection->clear_points ();
4666 selection->clear_lines ();
4669 rubberband_rect->hide();
4674 Editor::mouse_rename_region (ArdourCanvas::Item* item, GdkEvent* event)
4676 using namespace Gtkmm2ext;
4678 ArdourPrompter prompter (false);
4680 prompter.set_prompt (_("Name for region:"));
4681 prompter.set_initial_text (clicked_regionview->region()->name());
4682 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
4683 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
4684 prompter.show_all ();
4685 switch (prompter.run ()) {
4686 case Gtk::RESPONSE_ACCEPT:
4688 prompter.get_result(str);
4690 clicked_regionview->region()->set_name (str);
4698 Editor::start_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
4700 drag_info.item = item;
4701 drag_info.motion_callback = &Editor::time_fx_motion;
4702 drag_info.finished_callback = &Editor::end_time_fx;
4706 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4710 Editor::time_fx_motion (ArdourCanvas::Item *item, GdkEvent* event)
4712 RegionView* rv = clicked_regionview;
4714 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4715 snap_to (drag_info.current_pointer_frame);
4718 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
4722 if (drag_info.current_pointer_frame > rv->region()->position()) {
4723 rv->get_time_axis_view().show_timestretch (rv->region()->position(), drag_info.current_pointer_frame);
4726 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4727 drag_info.first_move = false;
4729 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4733 Editor::end_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
4735 clicked_regionview->get_time_axis_view().hide_timestretch ();
4737 if (drag_info.first_move) {
4741 nframes_t newlen = drag_info.last_pointer_frame - clicked_regionview->region()->position();
4742 float percentage = (float) ((double) newlen - (double) clicked_regionview->region()->length()) / ((double) newlen) * 100.0f;
4744 begin_reversible_command (_("timestretch"));
4746 if (run_timestretch (selection->regions, percentage) == 0) {
4747 session->commit_reversible_command ();
4752 Editor::mouse_brush_insert_region (RegionView* rv, nframes_t pos)
4754 /* no brushing without a useful snap setting */
4757 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
4760 switch (snap_mode) {
4762 return; /* can't work because it allows region to be placed anywhere */
4767 switch (snap_type) {
4770 case SnapToEditCursor:
4777 /* don't brush a copy over the original */
4779 if (pos == rv->region()->position()) {
4783 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&arv->get_time_axis_view());
4785 if (rtv == 0 || !rtv->is_track()) {
4789 boost::shared_ptr<Playlist> playlist = rtv->playlist();
4790 double speed = rtv->get_diskstream()->speed();
4792 XMLNode &before = playlist->get_state();
4793 playlist->add_region (boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (arv->audio_region())), (nframes_t) (pos * speed));
4794 XMLNode &after = playlist->get_state();
4795 session->add_command(new MementoCommand<Playlist>(*playlist.get(), &before, &after));
4797 // playlist is frozen, so we have to update manually
4799 playlist->Modified(); /* EMIT SIGNAL */
4803 Editor::track_height_step_timeout ()
4806 struct timeval delta;
4808 gettimeofday (&now, 0);
4809 timersub (&now, &last_track_height_step_timestamp, &delta);
4811 if (delta.tv_sec * 1000000 + delta.tv_usec > 250000) { /* milliseconds */
4812 current_stepping_trackview = 0;