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 buttons 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 if (mouse_mode == MouseNote)
261 midi_toolbar_frame.show();
263 midi_toolbar_frame.hide();
265 ignore_mouse_mode_toggle = false;
268 track_canvas.get_window()->set_cursor(*current_canvas_cursor);
273 Editor::step_mouse_mode (bool next)
275 switch (current_mouse_mode()) {
277 if (next) set_mouse_mode (MouseRange);
278 else set_mouse_mode (MouseTimeFX);
282 if (next) set_mouse_mode (MouseZoom);
283 else set_mouse_mode (MouseObject);
287 if (next) set_mouse_mode (MouseGain);
288 else set_mouse_mode (MouseRange);
292 if (next) set_mouse_mode (MouseTimeFX);
293 else set_mouse_mode (MouseZoom);
297 if (next) set_mouse_mode (MouseAudition);
298 else set_mouse_mode (MouseGain);
302 if (next) set_mouse_mode (MouseObject);
303 else set_mouse_mode (MouseTimeFX);
307 if (next) set_mouse_mode (MouseObject);
308 else set_mouse_mode (MouseAudition);
314 Editor::midi_edit_mode_toggled (MidiEditMode m)
316 if (ignore_midi_edit_mode_toggle) {
322 if (midi_tool_select_button.get_active()) {
323 set_midi_edit_mode (m);
328 if (midi_tool_pencil_button.get_active()) {
329 set_midi_edit_mode (m);
334 if (midi_tool_erase_button.get_active()) {
335 set_midi_edit_mode (m);
346 Editor::set_midi_edit_mode (MidiEditMode m, bool force)
348 if (drag_info.item) {
352 if (!force && m == midi_edit_mode) {
360 ignore_midi_edit_mode_toggle = true;
362 switch (midi_edit_mode) {
364 midi_tool_select_button.set_active (true);
365 //current_canvas_cursor = selector_cursor;
369 midi_tool_pencil_button.set_active (true);
370 //current_canvas_cursor = selector_cursor;
374 midi_tool_erase_button.set_active (true);
375 //current_canvas_cursor = selector_cursor;
379 if (mouse_mode == MouseNote)
380 midi_toolbar_frame.show();
382 midi_toolbar_frame.hide();
384 ignore_midi_edit_mode_toggle = false;
386 /*if (is_drawable()) {
387 track_canvas.get_window()->set_cursor(*current_canvas_cursor);
392 Editor::button_selection (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
396 /* in object/audition/timefx mode, any button press sets
397 the selection if the object can be selected. this is a
398 bit of hack, because we want to avoid this if the
399 mouse operation is a region alignment.
401 note: not dbl-click or triple-click
404 if (((mouse_mode != MouseObject) &&
405 (mouse_mode != MouseAudition || item_type != RegionItem) &&
406 (mouse_mode != MouseTimeFX || item_type != RegionItem) &&
407 (mouse_mode != MouseRange)) ||
409 (event->type != GDK_BUTTON_PRESS && event->type != GDK_BUTTON_RELEASE || event->button.button > 3)) {
414 if (event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) {
416 if ((event->button.state & Keyboard::RelevantModifierKeyMask) && event->button.button != 1) {
418 /* almost no selection action on modified button-2 or button-3 events */
420 if (item_type != RegionItem && event->button.button != 2) {
426 Selection::Operation op = Keyboard::selection_type (event->button.state);
427 bool press = (event->type == GDK_BUTTON_PRESS);
429 // begin_reversible_command (_("select on click"));
433 case RegionViewNameHighlight:
435 case FadeInHandleItem:
437 case FadeOutHandleItem:
439 if (mouse_mode != MouseRange) {
440 commit = set_selected_regionview_from_click (press, op, true);
441 } else if (event->type == GDK_BUTTON_PRESS) {
442 commit = set_selected_track_from_click (press, op, false);
446 case CrossfadeViewItem:
447 commit = set_selected_track_from_click (press, op, false);
450 case ControlPointItem:
451 commit = set_selected_track_from_click (press, op, true);
452 if (mouse_mode != MouseRange) {
453 commit |= set_selected_control_point_from_click (op, false);
458 /* for context click or range selection, select track */
459 if (event->button.button == 3) {
460 commit = set_selected_track_from_click (press, op, true);
461 } else if (event->type == GDK_BUTTON_PRESS && mouse_mode == MouseRange) {
462 commit = set_selected_track_from_click (press, op, false);
466 case AutomationTrackItem:
467 commit = set_selected_track_from_click (press, op, true);
475 // commit_reversible_command ();
480 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
482 track_canvas.grab_focus();
484 if (session && session->actively_recording()) {
488 button_selection (item, event, item_type);
490 if (drag_info.item == 0 &&
491 (Keyboard::is_delete_event (&event->button) ||
492 Keyboard::is_context_menu_event (&event->button) ||
493 Keyboard::is_edit_event (&event->button))) {
495 /* handled by button release */
499 switch (event->button.button) {
502 if (event->type == GDK_BUTTON_PRESS) {
504 if (drag_info.item) {
505 drag_info.item->ungrab (event->button.time);
508 /* single mouse clicks on any of these item types operate
509 independent of mouse mode, mostly because they are
510 not on the main track canvas or because we want
516 case PlayheadCursorItem:
517 start_cursor_grab (item, event);
521 if (Keyboard::modifier_state_equals (event->button.state,
522 Keyboard::ModifierMask(Keyboard::Control|Keyboard::Shift))) {
523 hide_marker (item, event);
525 start_marker_grab (item, event);
529 case TempoMarkerItem:
530 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
531 start_tempo_marker_copy_grab (item, event);
533 start_tempo_marker_grab (item, event);
537 case MeterMarkerItem:
538 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
539 start_meter_marker_copy_grab (item, event);
541 start_meter_marker_grab (item, event);
551 case RangeMarkerBarItem:
552 start_range_markerbar_op (item, event, CreateRangeMarker);
556 case TransportMarkerBarItem:
557 start_range_markerbar_op (item, event, CreateTransportMarker);
566 switch (mouse_mode) {
569 case StartSelectionTrimItem:
570 start_selection_op (item, event, SelectionStartTrim);
573 case EndSelectionTrimItem:
574 start_selection_op (item, event, SelectionEndTrim);
578 if (Keyboard::modifier_state_contains
579 (event->button.state, Keyboard::ModifierMask(Keyboard::Alt))) {
580 // contains and not equals because I can't use alt as a modifier alone.
581 start_selection_grab (item, event);
582 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
583 /* grab selection for moving */
584 start_selection_op (item, event, SelectionMove);
587 /* this was debated, but decided the more common action was to
588 make a new selection */
589 start_selection_op (item, event, CreateSelection);
594 start_selection_op (item, event, CreateSelection);
600 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::Control|Keyboard::Alt)) &&
601 event->type == GDK_BUTTON_PRESS) {
603 start_rubberband_select (item, event);
605 } else if (event->type == GDK_BUTTON_PRESS) {
608 case FadeInHandleItem:
609 start_fade_in_grab (item, event);
612 case FadeOutHandleItem:
613 start_fade_out_grab (item, event);
617 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
618 start_region_copy_grab (item, event);
619 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
620 start_region_brush_grab (item, event);
622 start_region_grab (item, event);
626 case RegionViewNameHighlight:
627 start_trim (item, event);
632 /* rename happens on edit clicks */
633 start_trim (clicked_regionview->get_name_highlight(), event);
637 case ControlPointItem:
638 start_control_point_grab (item, event);
642 case AutomationLineItem:
643 start_line_grab_from_line (item, event);
648 case AutomationTrackItem:
649 start_rubberband_select (item, event);
653 case ImageFrameHandleStartItem:
654 imageframe_start_handle_op(item, event) ;
657 case ImageFrameHandleEndItem:
658 imageframe_end_handle_op(item, event) ;
661 case MarkerViewHandleStartItem:
662 markerview_item_start_handle_op(item, event) ;
665 case MarkerViewHandleEndItem:
666 markerview_item_end_handle_op(item, event) ;
670 start_markerview_grab(item, event) ;
673 start_imageframe_grab(item, event) ;
691 // start_line_grab_from_regionview (item, event);
695 start_line_grab_from_line (item, event);
698 case ControlPointItem:
699 start_control_point_grab (item, event);
710 case ControlPointItem:
711 start_control_point_grab (item, event);
714 case AutomationLineItem:
715 start_line_grab_from_line (item, event);
719 // XXX need automation mode to identify which
721 // start_line_grab_from_regionview (item, event);
731 if (event->type == GDK_BUTTON_PRESS) {
732 start_mouse_zoom (item, event);
739 if (item_type == RegionItem) {
740 start_time_fx (item, event);
745 /* handled in release */
754 switch (mouse_mode) {
756 if (event->type == GDK_BUTTON_PRESS) {
759 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
760 start_region_copy_grab (item, event);
762 start_region_grab (item, event);
766 case ControlPointItem:
767 start_control_point_grab (item, event);
778 case RegionViewNameHighlight:
779 start_trim (item, event);
784 start_trim (clicked_regionview->get_name_highlight(), event);
795 if (event->type == GDK_BUTTON_PRESS) {
796 /* relax till release */
803 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
804 temporal_zoom_session();
806 temporal_zoom_to_frame (true, event_frame(event));
829 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
831 nframes_t where = event_frame (event, 0, 0);
833 /* no action if we're recording */
835 if (session && session->actively_recording()) {
839 /* first, see if we're finishing a drag ... */
841 if (drag_info.item) {
842 if (end_grab (item, event)) {
843 /* grab dragged, so do nothing else */
848 button_selection (item, event, item_type);
850 /* edit events get handled here */
852 if (drag_info.item == 0 && Keyboard::is_edit_event (&event->button)) {
858 case TempoMarkerItem:
859 edit_tempo_marker (item);
862 case MeterMarkerItem:
863 edit_meter_marker (item);
867 if (clicked_regionview->name_active()) {
868 return mouse_rename_region (item, event);
878 /* context menu events get handled here */
880 if (Keyboard::is_context_menu_event (&event->button)) {
882 if (drag_info.item == 0) {
884 /* no matter which button pops up the context menu, tell the menu
885 widget to use button 1 to drive menu selection.
890 case FadeInHandleItem:
892 case FadeOutHandleItem:
893 popup_fade_context_menu (1, event->button.time, item, item_type);
898 case RegionViewNameHighlight:
901 case AutomationTrackItem:
902 case CrossfadeViewItem:
903 popup_track_context_menu (1, event->button.time, where);
907 case RangeMarkerBarItem:
908 case TransportMarkerBarItem:
911 popup_ruler_menu (pixel_to_frame(event->button.x), item_type);
915 marker_context_menu (&event->button, item);
918 case TempoMarkerItem:
919 tm_marker_context_menu (&event->button, item);
922 case MeterMarkerItem:
923 tm_marker_context_menu (&event->button, item);
928 popup_imageframe_edit_menu(1, event->button.time, item, true) ;
930 case ImageFrameTimeAxisItem:
931 popup_imageframe_edit_menu(1, event->button.time, item, false) ;
934 popup_marker_time_axis_edit_menu(1, event->button.time, item, true) ;
936 case MarkerTimeAxisItem:
937 popup_marker_time_axis_edit_menu(1, event->button.time, item, false) ;
949 /* delete events get handled here */
951 if (drag_info.item == 0 && Keyboard::is_delete_event (&event->button)) {
954 case TempoMarkerItem:
955 remove_tempo_marker (item);
958 case MeterMarkerItem:
959 remove_meter_marker (item);
963 remove_marker (*item, event);
967 if (mouse_mode == MouseObject) {
968 remove_clicked_region ();
972 case ControlPointItem:
973 if (mouse_mode == MouseGain) {
974 remove_gain_control_point (item, event);
976 remove_control_point (item, event);
986 switch (event->button.button) {
990 /* see comments in button_press_handler */
992 case PlayheadCursorItem:
995 case AutomationLineItem:
996 case StartSelectionTrimItem:
997 case EndSelectionTrimItem:
1001 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1002 snap_to (where, 0, true);
1004 mouse_add_new_marker (where);
1008 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1011 mouse_add_new_tempo_event (where);
1015 mouse_add_new_meter_event (pixel_to_frame (event->button.x));
1023 switch (mouse_mode) {
1025 switch (item_type) {
1026 case AutomationTrackItem:
1027 dynamic_cast<AutomationTimeAxisView*>(clicked_axisview)->add_automation_event
1041 // Gain only makes sense for audio regions
1043 if (!dynamic_cast<AudioRegionView*>(clicked_regionview)) {
1047 switch (item_type) {
1049 dynamic_cast<AudioRegionView*>(clicked_regionview)->add_gain_point_event (item, event);
1053 case AutomationTrackItem:
1054 dynamic_cast<AutomationTimeAxisView*>(clicked_axisview)->
1055 add_automation_event (item, event, where, event->button.y);
1064 switch (item_type) {
1066 audition_selected_region ();
1083 switch (mouse_mode) {
1087 // x_style_paste (where, 1.0);
1107 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1113 switch (item_type) {
1114 case ControlPointItem:
1115 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1116 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1117 cp->set_visible (true);
1121 at_y = cp->get_y ();
1122 cp->item()->i2w (at_x, at_y);
1126 fraction = 1.0 - (cp->get_y() / cp->line().height());
1128 set_verbose_canvas_cursor (cp->line().get_verbose_cursor_string (fraction), at_x, at_y);
1129 show_verbose_canvas_cursor ();
1131 if (is_drawable()) {
1132 track_canvas.get_window()->set_cursor (*fader_cursor);
1138 if (mouse_mode == MouseGain) {
1139 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1141 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredGainLine.get();
1142 if (is_drawable()) {
1143 track_canvas.get_window()->set_cursor (*fader_cursor);
1148 case AutomationLineItem:
1149 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1151 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1153 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredAutomationLine.get();
1155 if (is_drawable()) {
1156 track_canvas.get_window()->set_cursor (*fader_cursor);
1161 case RegionViewNameHighlight:
1162 if (is_drawable() && mouse_mode == MouseObject) {
1163 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1167 case StartSelectionTrimItem:
1168 case EndSelectionTrimItem:
1171 case ImageFrameHandleStartItem:
1172 case ImageFrameHandleEndItem:
1173 case MarkerViewHandleStartItem:
1174 case MarkerViewHandleEndItem:
1177 if (is_drawable()) {
1178 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1182 case EditCursorItem:
1183 case PlayheadCursorItem:
1184 if (is_drawable()) {
1185 track_canvas.get_window()->set_cursor (*grabber_cursor);
1189 case RegionViewName:
1191 /* when the name is not an active item, the entire name highlight is for trimming */
1193 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1194 if (mouse_mode == MouseObject && is_drawable()) {
1195 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1201 case AutomationTrackItem:
1202 if (is_drawable()) {
1203 Gdk::Cursor *cursor;
1204 switch (mouse_mode) {
1206 cursor = selector_cursor;
1209 cursor = zoom_cursor;
1212 cursor = cross_hair_cursor;
1216 track_canvas.get_window()->set_cursor (*cursor);
1218 AutomationTimeAxisView* atv;
1219 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1220 clear_entered_track = false;
1221 set_entered_track (atv);
1227 case RangeMarkerBarItem:
1228 case TransportMarkerBarItem:
1231 if (is_drawable()) {
1232 time_canvas.get_window()->set_cursor (*timebar_cursor);
1237 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1240 marker->set_color_rgba (ARDOUR_UI::config()->canvasvar_EnteredMarker.get());
1242 case MeterMarkerItem:
1243 case TempoMarkerItem:
1244 if (is_drawable()) {
1245 time_canvas.get_window()->set_cursor (*timebar_cursor);
1248 case FadeInHandleItem:
1249 case FadeOutHandleItem:
1250 if (mouse_mode == MouseObject) {
1251 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1253 rect->property_fill_color_rgba() = 0;
1254 rect->property_outline_pixels() = 1;
1263 /* second pass to handle entered track status in a comprehensible way.
1266 switch (item_type) {
1268 case AutomationLineItem:
1269 case ControlPointItem:
1270 /* these do not affect the current entered track state */
1271 clear_entered_track = false;
1274 case AutomationTrackItem:
1275 /* handled above already */
1279 set_entered_track (0);
1287 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1296 switch (item_type) {
1297 case ControlPointItem:
1298 cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
1299 if (cp->line().the_list()->interpolation() != AutomationList::Discrete) {
1300 if (cp->line().npoints() > 1 && !cp->selected()) {
1301 cp->set_visible (false);
1305 if (is_drawable()) {
1306 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1309 hide_verbose_canvas_cursor ();
1312 case RegionViewNameHighlight:
1313 case StartSelectionTrimItem:
1314 case EndSelectionTrimItem:
1315 case EditCursorItem:
1316 case PlayheadCursorItem:
1319 case ImageFrameHandleStartItem:
1320 case ImageFrameHandleEndItem:
1321 case MarkerViewHandleStartItem:
1322 case MarkerViewHandleEndItem:
1325 if (is_drawable()) {
1326 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1331 case AutomationLineItem:
1332 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1334 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1336 line->property_fill_color_rgba() = al->get_line_color();
1338 if (is_drawable()) {
1339 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1343 case RegionViewName:
1344 /* see enter_handler() for notes */
1345 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1346 if (is_drawable() && mouse_mode == MouseObject) {
1347 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1352 case RangeMarkerBarItem:
1353 case TransportMarkerBarItem:
1357 if (is_drawable()) {
1358 time_canvas.get_window()->set_cursor (*timebar_cursor);
1363 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1366 loc = find_location_from_marker (marker, is_start);
1367 if (loc) location_flags_changed (loc, this);
1369 case MeterMarkerItem:
1370 case TempoMarkerItem:
1372 if (is_drawable()) {
1373 time_canvas.get_window()->set_cursor (*timebar_cursor);
1378 case FadeInHandleItem:
1379 case FadeOutHandleItem:
1380 rv = static_cast<RegionView*>(item->get_data ("regionview"));
1382 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1384 rect->property_fill_color_rgba() = rv->get_fill_color();
1385 rect->property_outline_pixels() = 0;
1390 case AutomationTrackItem:
1391 if (is_drawable()) {
1392 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1393 clear_entered_track = true;
1394 Glib::signal_idle().connect (mem_fun(*this, &Editor::left_automation_track));
1406 Editor::left_automation_track ()
1408 if (clear_entered_track) {
1409 set_entered_track (0);
1410 clear_entered_track = false;
1416 Editor::motion_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type, bool from_autoscroll)
1420 /* We call this so that MOTION_NOTIFY events continue to be
1421 delivered to the canvas. We need to do this because we set
1422 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
1423 the density of the events, at the expense of a round-trip
1424 to the server. Given that this will mostly occur on cases
1425 where DISPLAY = :0.0, and given the cost of what the motion
1426 event might do, its a good tradeoff.
1429 track_canvas.get_pointer (x, y);
1431 if (current_stepping_trackview) {
1432 /* don't keep the persistent stepped trackview if the mouse moves */
1433 current_stepping_trackview = 0;
1434 step_timeout.disconnect ();
1437 if (session && session->actively_recording()) {
1438 /* Sorry. no dragging stuff around while we record */
1442 drag_info.item_type = item_type;
1443 drag_info.current_pointer_frame = event_frame (event, &drag_info.current_pointer_x,
1444 &drag_info.current_pointer_y);
1446 if (!from_autoscroll && drag_info.item) {
1447 /* item != 0 is the best test i can think of for dragging.
1449 if (!drag_info.move_threshold_passed) {
1451 bool x_threshold_passed = (abs ((nframes64_t) (drag_info.current_pointer_x - drag_info.grab_x)) > 4LL);
1452 bool y_threshold_passed = (abs ((nframes64_t) (drag_info.current_pointer_y - drag_info.grab_y)) > 4LL);
1454 drag_info.move_threshold_passed = (x_threshold_passed || y_threshold_passed);
1456 // and change the initial grab loc/frame if this drag info wants us to
1458 if (drag_info.want_move_threshold && drag_info.move_threshold_passed) {
1459 drag_info.grab_frame = drag_info.current_pointer_frame;
1460 drag_info.grab_x = drag_info.current_pointer_x;
1461 drag_info.grab_y = drag_info.current_pointer_y;
1462 drag_info.last_pointer_frame = drag_info.grab_frame;
1463 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
1468 switch (item_type) {
1469 case PlayheadCursorItem:
1470 case EditCursorItem:
1472 case ControlPointItem:
1473 case TempoMarkerItem:
1474 case MeterMarkerItem:
1475 case RegionViewNameHighlight:
1476 case StartSelectionTrimItem:
1477 case EndSelectionTrimItem:
1480 case AutomationLineItem:
1481 case FadeInHandleItem:
1482 case FadeOutHandleItem:
1485 case ImageFrameHandleStartItem:
1486 case ImageFrameHandleEndItem:
1487 case MarkerViewHandleStartItem:
1488 case MarkerViewHandleEndItem:
1491 if (drag_info.item && (event->motion.state & Gdk::BUTTON1_MASK ||
1492 (event->motion.state & Gdk::BUTTON2_MASK))) {
1493 if (!from_autoscroll) {
1494 maybe_autoscroll (event);
1496 (this->*(drag_info.motion_callback)) (item, event);
1505 switch (mouse_mode) {
1510 if (drag_info.item && (event->motion.state & GDK_BUTTON1_MASK ||
1511 (event->motion.state & GDK_BUTTON2_MASK))) {
1512 if (!from_autoscroll) {
1513 maybe_autoscroll (event);
1515 (this->*(drag_info.motion_callback)) (item, event);
1526 track_canvas_motion (event);
1527 // drag_info.last_pointer_frame = drag_info.current_pointer_frame;
1535 Editor::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
1537 if (drag_info.item == 0) {
1538 fatal << _("programming error: start_grab called without drag item") << endmsg;
1544 cursor = grabber_cursor;
1547 // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
1549 if (event->button.button == 2) {
1550 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Alt)) {
1551 drag_info.y_constrained = true;
1552 drag_info.x_constrained = false;
1554 drag_info.y_constrained = false;
1555 drag_info.x_constrained = true;
1558 drag_info.x_constrained = false;
1559 drag_info.y_constrained = false;
1562 drag_info.grab_frame = event_frame (event, &drag_info.grab_x, &drag_info.grab_y);
1563 drag_info.last_pointer_frame = drag_info.grab_frame;
1564 drag_info.current_pointer_frame = drag_info.grab_frame;
1565 drag_info.current_pointer_x = drag_info.grab_x;
1566 drag_info.current_pointer_y = drag_info.grab_y;
1567 drag_info.cumulative_x_drag = 0;
1568 drag_info.cumulative_y_drag = 0;
1569 drag_info.first_move = true;
1570 drag_info.move_threshold_passed = false;
1571 drag_info.want_move_threshold = false;
1572 drag_info.pointer_frame_offset = 0;
1573 drag_info.brushing = false;
1574 drag_info.copied_location = 0;
1576 drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK,
1578 event->button.time);
1580 if (session && session->transport_rolling()) {
1581 drag_info.was_rolling = true;
1583 drag_info.was_rolling = false;
1586 switch (snap_type) {
1587 case SnapToRegionStart:
1588 case SnapToRegionEnd:
1589 case SnapToRegionSync:
1590 case SnapToRegionBoundary:
1591 build_region_boundary_cache ();
1599 Editor::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t time)
1601 drag_info.item->ungrab (0);
1602 drag_info.item = new_item;
1605 cursor = grabber_cursor;
1608 drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK, *cursor, time);
1612 Editor::end_grab (ArdourCanvas::Item* item, GdkEvent* event)
1614 bool did_drag = false;
1616 stop_canvas_autoscroll ();
1618 if (drag_info.item == 0) {
1622 drag_info.item->ungrab (event->button.time);
1624 if (drag_info.finished_callback) {
1625 (this->*(drag_info.finished_callback)) (item, event);
1628 did_drag = !drag_info.first_move;
1630 hide_verbose_canvas_cursor();
1633 drag_info.copy = false;
1634 drag_info.motion_callback = 0;
1635 drag_info.finished_callback = 0;
1636 drag_info.last_trackview = 0;
1637 drag_info.last_frame_position = 0;
1638 drag_info.grab_frame = 0;
1639 drag_info.last_pointer_frame = 0;
1640 drag_info.current_pointer_frame = 0;
1641 drag_info.brushing = false;
1643 if (drag_info.copied_location) {
1644 delete drag_info.copied_location;
1645 drag_info.copied_location = 0;
1652 Editor::set_edit_cursor (GdkEvent* event)
1654 nframes_t pointer_frame = event_frame (event);
1656 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1657 if (snap_type != SnapToEditCursor) {
1658 snap_to (pointer_frame);
1662 edit_cursor->set_position (pointer_frame);
1663 edit_cursor_clock.set (pointer_frame);
1667 Editor::set_playhead_cursor (GdkEvent* event)
1669 nframes_t pointer_frame = event_frame (event);
1671 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1672 snap_to (pointer_frame);
1676 session->request_locate (pointer_frame, session->transport_rolling());
1681 Editor::start_fade_in_grab (ArdourCanvas::Item* item, GdkEvent* event)
1683 drag_info.item = item;
1684 drag_info.motion_callback = &Editor::fade_in_drag_motion_callback;
1685 drag_info.finished_callback = &Editor::fade_in_drag_finished_callback;
1689 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
1690 fatal << _("programming error: fade in canvas item has no regionview data pointer!") << endmsg;
1694 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1696 drag_info.pointer_frame_offset = drag_info.grab_frame - ((nframes_t) arv->audio_region()->fade_in()->back()->when + arv->region()->position());
1700 Editor::fade_in_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1702 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1704 nframes_t fade_length;
1706 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1707 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1713 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1717 if (pos < (arv->region()->position() + 64)) {
1718 fade_length = 64; // this should be a minimum defined somewhere
1719 } else if (pos > arv->region()->last_frame()) {
1720 fade_length = arv->region()->length();
1722 fade_length = pos - arv->region()->position();
1724 /* mapover the region selection */
1726 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1728 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1734 tmp->reset_fade_in_shape_width (fade_length);
1737 show_verbose_duration_cursor (arv->region()->position(), arv->region()->position() + fade_length, 10);
1739 drag_info.first_move = false;
1743 Editor::fade_in_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1745 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1747 nframes_t fade_length;
1749 if (drag_info.first_move) return;
1751 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1752 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1757 if (pos < (arv->region()->position() + 64)) {
1758 fade_length = 64; // this should be a minimum defined somewhere
1759 } else if (pos > arv->region()->last_frame()) {
1760 fade_length = arv->region()->length();
1762 fade_length = pos - arv->region()->position();
1765 begin_reversible_command (_("change fade in length"));
1767 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1769 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1775 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
1776 XMLNode &before = alist->get_state();
1778 tmp->audio_region()->set_fade_in_length (fade_length);
1780 XMLNode &after = alist->get_state();
1781 session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
1784 commit_reversible_command ();
1788 Editor::start_fade_out_grab (ArdourCanvas::Item* item, GdkEvent* event)
1790 drag_info.item = item;
1791 drag_info.motion_callback = &Editor::fade_out_drag_motion_callback;
1792 drag_info.finished_callback = &Editor::fade_out_drag_finished_callback;
1796 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
1797 fatal << _("programming error: fade out canvas item has no regionview data pointer!") << endmsg;
1801 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1803 drag_info.pointer_frame_offset = drag_info.grab_frame - (arv->region()->length() - (nframes_t) arv->audio_region()->fade_out()->back()->when + arv->region()->position());
1807 Editor::fade_out_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1809 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1811 nframes_t fade_length;
1813 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1814 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1819 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1823 if (pos > (arv->region()->last_frame() - 64)) {
1824 fade_length = 64; // this should really be a minimum fade defined somewhere
1826 else if (pos < arv->region()->position()) {
1827 fade_length = arv->region()->length();
1830 fade_length = arv->region()->last_frame() - pos;
1833 /* mapover the region selection */
1835 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1837 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1843 tmp->reset_fade_out_shape_width (fade_length);
1846 show_verbose_duration_cursor (arv->region()->last_frame() - fade_length, arv->region()->last_frame(), 10);
1848 drag_info.first_move = false;
1852 Editor::fade_out_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1854 if (drag_info.first_move) return;
1856 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1858 nframes_t fade_length;
1860 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1861 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1867 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1871 if (pos > (arv->region()->last_frame() - 64)) {
1872 fade_length = 64; // this should really be a minimum fade defined somewhere
1874 else if (pos < arv->region()->position()) {
1875 fade_length = arv->region()->length();
1878 fade_length = arv->region()->last_frame() - pos;
1881 begin_reversible_command (_("change fade out length"));
1883 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1885 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1891 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
1892 XMLNode &before = alist->get_state();
1894 tmp->audio_region()->set_fade_out_length (fade_length);
1896 XMLNode &after = alist->get_state();
1897 session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
1900 commit_reversible_command ();
1904 Editor::start_cursor_grab (ArdourCanvas::Item* item, GdkEvent* event)
1906 drag_info.item = item;
1907 drag_info.motion_callback = &Editor::cursor_drag_motion_callback;
1908 drag_info.finished_callback = &Editor::cursor_drag_finished_callback;
1912 if ((drag_info.data = (item->get_data ("cursor"))) == 0) {
1913 fatal << _("programming error: cursor canvas item has no cursor data pointer!") << endmsg;
1917 Cursor* cursor = (Cursor *) drag_info.data;
1919 if (cursor == playhead_cursor) {
1920 _dragging_playhead = true;
1922 if (session && drag_info.was_rolling) {
1923 session->request_stop ();
1926 if (session && session->is_auditioning()) {
1927 session->cancel_audition ();
1931 drag_info.pointer_frame_offset = drag_info.grab_frame - cursor->current_frame;
1933 show_verbose_time_cursor (cursor->current_frame, 10);
1937 Editor::cursor_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1939 Cursor* cursor = (Cursor *) drag_info.data;
1940 nframes_t adjusted_frame;
1942 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1943 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1949 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1950 if (cursor != edit_cursor || snap_type != SnapToEditCursor) {
1951 snap_to (adjusted_frame);
1955 if (adjusted_frame == drag_info.last_pointer_frame) return;
1957 cursor->set_position (adjusted_frame);
1959 if (cursor == edit_cursor) {
1960 edit_cursor_clock.set (cursor->current_frame);
1962 UpdateAllTransportClocks (cursor->current_frame);
1965 show_verbose_time_cursor (cursor->current_frame, 10);
1967 drag_info.last_pointer_frame = adjusted_frame;
1968 drag_info.first_move = false;
1972 Editor::cursor_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1974 if (drag_info.first_move) return;
1976 cursor_drag_motion_callback (item, event);
1978 _dragging_playhead = false;
1980 if (item == &playhead_cursor->canvas_item) {
1982 session->request_locate (playhead_cursor->current_frame, drag_info.was_rolling);
1984 } else if (item == &edit_cursor->canvas_item) {
1985 edit_cursor->set_position (edit_cursor->current_frame);
1986 edit_cursor_clock.set (edit_cursor->current_frame);
1991 Editor::update_marker_drag_item (Location *location)
1993 double x1 = frame_to_pixel (location->start());
1994 double x2 = frame_to_pixel (location->end());
1996 if (location->is_mark()) {
1997 marker_drag_line_points.front().set_x(x1);
1998 marker_drag_line_points.back().set_x(x1);
1999 marker_drag_line->property_points() = marker_drag_line_points;
2002 range_marker_drag_rect->property_x1() = x1;
2003 range_marker_drag_rect->property_x2() = x2;
2008 Editor::start_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2012 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2013 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2019 Location *location = find_location_from_marker (marker, is_start);
2021 drag_info.item = item;
2022 drag_info.data = marker;
2023 drag_info.motion_callback = &Editor::marker_drag_motion_callback;
2024 drag_info.finished_callback = &Editor::marker_drag_finished_callback;
2028 drag_info.copied_location = new Location (*location);
2029 drag_info.pointer_frame_offset = drag_info.grab_frame - (is_start ? location->start() : location->end());
2031 update_marker_drag_item (location);
2033 if (location->is_mark()) {
2034 marker_drag_line->show();
2035 marker_drag_line->raise_to_top();
2038 range_marker_drag_rect->show();
2039 range_marker_drag_rect->raise_to_top();
2042 if (is_start) show_verbose_time_cursor (location->start(), 10);
2043 else show_verbose_time_cursor (location->end(), 10);
2047 Editor::marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2050 Marker* marker = (Marker *) drag_info.data;
2051 Location *real_location;
2052 Location *copy_location;
2054 bool move_both = false;
2058 if (drag_info.pointer_frame_offset <= drag_info.current_pointer_frame) {
2059 newframe = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2064 nframes_t next = newframe;
2066 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2067 snap_to (newframe, 0, true);
2070 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
2074 /* call this to find out if its the start or end */
2076 real_location = find_location_from_marker (marker, is_start);
2078 /* use the copy that we're "dragging" around */
2080 copy_location = drag_info.copied_location;
2082 f_delta = copy_location->end() - copy_location->start();
2084 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
2088 if (copy_location->is_mark()) {
2091 copy_location->set_start (newframe);
2095 if (is_start) { // start-of-range marker
2098 copy_location->set_start (newframe);
2099 copy_location->set_end (newframe + f_delta);
2100 } else if (newframe < copy_location->end()) {
2101 copy_location->set_start (newframe);
2103 snap_to (next, 1, true);
2104 copy_location->set_end (next);
2105 copy_location->set_start (newframe);
2108 } else { // end marker
2111 copy_location->set_end (newframe);
2112 copy_location->set_start (newframe - f_delta);
2113 } else if (newframe > copy_location->start()) {
2114 copy_location->set_end (newframe);
2116 } else if (newframe > 0) {
2117 snap_to (next, -1, true);
2118 copy_location->set_start (next);
2119 copy_location->set_end (newframe);
2124 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
2125 drag_info.first_move = false;
2127 update_marker_drag_item (copy_location);
2129 LocationMarkers* lm = find_location_markers (real_location);
2130 lm->set_position (copy_location->start(), copy_location->end());
2132 show_verbose_time_cursor (newframe, 10);
2136 Editor::marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2138 if (drag_info.first_move) {
2139 marker_drag_motion_callback (item, event);
2143 Marker* marker = (Marker *) drag_info.data;
2147 begin_reversible_command ( _("move marker") );
2148 XMLNode &before = session->locations()->get_state();
2150 Location * location = find_location_from_marker (marker, is_start);
2153 if (location->is_mark()) {
2154 location->set_start (drag_info.copied_location->start());
2156 location->set (drag_info.copied_location->start(), drag_info.copied_location->end());
2160 XMLNode &after = session->locations()->get_state();
2161 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
2162 commit_reversible_command ();
2164 marker_drag_line->hide();
2165 range_marker_drag_rect->hide();
2169 Editor::start_meter_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2172 MeterMarker* meter_marker;
2174 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2175 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2179 meter_marker = dynamic_cast<MeterMarker*> (marker);
2181 MetricSection& section (meter_marker->meter());
2183 if (!section.movable()) {
2187 drag_info.item = item;
2188 drag_info.data = marker;
2189 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2190 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2194 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2196 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2200 Editor::start_meter_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2203 MeterMarker* meter_marker;
2205 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2206 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2210 meter_marker = dynamic_cast<MeterMarker*> (marker);
2212 // create a dummy marker for visual representation of moving the copy.
2213 // The actual copying is not done before we reach the finish callback.
2215 snprintf (name, sizeof(name), "%g/%g", meter_marker->meter().beats_per_bar(), meter_marker->meter().note_divisor ());
2216 MeterMarker* new_marker = new MeterMarker(*this, *meter_group, ARDOUR_UI::config()->canvasvar_MeterMarker.get(), name,
2217 *new MeterSection(meter_marker->meter()));
2219 drag_info.item = &new_marker->the_item();
2220 drag_info.copy = true;
2221 drag_info.data = new_marker;
2222 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2223 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2227 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2229 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2233 Editor::meter_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2235 MeterMarker* marker = (MeterMarker *) drag_info.data;
2236 nframes_t adjusted_frame;
2238 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2239 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2245 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2246 snap_to (adjusted_frame);
2249 if (adjusted_frame == drag_info.last_pointer_frame) return;
2251 marker->set_position (adjusted_frame);
2254 drag_info.last_pointer_frame = adjusted_frame;
2255 drag_info.first_move = false;
2257 show_verbose_time_cursor (adjusted_frame, 10);
2261 Editor::meter_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2263 if (drag_info.first_move) return;
2265 meter_marker_drag_motion_callback (drag_info.item, event);
2267 MeterMarker* marker = (MeterMarker *) drag_info.data;
2270 TempoMap& map (session->tempo_map());
2271 map.bbt_time (drag_info.last_pointer_frame, when);
2273 if (drag_info.copy == true) {
2274 begin_reversible_command (_("copy meter mark"));
2275 XMLNode &before = map.get_state();
2276 map.add_meter (marker->meter(), when);
2277 XMLNode &after = map.get_state();
2278 session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2279 commit_reversible_command ();
2281 // delete the dummy marker we used for visual representation of copying.
2282 // a new visual marker will show up automatically.
2285 begin_reversible_command (_("move meter mark"));
2286 XMLNode &before = map.get_state();
2287 map.move_meter (marker->meter(), when);
2288 XMLNode &after = map.get_state();
2289 session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2290 commit_reversible_command ();
2295 Editor::start_tempo_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2298 TempoMarker* tempo_marker;
2300 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2301 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2305 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2306 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2310 MetricSection& section (tempo_marker->tempo());
2312 if (!section.movable()) {
2316 drag_info.item = item;
2317 drag_info.data = marker;
2318 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2319 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2323 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2324 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2328 Editor::start_tempo_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2331 TempoMarker* tempo_marker;
2333 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2334 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2338 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2339 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2343 // create a dummy marker for visual representation of moving the copy.
2344 // The actual copying is not done before we reach the finish callback.
2346 snprintf (name, sizeof (name), "%.2f", tempo_marker->tempo().beats_per_minute());
2347 TempoMarker* new_marker = new TempoMarker(*this, *tempo_group, ARDOUR_UI::config()->canvasvar_TempoMarker.get(), name,
2348 *new TempoSection(tempo_marker->tempo()));
2350 drag_info.item = &new_marker->the_item();
2351 drag_info.copy = true;
2352 drag_info.data = new_marker;
2353 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2354 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2358 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2360 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2364 Editor::tempo_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2366 TempoMarker* marker = (TempoMarker *) drag_info.data;
2367 nframes_t adjusted_frame;
2369 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2370 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2376 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2377 snap_to (adjusted_frame);
2380 if (adjusted_frame == drag_info.last_pointer_frame) return;
2382 /* OK, we've moved far enough to make it worth actually move the thing. */
2384 marker->set_position (adjusted_frame);
2386 show_verbose_time_cursor (adjusted_frame, 10);
2388 drag_info.last_pointer_frame = adjusted_frame;
2389 drag_info.first_move = false;
2393 Editor::tempo_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2395 if (drag_info.first_move) return;
2397 tempo_marker_drag_motion_callback (drag_info.item, event);
2399 TempoMarker* marker = (TempoMarker *) drag_info.data;
2402 TempoMap& map (session->tempo_map());
2403 map.bbt_time (drag_info.last_pointer_frame, when);
2405 if (drag_info.copy == true) {
2406 begin_reversible_command (_("copy tempo mark"));
2407 XMLNode &before = map.get_state();
2408 map.add_tempo (marker->tempo(), when);
2409 XMLNode &after = map.get_state();
2410 session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2411 commit_reversible_command ();
2413 // delete the dummy marker we used for visual representation of copying.
2414 // a new visual marker will show up automatically.
2417 begin_reversible_command (_("move tempo mark"));
2418 XMLNode &before = map.get_state();
2419 map.move_tempo (marker->tempo(), when);
2420 XMLNode &after = map.get_state();
2421 session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2422 commit_reversible_command ();
2427 Editor::remove_gain_control_point (ArdourCanvas::Item*item, GdkEvent* event)
2429 ControlPoint* control_point;
2431 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2432 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2436 // We shouldn't remove the first or last gain point
2437 if (control_point->line().is_last_point(*control_point) ||
2438 control_point->line().is_first_point(*control_point)) {
2442 control_point->line().remove_point (*control_point);
2446 Editor::remove_control_point (ArdourCanvas::Item* item, GdkEvent* event)
2448 ControlPoint* control_point;
2450 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2451 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2455 control_point->line().remove_point (*control_point);
2459 Editor::start_control_point_grab (ArdourCanvas::Item* item, GdkEvent* event)
2461 ControlPoint* control_point;
2463 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2464 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2468 drag_info.item = item;
2469 drag_info.data = control_point;
2470 drag_info.motion_callback = &Editor::control_point_drag_motion_callback;
2471 drag_info.finished_callback = &Editor::control_point_drag_finished_callback;
2473 start_grab (event, fader_cursor);
2475 control_point->line().start_drag (control_point, drag_info.grab_frame, 0);
2477 double fraction = 1.0 - ((control_point->get_y() - control_point->line().y_position()) / (double)control_point->line().height());
2478 set_verbose_canvas_cursor (control_point->line().get_verbose_cursor_string (fraction),
2479 drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
2481 show_verbose_canvas_cursor ();
2485 Editor::control_point_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2487 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2489 double cx = drag_info.current_pointer_x;
2490 double cy = drag_info.current_pointer_y;
2492 drag_info.cumulative_x_drag = cx - drag_info.grab_x ;
2493 drag_info.cumulative_y_drag = cy - drag_info.grab_y ;
2495 if (drag_info.x_constrained) {
2496 cx = drag_info.grab_x;
2498 if (drag_info.y_constrained) {
2499 cy = drag_info.grab_y;
2502 cp->line().parent_group().w2i (cx, cy);
2506 cy = min ((double) (cp->line().y_position() + cp->line().height()), cy);
2508 //translate cx to frames
2509 nframes_t cx_frames = unit_to_frame (cx);
2511 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier()) && !drag_info.x_constrained) {
2512 snap_to (cx_frames);
2515 const double fraction = 1.0 - ((cy - cp->line().y_position()) / (double)cp->line().height());
2519 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
2525 cp->line().point_drag (*cp, cx_frames , fraction, push);
2527 set_verbose_canvas_cursor_text (cp->line().get_verbose_cursor_string (fraction));
2529 drag_info.first_move = false;
2533 Editor::control_point_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2535 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2537 if (drag_info.first_move) {
2541 if ((event->type == GDK_BUTTON_RELEASE) && (event->button.button == 1) && Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
2542 reset_point_selection ();
2546 control_point_drag_motion_callback (item, event);
2548 cp->line().end_drag (cp);
2552 Editor::start_line_grab_from_regionview (ArdourCanvas::Item* item, GdkEvent* event)
2554 switch (mouse_mode) {
2556 assert(dynamic_cast<AudioRegionView*>(clicked_regionview));
2557 start_line_grab (dynamic_cast<AudioRegionView*>(clicked_regionview)->get_gain_line(), event);
2565 Editor::start_line_grab_from_line (ArdourCanvas::Item* item, GdkEvent* event)
2569 if ((al = reinterpret_cast<AutomationLine*> (item->get_data ("line"))) == 0) {
2570 fatal << _("programming error: line canvas item has no line pointer!") << endmsg;
2574 start_line_grab (al, event);
2578 Editor::start_line_grab (AutomationLine* line, GdkEvent* event)
2582 nframes_t frame_within_region;
2584 /* need to get x coordinate in terms of parent (TimeAxisItemView)
2588 cx = event->button.x;
2589 cy = event->button.y;
2590 line->parent_group().w2i (cx, cy);
2591 frame_within_region = (nframes_t) floor (cx * frames_per_unit);
2593 if (!line->control_points_adjacent (frame_within_region, current_line_drag_info.before,
2594 current_line_drag_info.after)) {
2595 /* no adjacent points */
2599 drag_info.item = &line->grab_item();
2600 drag_info.data = line;
2601 drag_info.motion_callback = &Editor::line_drag_motion_callback;
2602 drag_info.finished_callback = &Editor::line_drag_finished_callback;
2604 start_grab (event, fader_cursor);
2606 const double fraction = 1.0 - ((cy - line->y_position()) / (double)line->height());
2608 line->start_drag (0, drag_info.grab_frame, fraction);
2610 set_verbose_canvas_cursor (line->get_verbose_cursor_string (fraction),
2611 drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
2612 show_verbose_canvas_cursor ();
2616 Editor::line_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2618 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
2619 double cx = drag_info.current_pointer_x;
2620 double cy = drag_info.current_pointer_y;
2622 line->parent_group().w2i (cx, cy);
2624 const double fraction = 1.0 - ((cy - line->y_position()) / (double)line->height());
2628 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
2634 line->line_drag (current_line_drag_info.before, current_line_drag_info.after, fraction, push);
2636 set_verbose_canvas_cursor_text (line->get_verbose_cursor_string (fraction));
2640 Editor::line_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2642 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
2643 line_drag_motion_callback (item, event);
2648 Editor::start_region_grab (ArdourCanvas::Item* item, GdkEvent* event)
2650 if (selection->regions.empty() || clicked_regionview == 0) {
2654 drag_info.copy = false;
2655 drag_info.item = item;
2656 drag_info.data = clicked_regionview;
2657 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2658 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2663 TimeAxisView* tvp = clicked_axisview;
2664 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2666 if (tv && tv->is_track()) {
2667 speed = tv->get_diskstream()->speed();
2670 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2671 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2672 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2673 // we want a move threshold
2674 drag_info.want_move_threshold = true;
2676 show_verbose_time_cursor (drag_info.last_frame_position, 10);
2678 begin_reversible_command (_("move region(s)"));
2682 Editor::start_region_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2684 if (selection->regions.empty() || clicked_regionview == 0) {
2688 drag_info.copy = true;
2689 drag_info.item = item;
2690 drag_info.data = clicked_regionview;
2694 TimeAxisView* tv = &clicked_regionview->get_time_axis_view();
2695 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
2698 if (rtv && rtv->is_track()) {
2699 speed = rtv->get_diskstream()->speed();
2702 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2703 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2704 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2705 // we want a move threshold
2706 drag_info.want_move_threshold = true;
2707 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2708 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2709 show_verbose_time_cursor (drag_info.last_frame_position, 10);
2713 Editor::start_region_brush_grab (ArdourCanvas::Item* item, GdkEvent* event)
2715 if (selection->regions.empty() || clicked_regionview == 0) {
2719 drag_info.copy = false;
2720 drag_info.item = item;
2721 drag_info.data = clicked_regionview;
2722 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2723 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2728 TimeAxisView* tvp = clicked_axisview;
2729 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2731 if (tv && tv->is_track()) {
2732 speed = tv->get_diskstream()->speed();
2735 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2736 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2737 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2738 // we want a move threshold
2739 drag_info.want_move_threshold = true;
2740 drag_info.brushing = true;
2742 begin_reversible_command (_("Drag region brush"));
2746 Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2750 RegionView* rv = reinterpret_cast<RegionView*> (drag_info.data);
2751 nframes_t pending_region_position = 0;
2752 int32_t pointer_y_span = 0, canvas_pointer_y_span = 0, original_pointer_order;
2753 int32_t visible_y_high = 0, visible_y_low = 512; //high meaning higher numbered.. not the height on the screen
2754 bool clamp_y_axis = false;
2755 vector<int32_t> height_list(512) ;
2756 vector<int32_t>::iterator j;
2758 if (drag_info.copy && drag_info.move_threshold_passed && drag_info.want_move_threshold) {
2760 drag_info.want_move_threshold = false; // don't copy again
2762 /* duplicate the region(s) */
2764 vector<RegionView*> new_regionviews;
2766 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
2772 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
2773 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
2776 nrv = new AudioRegionView (*arv);
2778 nrv = new MidiRegionView (*mrv);
2783 nrv->get_canvas_group()->show ();
2785 new_regionviews.push_back (nrv);
2788 if (new_regionviews.empty()) {
2792 /* reset selection to new regionviews */
2794 selection->set (new_regionviews);
2796 /* reset drag_info data to reflect the fact that we are dragging the copies */
2798 drag_info.data = new_regionviews.front();
2800 swap_grab (new_regionviews.front()->get_canvas_group (), 0, event->motion.time);
2803 /* Which trackview is this ? */
2805 TimeAxisView* tvp = trackview_by_y_position (drag_info.current_pointer_y);
2806 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2808 /* The region motion is only processed if the pointer is over
2812 if (!tv || !tv->is_track()) {
2813 /* To make sure we hide the verbose canvas cursor when the mouse is
2814 not held over a track.
2816 hide_verbose_canvas_cursor ();
2820 original_pointer_order = drag_info.last_trackview->order;
2822 /************************************************************
2824 ************************************************************/
2826 if (drag_info.brushing) {
2827 clamp_y_axis = true;
2832 if ((pointer_y_span = (drag_info.last_trackview->order - tv->order)) != 0) {
2834 int32_t children = 0, numtracks = 0;
2835 // XXX hard coding track limit, oh my, so very very bad
2836 bitset <1024> tracks (0x00);
2837 /* get a bitmask representing the visible tracks */
2839 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2840 TimeAxisView *tracklist_timeview;
2841 tracklist_timeview = (*i);
2842 RouteTimeAxisView* rtv2 = dynamic_cast<RouteTimeAxisView*>(tracklist_timeview);
2843 TimeAxisView::Children children_list;
2845 /* zeroes are audio tracks. ones are other types. */
2847 if (!rtv2->hidden()) {
2849 if (visible_y_high < rtv2->order) {
2850 visible_y_high = rtv2->order;
2852 if (visible_y_low > rtv2->order) {
2853 visible_y_low = rtv2->order;
2856 if (!rtv2->is_track()) {
2857 tracks = tracks |= (0x01 << rtv2->order);
2860 height_list[rtv2->order] = (*i)->height;
2862 if ((children_list = rtv2->get_child_list()).size() > 0) {
2863 for (TimeAxisView::Children::iterator j = children_list.begin(); j != children_list.end(); ++j) {
2864 tracks = tracks |= (0x01 << (rtv2->order + children));
2865 height_list[rtv2->order + children] = (*j)->height;
2873 /* find the actual span according to the canvas */
2875 canvas_pointer_y_span = pointer_y_span;
2876 if (drag_info.last_trackview->order >= tv->order) {
2878 for (y = tv->order; y < drag_info.last_trackview->order; y++) {
2879 if (height_list[y] == 0 ) {
2880 canvas_pointer_y_span--;
2885 for (y = drag_info.last_trackview->order;y <= tv->order; y++) {
2886 if ( height_list[y] == 0 ) {
2887 canvas_pointer_y_span++;
2892 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
2893 RegionView* rv2 = (*i);
2894 double ix1, ix2, iy1, iy2;
2897 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
2898 rv2->get_canvas_group()->i2w (ix1, iy1);
2899 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
2900 RouteTimeAxisView* rtv2 = dynamic_cast<RouteTimeAxisView*>(tvp2);
2902 if (rtv2->order != original_pointer_order) {
2903 /* this isn't the pointer track */
2905 if (canvas_pointer_y_span > 0) {
2907 /* moving up the canvas */
2908 if ((rtv2->order - canvas_pointer_y_span) >= visible_y_low) {
2910 int32_t visible_tracks = 0;
2911 while (visible_tracks < canvas_pointer_y_span ) {
2914 while (height_list[rtv2->order - (visible_tracks - n)] == 0) {
2915 /* we're passing through a hidden track */
2920 if (tracks[rtv2->order - (canvas_pointer_y_span - n)] != 0x00) {
2921 clamp_y_axis = true;
2925 clamp_y_axis = true;
2928 } else if (canvas_pointer_y_span < 0) {
2930 /*moving down the canvas*/
2932 if ((rtv2->order - (canvas_pointer_y_span - n)) <= visible_y_high) { // we will overflow
2935 int32_t visible_tracks = 0;
2937 while (visible_tracks > canvas_pointer_y_span ) {
2940 while (height_list[rtv2->order - (visible_tracks - n)] == 0) {
2944 if ( tracks[rtv2->order - ( canvas_pointer_y_span - n)] != 0x00) {
2945 clamp_y_axis = true;
2950 clamp_y_axis = true;
2956 /* this is the pointer's track */
2957 if ((rtv2->order - pointer_y_span) > visible_y_high) { // we will overflow
2958 clamp_y_axis = true;
2959 } else if ((rtv2->order - pointer_y_span) < visible_y_low) { // we will underflow
2960 clamp_y_axis = true;
2968 } else if (drag_info.last_trackview == tv) {
2969 clamp_y_axis = true;
2973 if (!clamp_y_axis) {
2974 drag_info.last_trackview = tv;
2977 /************************************************************
2979 ************************************************************/
2981 /* compute the amount of pointer motion in frames, and where
2982 the region would be if we moved it by that much.
2985 if (drag_info.move_threshold_passed) {
2987 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2989 nframes_t sync_frame;
2990 nframes_t sync_offset;
2993 pending_region_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2995 sync_offset = rv->region()->sync_offset (sync_dir);
2996 sync_frame = rv->region()->adjust_to_sync (pending_region_position);
2998 /* we snap if the snap modifier is not enabled.
3001 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3002 snap_to (sync_frame);
3005 if (sync_frame - sync_offset <= sync_frame) {
3006 pending_region_position = sync_frame - (sync_dir*sync_offset);
3008 pending_region_position = 0;
3012 pending_region_position = 0;
3015 if (pending_region_position > max_frames - rv->region()->length()) {
3016 pending_region_position = drag_info.last_frame_position;
3019 // printf ("3: pending_region_position= %lu %lu\n", pending_region_position, drag_info.last_frame_position );
3021 if (pending_region_position != drag_info.last_frame_position && !drag_info.x_constrained) {
3023 /* now compute the canvas unit distance we need to move the regiondrag_info.last_trackview->order
3024 to make it appear at the new location.
3027 if (pending_region_position > drag_info.last_frame_position) {
3028 x_delta = ((double) (pending_region_position - drag_info.last_frame_position) / frames_per_unit);
3030 x_delta = -((double) (drag_info.last_frame_position - pending_region_position) / frames_per_unit);
3033 drag_info.last_frame_position = pending_region_position;
3040 /* threshold not passed */
3045 /*************************************************************
3047 ************************************************************/
3049 if (x_delta == 0 && (pointer_y_span == 0)) {
3050 /* haven't reached next snap point, and we're not switching
3051 trackviews. nothing to do.
3058 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3060 RegionView* rv2 = (*i);
3062 // If any regionview is at zero, we need to know so we can stop further leftward motion.
3064 double ix1, ix2, iy1, iy2;
3065 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3066 rv2->get_canvas_group()->i2w (ix1, iy1);
3075 /*************************************************************
3077 ************************************************************/
3081 if (drag_info.first_move) {
3082 if (drag_info.move_threshold_passed) {
3093 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
3094 const list<RegionView*>& layered_regions = selection->regions.by_layer();
3096 for (list<RegionView*>::const_iterator i = layered_regions.begin(); i != layered_regions.end(); ++i) {
3098 RegionView* rv = (*i);
3099 double ix1, ix2, iy1, iy2;
3100 int32_t temp_pointer_y_span = pointer_y_span;
3102 /* get item BBox, which will be relative to parent. so we have
3103 to query on a child, then convert to world coordinates using
3107 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3108 rv->get_canvas_group()->i2w (ix1, iy1);
3109 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3110 RouteTimeAxisView* canvas_rtv = dynamic_cast<RouteTimeAxisView*>(tvp2);
3111 RouteTimeAxisView* temp_rtv;
3113 if ((pointer_y_span != 0) && !clamp_y_axis) {
3116 for (j = height_list.begin(); j!= height_list.end(); j++) {
3117 if (x == canvas_rtv->order) {
3118 /* we found the track the region is on */
3119 if (x != original_pointer_order) {
3120 /*this isn't from the same track we're dragging from */
3121 temp_pointer_y_span = canvas_pointer_y_span;
3123 while (temp_pointer_y_span > 0) {
3124 /* we're moving up canvas-wise,
3125 so we need to find the next track height
3127 if (j != height_list.begin()) {
3130 if (x != original_pointer_order) {
3131 /* we're not from the dragged track, so ignore hidden tracks. */
3133 temp_pointer_y_span++;
3137 temp_pointer_y_span--;
3139 while (temp_pointer_y_span < 0) {
3141 if (x != original_pointer_order) {
3143 temp_pointer_y_span--;
3147 if (j != height_list.end()) {
3150 temp_pointer_y_span++;
3152 /* find out where we'll be when we move and set height accordingly */
3154 tvp2 = trackview_by_y_position (iy1 + y_delta);
3155 temp_rtv = dynamic_cast<RouteTimeAxisView*>(tvp2);
3156 rv->set_y_position_and_height (0, temp_rtv->height);
3158 /* if you un-comment the following, the region colours will follow the track colours whilst dragging,
3159 personally, i think this can confuse things, but never mind.
3162 //const GdkColor& col (temp_rtv->view->get_region_color());
3163 //rv->set_color (const_cast<GdkColor&>(col));
3170 /* prevent the regionview from being moved to before
3171 the zero position on the canvas.
3176 if (-x_delta > ix1) {
3179 } else if ((x_delta > 0) && (rv->region()->last_frame() > max_frames - x_delta)) {
3180 x_delta = max_frames - rv->region()->last_frame();
3184 if (drag_info.first_move) {
3186 /* hide any dependent views */
3188 rv->get_time_axis_view().hide_dependent_views (*rv);
3190 /* this is subtle. raising the regionview itself won't help,
3191 because raise_to_top() just puts the item on the top of
3192 its parent's stack. so, we need to put the trackview canvas_display group
3193 on the top, since its parent is the whole canvas.
3196 rv->get_canvas_group()->raise_to_top();
3197 rv->get_time_axis_view().canvas_display->raise_to_top();
3198 cursor_group->raise_to_top();
3200 rv->fake_set_opaque (true);
3203 if (drag_info.brushing) {
3204 mouse_brush_insert_region (rv, pending_region_position);
3206 rv->move (x_delta, y_delta);
3209 } /* foreach region */
3213 if (drag_info.first_move && drag_info.move_threshold_passed) {
3214 cursor_group->raise_to_top();
3215 drag_info.first_move = false;
3218 if (x_delta != 0 && !drag_info.brushing) {
3219 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3224 Editor::region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3227 RegionView* rv = reinterpret_cast<RegionView *> (drag_info.data);
3228 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
3229 bool nocommit = true;
3231 RouteTimeAxisView* rtv;
3232 bool regionview_y_movement;
3233 bool regionview_x_movement;
3234 vector<RegionView*> copies;
3236 /* first_move is set to false if the regionview has been moved in the
3240 if (drag_info.first_move) {
3247 /* The regionview has been moved at some stage during the grab so we need
3248 to account for any mouse movement between this event and the last one.
3251 region_drag_motion_callback (item, event);
3253 if (drag_info.brushing) {
3254 /* all changes were made during motion event handlers */
3256 if (drag_info.copy) {
3257 for (list<RegionView*>::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
3258 copies.push_back (*i);
3265 /* adjust for track speed */
3268 rtv = dynamic_cast<RouteTimeAxisView*> (drag_info.last_trackview);
3269 if (rtv && rtv->get_diskstream()) {
3270 speed = rtv->get_diskstream()->speed();
3273 regionview_x_movement = (drag_info.last_frame_position != (nframes_t) (rv->region()->position()/speed));
3274 regionview_y_movement = (drag_info.last_trackview != &rv->get_time_axis_view());
3276 //printf ("last_frame: %s position is %lu %g\n", rv->get_time_axis_view().name().c_str(), drag_info.last_frame_position, speed);
3277 //printf ("last_rackview: %s \n", drag_info.last_trackview->name().c_str());
3281 if (drag_info.copy) {
3282 if (drag_info.x_constrained) {
3283 op_string = _("fixed time region copy");
3285 op_string = _("region copy");
3288 if (drag_info.x_constrained) {
3289 op_string = _("fixed time region drag");
3291 op_string = _("region drag");
3295 begin_reversible_command (op_string);
3297 if (regionview_y_movement) {
3299 /* moved to a different audio track. */
3301 vector<RegionView*> new_selection;
3303 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ) {
3305 RegionView* rv = (*i);
3307 double ix1, ix2, iy1, iy2;
3309 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3310 rv->get_canvas_group()->i2w (ix1, iy1);
3311 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3312 RouteTimeAxisView* rtv2 = dynamic_cast<RouteTimeAxisView*>(tvp2);
3314 boost::shared_ptr<Playlist> from_playlist = rv->region()->playlist();
3315 boost::shared_ptr<Playlist> to_playlist = rtv2->playlist();
3317 where = (nframes_t) (unit_to_frame (ix1) * speed);
3318 boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region()));
3320 /* undo the previous hide_dependent_views so that xfades don't
3321 disappear on copying regions
3324 rv->get_time_axis_view().reveal_dependent_views (*rv);
3326 if (!drag_info.copy) {
3328 /* the region that used to be in the old playlist is not
3329 moved to the new one - we make a copy of it. as a result,
3330 any existing editor for the region should no longer be
3334 rv->hide_region_editor();
3335 rv->fake_set_opaque (false);
3337 session->add_command (new MementoCommand<Playlist>(*from_playlist, &from_playlist->get_state(), 0));
3338 from_playlist->remove_region ((rv->region()));
3339 session->add_command (new MementoCommand<Playlist>(*from_playlist, 0, &from_playlist->get_state()));
3343 /* the regionview we dragged around is a temporary copy, queue it for deletion */
3345 copies.push_back (rv);
3348 latest_regionview = 0;
3350 sigc::connection c = rtv2->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3351 session->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
3352 to_playlist->add_region (new_region, where);
3353 session->add_command (new MementoCommand<Playlist>(*to_playlist, 0, &to_playlist->get_state()));
3356 if (latest_regionview) {
3357 new_selection.push_back (latest_regionview);
3360 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
3361 was selected in all of them, then removing it from the playlist will have removed all
3362 trace of it from the selection (i.e. there were N regions selected, we removed 1,
3363 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
3364 corresponding regionview, and the selection is now empty).
3366 this could have invalidated any and all iterators into the region selection.
3368 the heuristic we use here is: if the region selection is empty, break out of the loop
3369 here. if the region selection is not empty, then restart the loop because we know that
3370 we must have removed at least the region(view) we've just been working on as well as any
3371 that we processed on previous iterations.
3373 EXCEPT .... if we are doing a copy drag, then the selection hasn't been modified and
3374 we can just iterate.
3377 if (drag_info.copy) {
3380 if (selection->regions.empty()) {
3383 i = selection->regions.by_layer().begin();
3388 selection->set (new_selection);
3392 /* motion within a single track */
3394 list<RegionView*> regions = selection->regions.by_layer();
3396 if (drag_info.copy) {
3397 selection->clear_regions();
3400 for (list<RegionView*>::iterator i = regions.begin(); i != regions.end(); ++i) {
3404 if (!rv->region()->can_move()) {
3408 if (regionview_x_movement) {
3409 double ownspeed = 1.0;
3410 rtv = dynamic_cast<RouteTimeAxisView*> (&(rv->get_time_axis_view()));
3412 if (rtv && rtv->get_diskstream()) {
3413 ownspeed = rtv->get_diskstream()->speed();
3416 /* base the new region position on the current position of the regionview.*/
3418 double ix1, ix2, iy1, iy2;
3420 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3421 rv->get_canvas_group()->i2w (ix1, iy1);
3422 where = (nframes_t) (unit_to_frame (ix1) * ownspeed);
3426 where = rv->region()->position();
3429 boost::shared_ptr<Playlist> to_playlist = rv->region()->playlist();
3431 assert (to_playlist);
3435 session->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
3437 if (drag_info.copy) {
3439 boost::shared_ptr<Region> newregion;
3440 boost::shared_ptr<Region> ar;
3441 boost::shared_ptr<Region> mr;
3443 if ((ar = boost::dynamic_pointer_cast<AudioRegion>(rv->region())) != 0) {
3444 newregion = RegionFactory::create (ar);
3445 } else if ((mr = boost::dynamic_pointer_cast<MidiRegion>(rv->region())) != 0) {
3446 newregion = RegionFactory::create (mr);
3451 latest_regionview = 0;
3452 sigc::connection c = rtv->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3453 to_playlist->add_region (newregion, (nframes_t) (where * rtv->get_diskstream()->speed()));
3456 if (latest_regionview) {
3457 rtv->reveal_dependent_views (*latest_regionview);
3458 selection->add (latest_regionview);
3463 /* just change the model */
3465 rv->region()->set_position (where, (void*) this);
3471 session->add_command (new MementoCommand<Playlist>(*to_playlist, 0, &to_playlist->get_state()));
3473 if (drag_info.copy) {
3474 copies.push_back (rv);
3482 commit_reversible_command ();
3485 for (vector<RegionView*>::iterator x = copies.begin(); x != copies.end(); ++x) {
3491 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
3493 /* Either add to or set the set the region selection, unless
3494 this is an alignment click (control used)
3497 if (Keyboard::modifier_state_contains (event->state, Keyboard::Control)) {
3498 TimeAxisView* tv = &rv.get_time_axis_view();
3499 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
3501 if (rtv && rtv->is_track()) {
3502 speed = rtv->get_diskstream()->speed();
3505 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Alt))) {
3507 align_region (rv.region(), SyncPoint, (nframes_t) (edit_cursor->current_frame * speed));
3509 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Shift))) {
3511 align_region (rv.region(), End, (nframes_t) (edit_cursor->current_frame * speed));
3515 align_region (rv.region(), Start, (nframes_t) (edit_cursor->current_frame * speed));
3521 Editor::show_verbose_time_cursor (nframes_t frame, double offset, double xpos, double ypos)
3527 nframes_t frame_rate;
3534 switch (Profile->get_small_screen() ? ARDOUR_UI::instance()->primary_clock.mode () : ARDOUR_UI::instance()->secondary_clock.mode ()) {
3535 case AudioClock::BBT:
3536 session->bbt_time (frame, bbt);
3537 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, bbt.bars, bbt.beats, bbt.ticks);
3540 case AudioClock::SMPTE:
3541 session->smpte_time (frame, smpte);
3542 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
3545 case AudioClock::MinSec:
3546 /* XXX this is copied from show_verbose_duration_cursor() */
3547 frame_rate = session->frame_rate();
3548 hours = frame / (frame_rate * 3600);
3549 frame = frame % (frame_rate * 3600);
3550 mins = frame / (frame_rate * 60);
3551 frame = frame % (frame_rate * 60);
3552 secs = (float) frame / (float) frame_rate;
3553 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
3557 snprintf (buf, sizeof(buf), "%u", frame);
3561 if (xpos >= 0 && ypos >=0) {
3562 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
3565 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
3567 show_verbose_canvas_cursor ();
3571 Editor::show_verbose_duration_cursor (nframes_t start, nframes_t end, double offset, double xpos, double ypos)
3578 nframes_t distance, frame_rate;
3580 Meter meter_at_start(session->tempo_map().meter_at(start));
3586 switch (ARDOUR_UI::instance()->secondary_clock.mode ()) {
3587 case AudioClock::BBT:
3588 session->bbt_time (start, sbbt);
3589 session->bbt_time (end, ebbt);
3592 /* XXX this computation won't work well if the
3593 user makes a selection that spans any meter changes.
3596 ebbt.bars -= sbbt.bars;
3597 if (ebbt.beats >= sbbt.beats) {
3598 ebbt.beats -= sbbt.beats;
3601 ebbt.beats = int(meter_at_start.beats_per_bar()) + ebbt.beats - sbbt.beats;
3603 if (ebbt.ticks >= sbbt.ticks) {
3604 ebbt.ticks -= sbbt.ticks;
3607 ebbt.ticks = int(Meter::ticks_per_beat) + ebbt.ticks - sbbt.ticks;
3610 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, ebbt.bars, ebbt.beats, ebbt.ticks);
3613 case AudioClock::SMPTE:
3614 session->smpte_duration (end - start, smpte);
3615 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
3618 case AudioClock::MinSec:
3619 /* XXX this stuff should be elsewhere.. */
3620 distance = end - start;
3621 frame_rate = session->frame_rate();
3622 hours = distance / (frame_rate * 3600);
3623 distance = distance % (frame_rate * 3600);
3624 mins = distance / (frame_rate * 60);
3625 distance = distance % (frame_rate * 60);
3626 secs = (float) distance / (float) frame_rate;
3627 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
3631 snprintf (buf, sizeof(buf), "%u", end - start);
3635 if (xpos >= 0 && ypos >=0) {
3636 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
3639 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
3641 show_verbose_canvas_cursor ();
3645 Editor::collect_new_region_view (RegionView* rv)
3647 latest_regionview = rv;
3651 Editor::start_selection_grab (ArdourCanvas::Item* item, GdkEvent* event)
3653 if (clicked_regionview == 0) {
3657 /* lets try to create new Region for the selection */
3659 vector<boost::shared_ptr<AudioRegion> > new_regions;
3660 create_region_from_selection (new_regions);
3662 if (new_regions.empty()) {
3666 /* XXX fix me one day to use all new regions */
3668 boost::shared_ptr<Region> region (new_regions.front());
3670 /* add it to the current stream/playlist.
3672 tricky: the streamview for the track will add a new regionview. we will
3673 catch the signal it sends when it creates the regionview to
3674 set the regionview we want to then drag.
3677 latest_regionview = 0;
3678 sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3680 /* A selection grab currently creates two undo/redo operations, one for
3681 creating the new region and another for moving it.
3684 begin_reversible_command (_("selection grab"));
3686 boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
3688 XMLNode *before = &(playlist->get_state());
3689 clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
3690 XMLNode *after = &(playlist->get_state());
3691 session->add_command(new MementoCommand<Playlist>(*playlist, before, after));
3693 commit_reversible_command ();
3697 if (latest_regionview == 0) {
3698 /* something went wrong */
3702 /* we need to deselect all other regionviews, and select this one
3703 i'm ignoring undo stuff, because the region creation will take care of it */
3704 selection->set (latest_regionview);
3706 drag_info.item = latest_regionview->get_canvas_group();
3707 drag_info.data = latest_regionview;
3708 drag_info.motion_callback = &Editor::region_drag_motion_callback;
3709 drag_info.finished_callback = &Editor::region_drag_finished_callback;
3713 drag_info.last_trackview = clicked_axisview;
3714 drag_info.last_frame_position = latest_regionview->region()->position();
3715 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
3717 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3721 Editor::cancel_selection ()
3723 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
3724 (*i)->hide_selection ();
3726 begin_reversible_command (_("cancel selection"));
3727 selection->clear ();
3728 clicked_selection = 0;
3729 commit_reversible_command ();
3733 Editor::start_selection_op (ArdourCanvas::Item* item, GdkEvent* event, SelectionOp op)
3735 nframes_t start = 0;
3742 drag_info.item = item;
3743 drag_info.motion_callback = &Editor::drag_selection;
3744 drag_info.finished_callback = &Editor::end_selection_op;
3749 case CreateSelection:
3750 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
3751 drag_info.copy = true;
3753 drag_info.copy = false;
3755 start_grab (event, selector_cursor);
3758 case SelectionStartTrim:
3759 if (clicked_axisview) {
3760 clicked_axisview->order_selection_trims (item, true);
3762 start_grab (event, trimmer_cursor);
3763 start = selection->time[clicked_selection].start;
3764 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
3767 case SelectionEndTrim:
3768 if (clicked_axisview) {
3769 clicked_axisview->order_selection_trims (item, false);
3771 start_grab (event, trimmer_cursor);
3772 end = selection->time[clicked_selection].end;
3773 drag_info.pointer_frame_offset = drag_info.grab_frame - end;
3777 start = selection->time[clicked_selection].start;
3779 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
3783 if (selection_op == SelectionMove) {
3784 show_verbose_time_cursor(start, 10);
3786 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
3791 Editor::drag_selection (ArdourCanvas::Item* item, GdkEvent* event)
3793 nframes_t start = 0;
3796 nframes_t pending_position;
3798 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
3799 pending_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
3801 pending_position = 0;
3804 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3805 snap_to (pending_position);
3808 /* only alter selection if the current frame is
3809 different from the last frame position (adjusted)
3812 if (pending_position == drag_info.last_pointer_frame) return;
3814 switch (selection_op) {
3815 case CreateSelection:
3817 if (drag_info.first_move) {
3818 snap_to (drag_info.grab_frame);
3821 if (pending_position < drag_info.grab_frame) {
3822 start = pending_position;
3823 end = drag_info.grab_frame;
3825 end = pending_position;
3826 start = drag_info.grab_frame;
3829 /* first drag: Either add to the selection
3830 or create a new selection->
3833 if (drag_info.first_move) {
3835 begin_reversible_command (_("range selection"));
3837 if (drag_info.copy) {
3838 /* adding to the selection */
3839 clicked_selection = selection->add (start, end);
3840 drag_info.copy = false;
3842 /* new selection-> */
3843 clicked_selection = selection->set (clicked_axisview, start, end);
3848 case SelectionStartTrim:
3850 if (drag_info.first_move) {
3851 begin_reversible_command (_("trim selection start"));
3854 start = selection->time[clicked_selection].start;
3855 end = selection->time[clicked_selection].end;
3857 if (pending_position > end) {
3860 start = pending_position;
3864 case SelectionEndTrim:
3866 if (drag_info.first_move) {
3867 begin_reversible_command (_("trim selection end"));
3870 start = selection->time[clicked_selection].start;
3871 end = selection->time[clicked_selection].end;
3873 if (pending_position < start) {
3876 end = pending_position;
3883 if (drag_info.first_move) {
3884 begin_reversible_command (_("move selection"));
3887 start = selection->time[clicked_selection].start;
3888 end = selection->time[clicked_selection].end;
3890 length = end - start;
3892 start = pending_position;
3895 end = start + length;
3900 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
3901 start_canvas_autoscroll (1);
3905 selection->replace (clicked_selection, start, end);
3908 drag_info.last_pointer_frame = pending_position;
3909 drag_info.first_move = false;
3911 if (selection_op == SelectionMove) {
3912 show_verbose_time_cursor(start, 10);
3914 show_verbose_time_cursor(pending_position, 10);
3919 Editor::end_selection_op (ArdourCanvas::Item* item, GdkEvent* event)
3921 if (!drag_info.first_move) {
3922 drag_selection (item, event);
3923 /* XXX this is not object-oriented programming at all. ick */
3924 if (selection->time.consolidate()) {
3925 selection->TimeChanged ();
3927 commit_reversible_command ();
3929 /* just a click, no pointer movement.*/
3931 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
3933 selection->clear_time();
3938 /* XXX what happens if its a music selection? */
3939 session->set_audio_range (selection->time);
3940 stop_canvas_autoscroll ();
3944 Editor::start_trim (ArdourCanvas::Item* item, GdkEvent* event)
3947 TimeAxisView* tvp = clicked_axisview;
3948 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
3950 if (tv && tv->is_track()) {
3951 speed = tv->get_diskstream()->speed();
3954 nframes_t region_start = (nframes_t) (clicked_regionview->region()->position() / speed);
3955 nframes_t region_end = (nframes_t) (clicked_regionview->region()->last_frame() / speed);
3956 nframes_t region_length = (nframes_t) (clicked_regionview->region()->length() / speed);
3958 //drag_info.item = clicked_regionview->get_name_highlight();
3959 drag_info.item = item;
3960 drag_info.motion_callback = &Editor::trim_motion_callback;
3961 drag_info.finished_callback = &Editor::trim_finished_callback;
3963 start_grab (event, trimmer_cursor);
3965 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
3966 trim_op = ContentsTrim;
3968 /* These will get overridden for a point trim.*/
3969 if (drag_info.current_pointer_frame < (region_start + region_length/2)) {
3970 /* closer to start */
3971 trim_op = StartTrim;
3972 } else if (drag_info.current_pointer_frame > (region_end - region_length/2)) {
3980 show_verbose_time_cursor(region_start, 10);
3983 show_verbose_time_cursor(region_end, 10);
3986 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
3992 Editor::trim_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
3994 RegionView* rv = clicked_regionview;
3995 nframes_t frame_delta = 0;
3996 bool left_direction;
3997 bool obey_snap = !Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier());
3999 /* snap modifier works differently here..
4000 its' current state has to be passed to the
4001 various trim functions in order to work properly
4005 TimeAxisView* tvp = clicked_axisview;
4006 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4007 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
4009 if (tv && tv->is_track()) {
4010 speed = tv->get_diskstream()->speed();
4013 if (drag_info.last_pointer_frame > drag_info.current_pointer_frame) {
4014 left_direction = true;
4016 left_direction = false;
4020 snap_to (drag_info.current_pointer_frame);
4023 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
4027 if (drag_info.first_move) {
4033 trim_type = "Region start trim";
4036 trim_type = "Region end trim";
4039 trim_type = "Region content trim";
4043 begin_reversible_command (trim_type);
4045 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4046 (*i)->fake_set_opaque(false);
4047 (*i)->region()->freeze ();
4049 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
4051 arv->temporarily_hide_envelope ();
4053 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4054 insert_result = motion_frozen_playlists.insert (pl);
4055 if (insert_result.second) {
4056 session->add_command(new MementoCommand<Playlist>(*pl, &pl->get_state(), 0));
4061 if (left_direction) {
4062 frame_delta = (drag_info.last_pointer_frame - drag_info.current_pointer_frame);
4064 frame_delta = (drag_info.current_pointer_frame - drag_info.last_pointer_frame);
4069 if ((left_direction == false) && (drag_info.current_pointer_frame <= rv->region()->first_frame()/speed)) {
4072 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4073 single_start_trim (**i, frame_delta, left_direction, obey_snap);
4079 if ((left_direction == true) && (drag_info.current_pointer_frame > (nframes_t) (rv->region()->last_frame()/speed))) {
4082 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4083 single_end_trim (**i, frame_delta, left_direction, obey_snap);
4090 bool swap_direction = false;
4092 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
4093 swap_direction = true;
4096 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4097 i != selection->regions.by_layer().end(); ++i)
4099 single_contents_trim (**i, frame_delta, left_direction, swap_direction, obey_snap);
4107 show_verbose_time_cursor((nframes_t) (rv->region()->position()/speed), 10);
4110 show_verbose_time_cursor((nframes_t) (rv->region()->last_frame()/speed), 10);
4113 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4117 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4118 drag_info.first_move = false;
4122 Editor::single_contents_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool swap_direction, bool obey_snap)
4124 boost::shared_ptr<Region> region (rv.region());
4126 if (region->locked()) {
4130 nframes_t new_bound;
4133 TimeAxisView* tvp = clicked_axisview;
4134 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4136 if (tv && tv->is_track()) {
4137 speed = tv->get_diskstream()->speed();
4140 if (left_direction) {
4141 if (swap_direction) {
4142 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4144 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4147 if (swap_direction) {
4148 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4150 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4155 snap_to (new_bound);
4157 region->trim_start ((nframes_t) (new_bound * speed), this);
4158 rv.region_changed (StartChanged);
4162 Editor::single_start_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool obey_snap)
4164 boost::shared_ptr<Region> region (rv.region());
4166 if (region->locked()) {
4170 nframes_t new_bound;
4173 TimeAxisView* tvp = clicked_axisview;
4174 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4176 if (tv && tv->is_track()) {
4177 speed = tv->get_diskstream()->speed();
4180 if (left_direction) {
4181 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4183 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4187 snap_to (new_bound, (left_direction ? 0 : 1));
4190 region->trim_front ((nframes_t) (new_bound * speed), this);
4192 rv.region_changed (Change (LengthChanged|PositionChanged|StartChanged));
4196 Editor::single_end_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool obey_snap)
4198 boost::shared_ptr<Region> region (rv.region());
4200 if (region->locked()) {
4204 nframes_t new_bound;
4207 TimeAxisView* tvp = clicked_axisview;
4208 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4210 if (tv && tv->is_track()) {
4211 speed = tv->get_diskstream()->speed();
4214 if (left_direction) {
4215 new_bound = (nframes_t) ((region->last_frame() + 1)/speed) - frame_delta;
4217 new_bound = (nframes_t) ((region->last_frame() + 1)/speed) + frame_delta;
4221 snap_to (new_bound);
4223 region->trim_end ((nframes_t) (new_bound * speed), this);
4224 rv.region_changed (LengthChanged);
4228 Editor::trim_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
4230 if (!drag_info.first_move) {
4231 trim_motion_callback (item, event);
4233 if (!clicked_regionview->get_selected()) {
4234 thaw_region_after_trim (*clicked_regionview);
4237 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4238 i != selection->regions.by_layer().end(); ++i)
4240 thaw_region_after_trim (**i);
4241 (*i)->fake_set_opaque (true);
4245 for (set<boost::shared_ptr<Playlist> >::iterator p = motion_frozen_playlists.begin(); p != motion_frozen_playlists.end(); ++p) {
4247 session->add_command (new MementoCommand<Playlist>(*(*p).get(), 0, &(*p)->get_state()));
4250 motion_frozen_playlists.clear ();
4252 commit_reversible_command();
4254 /* no mouse movement */
4260 Editor::point_trim (GdkEvent* event)
4262 RegionView* rv = clicked_regionview;
4263 nframes_t new_bound = drag_info.current_pointer_frame;
4265 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4266 snap_to (new_bound);
4269 /* Choose action dependant on which button was pressed */
4270 switch (event->button.button) {
4272 trim_op = StartTrim;
4273 begin_reversible_command (_("Start point trim"));
4275 if (rv->get_selected()) {
4277 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4278 i != selection->regions.by_layer().end(); ++i)
4280 if (!(*i)->region()->locked()) {
4281 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4282 XMLNode &before = pl->get_state();
4283 (*i)->region()->trim_front (new_bound, this);
4284 XMLNode &after = pl->get_state();
4285 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4291 if (!rv->region()->locked()) {
4292 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
4293 XMLNode &before = pl->get_state();
4294 rv->region()->trim_front (new_bound, this);
4295 XMLNode &after = pl->get_state();
4296 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4300 commit_reversible_command();
4305 begin_reversible_command (_("End point trim"));
4307 if (rv->get_selected()) {
4309 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
4311 if (!(*i)->region()->locked()) {
4312 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4313 XMLNode &before = pl->get_state();
4314 (*i)->region()->trim_end (new_bound, this);
4315 XMLNode &after = pl->get_state();
4316 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4322 if (!rv->region()->locked()) {
4323 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
4324 XMLNode &before = pl->get_state();
4325 rv->region()->trim_end (new_bound, this);
4326 XMLNode &after = pl->get_state();
4327 session->add_command (new MementoCommand<Playlist>(*pl.get(), &before, &after));
4331 commit_reversible_command();
4340 Editor::thaw_region_after_trim (RegionView& rv)
4342 boost::shared_ptr<Region> region (rv.region());
4344 if (region->locked()) {
4348 region->thaw (_("trimmed region"));
4349 XMLNode &after = region->playlist()->get_state();
4350 session->add_command (new MementoCommand<Playlist>(*(region->playlist()), 0, &after));
4352 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(&rv);
4354 arv->unhide_envelope ();
4358 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* event)
4363 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
4364 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
4368 Location* location = find_location_from_marker (marker, is_start);
4369 location->set_hidden (true, this);
4374 Editor::start_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event, RangeMarkerOp op)
4380 drag_info.item = item;
4381 drag_info.motion_callback = &Editor::drag_range_markerbar_op;
4382 drag_info.finished_callback = &Editor::end_range_markerbar_op;
4384 range_marker_op = op;
4386 if (!temp_location) {
4387 temp_location = new Location;
4391 case CreateRangeMarker:
4392 case CreateTransportMarker:
4394 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
4395 drag_info.copy = true;
4397 drag_info.copy = false;
4399 start_grab (event, selector_cursor);
4403 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4408 Editor::drag_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4410 nframes_t start = 0;
4412 ArdourCanvas::SimpleRect *crect = (range_marker_op == CreateRangeMarker) ? range_bar_drag_rect: transport_bar_drag_rect;
4414 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4415 snap_to (drag_info.current_pointer_frame);
4418 /* only alter selection if the current frame is
4419 different from the last frame position.
4422 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4424 switch (range_marker_op) {
4425 case CreateRangeMarker:
4426 case CreateTransportMarker:
4427 if (drag_info.first_move) {
4428 snap_to (drag_info.grab_frame);
4431 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4432 start = drag_info.current_pointer_frame;
4433 end = drag_info.grab_frame;
4435 end = drag_info.current_pointer_frame;
4436 start = drag_info.grab_frame;
4439 /* first drag: Either add to the selection
4440 or create a new selection.
4443 if (drag_info.first_move) {
4445 temp_location->set (start, end);
4449 update_marker_drag_item (temp_location);
4450 range_marker_drag_rect->show();
4451 range_marker_drag_rect->raise_to_top();
4457 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
4458 start_canvas_autoscroll (1);
4462 temp_location->set (start, end);
4464 double x1 = frame_to_pixel (start);
4465 double x2 = frame_to_pixel (end);
4466 crect->property_x1() = x1;
4467 crect->property_x2() = x2;
4469 update_marker_drag_item (temp_location);
4472 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4473 drag_info.first_move = false;
4475 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4480 Editor::end_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4482 Location * newloc = 0;
4485 if (!drag_info.first_move) {
4486 drag_range_markerbar_op (item, event);
4488 switch (range_marker_op) {
4489 case CreateRangeMarker:
4491 begin_reversible_command (_("new range marker"));
4492 XMLNode &before = session->locations()->get_state();
4493 session->locations()->next_available_name(rangename,"unnamed");
4494 newloc = new Location(temp_location->start(), temp_location->end(), rangename, Location::IsRangeMarker);
4495 session->locations()->add (newloc, true);
4496 XMLNode &after = session->locations()->get_state();
4497 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
4498 commit_reversible_command ();
4500 range_bar_drag_rect->hide();
4501 range_marker_drag_rect->hide();
4505 case CreateTransportMarker:
4506 // popup menu to pick loop or punch
4507 new_transport_marker_context_menu (&event->button, item);
4512 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
4514 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
4519 start = session->locations()->first_mark_before (drag_info.grab_frame);
4520 end = session->locations()->first_mark_after (drag_info.grab_frame);
4522 if (end == max_frames) {
4523 end = session->current_end_frame ();
4527 start = session->current_start_frame ();
4530 switch (mouse_mode) {
4532 /* find the two markers on either side and then make the selection from it */
4533 select_all_within (start, end, 0.0f, FLT_MAX, track_views, Selection::Set);
4537 /* find the two markers on either side of the click and make the range out of it */
4538 selection->set (0, start, end);
4547 stop_canvas_autoscroll ();
4553 Editor::start_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4555 drag_info.item = item;
4556 drag_info.motion_callback = &Editor::drag_mouse_zoom;
4557 drag_info.finished_callback = &Editor::end_mouse_zoom;
4559 start_grab (event, zoom_cursor);
4561 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4565 Editor::drag_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4570 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4571 snap_to (drag_info.current_pointer_frame);
4573 if (drag_info.first_move) {
4574 snap_to (drag_info.grab_frame);
4578 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4580 /* base start and end on initial click position */
4581 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4582 start = drag_info.current_pointer_frame;
4583 end = drag_info.grab_frame;
4585 end = drag_info.current_pointer_frame;
4586 start = drag_info.grab_frame;
4591 if (drag_info.first_move) {
4593 zoom_rect->raise_to_top();
4596 reposition_zoom_rect(start, end);
4598 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4599 drag_info.first_move = false;
4601 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4606 Editor::end_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4608 if (!drag_info.first_move) {
4609 drag_mouse_zoom (item, event);
4611 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
4612 temporal_zoom_by_frame (drag_info.grab_frame, drag_info.last_pointer_frame, "mouse zoom");
4614 temporal_zoom_by_frame (drag_info.last_pointer_frame, drag_info.grab_frame, "mouse zoom");
4617 temporal_zoom_to_frame (false, drag_info.grab_frame);
4619 temporal_zoom_step (false);
4620 center_screen (drag_info.grab_frame);
4628 Editor::reposition_zoom_rect (nframes_t start, nframes_t end)
4630 double x1 = frame_to_pixel (start);
4631 double x2 = frame_to_pixel (end);
4632 double y2 = full_canvas_height - 1.0;
4634 zoom_rect->property_x1() = x1;
4635 zoom_rect->property_y1() = 1.0;
4636 zoom_rect->property_x2() = x2;
4637 zoom_rect->property_y2() = y2;
4641 Editor::start_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4643 drag_info.item = item;
4644 drag_info.motion_callback = &Editor::drag_rubberband_select;
4645 drag_info.finished_callback = &Editor::end_rubberband_select;
4647 start_grab (event, cross_hair_cursor);
4649 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4653 Editor::drag_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4660 /* use a bigger drag threshold than the default */
4662 if (abs ((int) (drag_info.current_pointer_frame - drag_info.grab_frame)) < 8) {
4666 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4667 if (drag_info.first_move) {
4668 snap_to (drag_info.grab_frame);
4670 snap_to (drag_info.current_pointer_frame);
4673 /* base start and end on initial click position */
4675 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4676 start = drag_info.current_pointer_frame;
4677 end = drag_info.grab_frame;
4679 end = drag_info.current_pointer_frame;
4680 start = drag_info.grab_frame;
4683 if (drag_info.current_pointer_y < drag_info.grab_y) {
4684 y1 = drag_info.current_pointer_y;
4685 y2 = drag_info.grab_y;
4687 y2 = drag_info.current_pointer_y;
4688 y1 = drag_info.grab_y;
4692 if (start != end || y1 != y2) {
4694 double x1 = frame_to_pixel (start);
4695 double x2 = frame_to_pixel (end);
4697 rubberband_rect->property_x1() = x1;
4698 rubberband_rect->property_y1() = y1;
4699 rubberband_rect->property_x2() = x2;
4700 rubberband_rect->property_y2() = y2;
4702 rubberband_rect->show();
4703 rubberband_rect->raise_to_top();
4705 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4706 drag_info.first_move = false;
4708 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4713 Editor::end_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4715 if (!drag_info.first_move) {
4717 drag_rubberband_select (item, event);
4720 if (drag_info.current_pointer_y < drag_info.grab_y) {
4721 y1 = drag_info.current_pointer_y;
4722 y2 = drag_info.grab_y;
4725 y2 = drag_info.current_pointer_y;
4726 y1 = drag_info.grab_y;
4730 Selection::Operation op = Keyboard::selection_type (event->button.state);
4733 begin_reversible_command (_("rubberband selection"));
4735 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
4736 commit = select_all_within (drag_info.grab_frame, drag_info.last_pointer_frame, y1, y2, track_views, op);
4738 commit = select_all_within (drag_info.last_pointer_frame, drag_info.grab_frame, y1, y2, track_views, op);
4742 commit_reversible_command ();
4746 selection->clear_tracks();
4747 selection->clear_regions();
4748 selection->clear_points ();
4749 selection->clear_lines ();
4752 rubberband_rect->hide();
4757 Editor::mouse_rename_region (ArdourCanvas::Item* item, GdkEvent* event)
4759 using namespace Gtkmm2ext;
4761 ArdourPrompter prompter (false);
4763 prompter.set_prompt (_("Name for region:"));
4764 prompter.set_initial_text (clicked_regionview->region()->name());
4765 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
4766 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
4767 prompter.show_all ();
4768 switch (prompter.run ()) {
4769 case Gtk::RESPONSE_ACCEPT:
4771 prompter.get_result(str);
4773 clicked_regionview->region()->set_name (str);
4781 Editor::start_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
4783 drag_info.item = item;
4784 drag_info.motion_callback = &Editor::time_fx_motion;
4785 drag_info.finished_callback = &Editor::end_time_fx;
4789 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4793 Editor::time_fx_motion (ArdourCanvas::Item *item, GdkEvent* event)
4795 RegionView* rv = clicked_regionview;
4797 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4798 snap_to (drag_info.current_pointer_frame);
4801 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
4805 if (drag_info.current_pointer_frame > rv->region()->position()) {
4806 rv->get_time_axis_view().show_timestretch (rv->region()->position(), drag_info.current_pointer_frame);
4809 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4810 drag_info.first_move = false;
4812 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4816 Editor::end_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
4818 clicked_regionview->get_time_axis_view().hide_timestretch ();
4820 if (drag_info.first_move) {
4824 nframes_t newlen = drag_info.last_pointer_frame - clicked_regionview->region()->position();
4825 float percentage = (float) ((double) newlen - (double) clicked_regionview->region()->length()) / ((double) newlen) * 100.0f;
4827 begin_reversible_command (_("timestretch"));
4829 if (run_timestretch (selection->regions, percentage) == 0) {
4830 session->commit_reversible_command ();
4835 Editor::mouse_brush_insert_region (RegionView* rv, nframes_t pos)
4837 /* no brushing without a useful snap setting */
4840 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
4843 switch (snap_mode) {
4845 return; /* can't work because it allows region to be placed anywhere */
4850 switch (snap_type) {
4853 case SnapToEditCursor:
4860 /* don't brush a copy over the original */
4862 if (pos == rv->region()->position()) {
4866 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&arv->get_time_axis_view());
4868 if (rtv == 0 || !rtv->is_track()) {
4872 boost::shared_ptr<Playlist> playlist = rtv->playlist();
4873 double speed = rtv->get_diskstream()->speed();
4875 XMLNode &before = playlist->get_state();
4876 playlist->add_region (boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (arv->audio_region())), (nframes_t) (pos * speed));
4877 XMLNode &after = playlist->get_state();
4878 session->add_command(new MementoCommand<Playlist>(*playlist.get(), &before, &after));
4880 // playlist is frozen, so we have to update manually
4882 playlist->Modified(); /* EMIT SIGNAL */
4886 Editor::track_height_step_timeout ()
4889 struct timeval delta;
4891 gettimeofday (&now, 0);
4892 timersub (&now, &last_track_height_step_timestamp, &delta);
4894 if (delta.tv_sec * 1000000 + delta.tv_usec > 250000) { /* milliseconds */
4895 current_stepping_trackview = 0;