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>
31 #include <pbd/basename.h>
33 #include "ardour_ui.h"
35 #include "time_axis_view.h"
36 #include "audio_time_axis.h"
37 #include "audio_region_view.h"
38 #include "midi_region_view.h"
40 #include "streamview.h"
41 #include "region_gain_line.h"
42 #include "automation_time_axis.h"
43 #include "control_point.h"
46 #include "selection.h"
49 #include "rgb_macros.h"
51 #include <ardour/types.h>
52 #include <ardour/profile.h>
53 #include <ardour/route.h>
54 #include <ardour/audio_track.h>
55 #include <ardour/audio_diskstream.h>
56 #include <ardour/midi_diskstream.h>
57 #include <ardour/playlist.h>
58 #include <ardour/audioplaylist.h>
59 #include <ardour/audioregion.h>
60 #include <ardour/midi_region.h>
61 #include <ardour/dB.h>
62 #include <ardour/utils.h>
63 #include <ardour/region_factory.h>
64 #include <ardour/source_factory.h>
71 using namespace ARDOUR;
75 using namespace Editing;
77 const static double ZERO_GAIN_FRACTION = gain_to_slider_position(dB_to_coefficient(0.0));
80 Editor::mouse_frame (nframes64_t& where, bool& in_track_canvas) const
84 Gdk::ModifierType mask;
85 Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->track_canvas.get_window();
86 Glib::RefPtr<const Gdk::Window> pointer_window;
88 pointer_window = canvas_window->get_pointer (x, y, mask);
90 if (pointer_window == track_canvas.get_bin_window()) {
92 track_canvas.window_to_world (x, y, wx, wy);
93 in_track_canvas = true;
96 in_track_canvas = false;
98 if (pointer_window == time_canvas.get_bin_window()) {
99 time_canvas.window_to_world (x, y, wx, wy);
105 wx += horizontal_adjustment.get_value();
106 wy += vertical_adjustment.get_value();
109 event.type = GDK_BUTTON_RELEASE;
113 where = event_frame (&event, 0, 0);
118 Editor::event_frame (GdkEvent* event, double* pcx, double* pcy) const
132 switch (event->type) {
133 case GDK_BUTTON_RELEASE:
134 case GDK_BUTTON_PRESS:
135 case GDK_2BUTTON_PRESS:
136 case GDK_3BUTTON_PRESS:
137 track_canvas.w2c(event->button.x, event->button.y, *pcx, *pcy);
139 case GDK_MOTION_NOTIFY:
140 track_canvas.w2c(event->motion.x, event->motion.y, *pcx, *pcy);
142 case GDK_ENTER_NOTIFY:
143 case GDK_LEAVE_NOTIFY:
144 track_canvas.w2c(event->crossing.x, event->crossing.y, *pcx, *pcy);
147 case GDK_KEY_RELEASE:
148 // track_canvas.w2c(event->key.x, event->key.y, *pcx, *pcy);
151 warning << string_compose (_("Editor::event_frame() used on unhandled event type %1"), event->type) << endmsg;
155 /* note that pixel_to_frame() never returns less than zero, so even if the pixel
156 position is negative (as can be the case with motion events in particular),
157 the frame location is always positive.
160 return pixel_to_frame (*pcx);
164 Editor::mouse_mode_toggled (MouseMode m)
166 if (ignore_mouse_mode_toggle) {
172 if (mouse_select_button.get_active()) {
178 if (mouse_move_button.get_active()) {
184 if (mouse_gain_button.get_active()) {
190 if (mouse_zoom_button.get_active()) {
196 if (mouse_timefx_button.get_active()) {
202 if (mouse_audition_button.get_active()) {
208 if (mouse_note_button.get_active()) {
219 Editor::set_mouse_mode (MouseMode m, bool force)
221 if (drag_info.item) {
225 if (!force && m == mouse_mode) {
233 if (mouse_mode != MouseRange) {
235 /* in all modes except range, hide the range selection,
236 show the object (region) selection.
239 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
240 (*i)->set_should_show_selection (true);
242 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
243 (*i)->hide_selection ();
249 in range mode,show the range selection.
252 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
253 if ((*i)->get_selected()) {
254 (*i)->show_selection (selection->time);
259 /* XXX the hack of unsetting all other buttons should go
260 away once GTK2 allows us to use regular radio buttons drawn like
261 normal buttons, rather than my silly GroupedButton hack.
264 ignore_mouse_mode_toggle = true;
266 switch (mouse_mode) {
268 mouse_select_button.set_active (true);
269 current_canvas_cursor = selector_cursor;
273 mouse_move_button.set_active (true);
274 if (Profile->get_sae()) {
275 current_canvas_cursor = timebar_cursor;
277 current_canvas_cursor = grabber_cursor;
282 mouse_gain_button.set_active (true);
283 current_canvas_cursor = cross_hair_cursor;
287 mouse_zoom_button.set_active (true);
288 current_canvas_cursor = zoom_cursor;
292 mouse_timefx_button.set_active (true);
293 current_canvas_cursor = time_fx_cursor; // just use playhead
297 mouse_audition_button.set_active (true);
298 current_canvas_cursor = speaker_cursor;
302 mouse_note_button.set_active (true);
303 set_midi_edit_cursor (current_midi_edit_mode());
307 if (mouse_mode == MouseNote)
308 midi_toolbar_frame.show();
310 midi_toolbar_frame.hide();
312 ignore_mouse_mode_toggle = false;
315 track_canvas.get_window()->set_cursor(*current_canvas_cursor);
320 Editor::step_mouse_mode (bool next)
322 switch (current_mouse_mode()) {
324 if (next) set_mouse_mode (MouseRange);
325 else set_mouse_mode (MouseTimeFX);
329 if (next) set_mouse_mode (MouseZoom);
330 else set_mouse_mode (MouseObject);
334 if (next) set_mouse_mode (MouseGain);
335 else set_mouse_mode (MouseRange);
339 if (next) set_mouse_mode (MouseTimeFX);
340 else set_mouse_mode (MouseZoom);
344 if (next) set_mouse_mode (MouseAudition);
345 else set_mouse_mode (MouseGain);
349 if (next) set_mouse_mode (MouseObject);
350 else set_mouse_mode (MouseTimeFX);
354 if (next) set_mouse_mode (MouseObject);
355 else set_mouse_mode (MouseAudition);
361 Editor::midi_edit_mode_toggled (MidiEditMode m)
363 if (ignore_midi_edit_mode_toggle) {
369 if (midi_tool_pencil_button.get_active())
370 set_midi_edit_mode (m);
374 if (midi_tool_select_button.get_active())
375 set_midi_edit_mode (m);
379 if (midi_tool_erase_button.get_active())
380 set_midi_edit_mode (m);
387 set_midi_edit_cursor(m);
392 Editor::set_midi_edit_mode (MidiEditMode m, bool force)
394 if (drag_info.item) {
398 if (!force && m == midi_edit_mode) {
406 ignore_midi_edit_mode_toggle = true;
408 switch (midi_edit_mode) {
410 midi_tool_pencil_button.set_active (true);
414 midi_tool_select_button.set_active (true);
418 midi_tool_erase_button.set_active (true);
422 ignore_midi_edit_mode_toggle = false;
424 set_midi_edit_cursor (current_midi_edit_mode());
427 track_canvas.get_window()->set_cursor(*current_canvas_cursor);
432 Editor::set_midi_edit_cursor (MidiEditMode m)
434 switch (midi_edit_mode) {
436 current_canvas_cursor = midi_pencil_cursor;
440 current_canvas_cursor = midi_select_cursor;
444 current_canvas_cursor = midi_erase_cursor;
450 Editor::button_selection (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
452 /* in object/audition/timefx mode, any button press sets
453 the selection if the object can be selected. this is a
454 bit of hack, because we want to avoid this if the
455 mouse operation is a region alignment.
457 note: not dbl-click or triple-click
460 if (((mouse_mode != MouseObject) &&
461 (mouse_mode != MouseAudition || item_type != RegionItem) &&
462 (mouse_mode != MouseTimeFX || item_type != RegionItem) &&
463 (mouse_mode != MouseRange)) ||
465 (event->type != GDK_BUTTON_PRESS && event->type != GDK_BUTTON_RELEASE || event->button.button > 3)) {
470 if (event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) {
472 if ((event->button.state & Keyboard::RelevantModifierKeyMask) && event->button.button != 1) {
474 /* almost no selection action on modified button-2 or button-3 events */
476 if (item_type != RegionItem && event->button.button != 2) {
482 Selection::Operation op = Keyboard::selection_type (event->button.state);
483 bool press = (event->type == GDK_BUTTON_PRESS);
485 // begin_reversible_command (_("select on click"));
489 if (mouse_mode != MouseRange) {
490 set_selected_regionview_from_click (press, op, true);
491 } else if (event->type == GDK_BUTTON_PRESS) {
492 set_selected_track_as_side_effect ();
496 case RegionViewNameHighlight:
498 if (mouse_mode != MouseRange) {
499 set_selected_regionview_from_click (press, op, true);
500 } else if (event->type == GDK_BUTTON_PRESS) {
501 set_selected_track_as_side_effect ();
506 case FadeInHandleItem:
508 case FadeOutHandleItem:
510 if (mouse_mode != MouseRange) {
511 set_selected_regionview_from_click (press, op, true);
512 } else if (event->type == GDK_BUTTON_PRESS) {
513 set_selected_track_as_side_effect ();
517 case ControlPointItem:
518 set_selected_track_as_side_effect ();
519 if (mouse_mode != MouseRange) {
520 set_selected_control_point_from_click (op, false);
525 /* for context click or range selection, select track */
526 if (event->button.button == 3) {
527 set_selected_track_as_side_effect ();
528 } else if (event->type == GDK_BUTTON_PRESS && mouse_mode == MouseRange) {
529 set_selected_track_as_side_effect ();
533 case AutomationTrackItem:
534 set_selected_track_as_side_effect (true);
543 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
545 track_canvas.grab_focus();
547 if (session && session->actively_recording()) {
551 button_selection (item, event, item_type);
553 if (drag_info.item == 0 &&
554 (Keyboard::is_delete_event (&event->button) ||
555 Keyboard::is_context_menu_event (&event->button) ||
556 Keyboard::is_edit_event (&event->button))) {
558 /* handled by button release */
562 switch (event->button.button) {
565 if (event->type == GDK_BUTTON_PRESS) {
567 if (drag_info.item) {
568 drag_info.item->ungrab (event->button.time);
571 /* single mouse clicks on any of these item types operate
572 independent of mouse mode, mostly because they are
573 not on the main track canvas or because we want
578 case PlayheadCursorItem:
579 start_cursor_grab (item, event);
583 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
584 hide_marker (item, event);
586 start_marker_grab (item, event);
590 case TempoMarkerItem:
591 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
592 start_tempo_marker_copy_grab (item, event);
594 start_tempo_marker_grab (item, event);
598 case MeterMarkerItem:
599 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
600 start_meter_marker_copy_grab (item, event);
602 start_meter_marker_grab (item, event);
612 case RangeMarkerBarItem:
613 start_range_markerbar_op (item, event, CreateRangeMarker);
617 case CdMarkerBarItem:
618 start_range_markerbar_op (item, event, CreateCDMarker);
622 case TransportMarkerBarItem:
623 start_range_markerbar_op (item, event, CreateTransportMarker);
632 switch (mouse_mode) {
635 case StartSelectionTrimItem:
636 start_selection_op (item, event, SelectionStartTrim);
639 case EndSelectionTrimItem:
640 start_selection_op (item, event, SelectionEndTrim);
644 if (Keyboard::modifier_state_contains
645 (event->button.state, Keyboard::ModifierMask(Keyboard::SecondaryModifier))) {
646 // contains and not equals because I can't use alt as a modifier alone.
647 start_selection_grab (item, event);
648 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
649 /* grab selection for moving */
650 start_selection_op (item, event, SelectionMove);
652 /* this was debated, but decided the more common action was to
653 make a new selection */
654 start_selection_op (item, event, CreateSelection);
659 start_selection_op (item, event, CreateSelection);
665 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)) &&
666 event->type == GDK_BUTTON_PRESS) {
668 start_rubberband_select (item, event);
670 } else if (event->type == GDK_BUTTON_PRESS) {
673 case FadeInHandleItem:
674 start_fade_in_grab (item, event);
677 case FadeOutHandleItem:
678 start_fade_out_grab (item, event);
682 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
683 start_region_copy_grab (item, event);
684 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
685 start_region_brush_grab (item, event);
687 start_region_grab (item, event);
691 case RegionViewNameHighlight:
692 start_trim (item, event);
697 /* rename happens on edit clicks */
698 start_trim (clicked_regionview->get_name_highlight(), event);
702 case ControlPointItem:
703 start_control_point_grab (item, event);
707 case AutomationLineItem:
708 start_line_grab_from_line (item, event);
713 case AutomationTrackItem:
714 start_rubberband_select (item, event);
718 case ImageFrameHandleStartItem:
719 imageframe_start_handle_op(item, event) ;
722 case ImageFrameHandleEndItem:
723 imageframe_end_handle_op(item, event) ;
726 case MarkerViewHandleStartItem:
727 markerview_item_start_handle_op(item, event) ;
730 case MarkerViewHandleEndItem:
731 markerview_item_end_handle_op(item, event) ;
735 start_markerview_grab(item, event) ;
738 start_imageframe_grab(item, event) ;
756 // start_line_grab_from_regionview (item, event);
760 start_line_grab_from_line (item, event);
763 case ControlPointItem:
764 start_control_point_grab (item, event);
775 case ControlPointItem:
776 start_control_point_grab (item, event);
779 case AutomationLineItem:
780 start_line_grab_from_line (item, event);
784 // XXX need automation mode to identify which
786 // start_line_grab_from_regionview (item, event);
796 if (event->type == GDK_BUTTON_PRESS) {
797 start_mouse_zoom (item, event);
804 if (item_type == RegionItem) {
805 start_time_fx (item, event);
812 scrub_reverse_distance = 0;
813 last_scrub_x = event->button.x;
814 scrubbing_direction = 0;
815 track_canvas.get_window()->set_cursor (*transparent_cursor);
816 /* rest handled in motion & release */
820 start_create_region_grab (item, event);
829 switch (mouse_mode) {
831 if (event->type == GDK_BUTTON_PRESS) {
834 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
835 start_region_copy_grab (item, event);
837 start_region_grab (item, event);
841 case ControlPointItem:
842 start_control_point_grab (item, event);
853 case RegionViewNameHighlight:
854 start_trim (item, event);
859 start_trim (clicked_regionview->get_name_highlight(), event);
870 if (event->type == GDK_BUTTON_PRESS) {
871 /* relax till release */
878 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
879 temporal_zoom_session();
881 temporal_zoom_to_frame (true, event_frame(event));
904 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
906 nframes_t where = event_frame (event, 0, 0);
908 /* no action if we're recording */
910 if (session && session->actively_recording()) {
914 /* first, see if we're finishing a drag ... */
916 if (drag_info.item) {
917 if (end_grab (item, event)) {
918 /* grab dragged, so do nothing else */
923 button_selection (item, event, item_type);
925 /* edit events get handled here */
927 if (drag_info.item == 0 && Keyboard::is_edit_event (&event->button)) {
933 case TempoMarkerItem:
934 edit_tempo_marker (item);
937 case MeterMarkerItem:
938 edit_meter_marker (item);
942 if (clicked_regionview->name_active()) {
943 return mouse_rename_region (item, event);
953 /* context menu events get handled here */
955 if (Keyboard::is_context_menu_event (&event->button)) {
957 if (drag_info.item == 0) {
959 /* no matter which button pops up the context menu, tell the menu
960 widget to use button 1 to drive menu selection.
965 case FadeInHandleItem:
967 case FadeOutHandleItem:
968 popup_fade_context_menu (1, event->button.time, item, item_type);
972 popup_track_context_menu (1, event->button.time, item_type, false, where);
976 case RegionViewNameHighlight:
978 popup_track_context_menu (1, event->button.time, item_type, false, where);
982 popup_track_context_menu (1, event->button.time, item_type, true, where);
985 case AutomationTrackItem:
986 popup_track_context_menu (1, event->button.time, item_type, false, where);
990 case RangeMarkerBarItem:
991 case TransportMarkerBarItem:
992 case CdMarkerBarItem:
995 popup_ruler_menu (pixel_to_frame(event->button.x), item_type);
999 marker_context_menu (&event->button, item);
1002 case TempoMarkerItem:
1003 tm_marker_context_menu (&event->button, item);
1006 case MeterMarkerItem:
1007 tm_marker_context_menu (&event->button, item);
1010 case CrossfadeViewItem:
1011 popup_track_context_menu (1, event->button.time, item_type, false, where);
1015 case ImageFrameItem:
1016 popup_imageframe_edit_menu(1, event->button.time, item, true) ;
1018 case ImageFrameTimeAxisItem:
1019 popup_imageframe_edit_menu(1, event->button.time, item, false) ;
1021 case MarkerViewItem:
1022 popup_marker_time_axis_edit_menu(1, event->button.time, item, true) ;
1024 case MarkerTimeAxisItem:
1025 popup_marker_time_axis_edit_menu(1, event->button.time, item, false) ;
1037 /* delete events get handled here */
1039 if (drag_info.item == 0 && Keyboard::is_delete_event (&event->button)) {
1041 switch (item_type) {
1042 case TempoMarkerItem:
1043 remove_tempo_marker (item);
1046 case MeterMarkerItem:
1047 remove_meter_marker (item);
1051 remove_marker (*item, event);
1055 if (mouse_mode == MouseObject) {
1056 remove_clicked_region ();
1060 case ControlPointItem:
1061 if (mouse_mode == MouseGain) {
1062 remove_gain_control_point (item, event);
1064 remove_control_point (item, event);
1074 switch (event->button.button) {
1077 switch (item_type) {
1078 /* see comments in button_press_handler */
1079 case PlayheadCursorItem:
1082 case AutomationLineItem:
1083 case StartSelectionTrimItem:
1084 case EndSelectionTrimItem:
1088 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1089 snap_to (where, 0, true);
1091 mouse_add_new_marker (where);
1094 case CdMarkerBarItem:
1095 // if we get here then a dragged range wasn't done
1096 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1097 snap_to (where, 0, true);
1099 mouse_add_new_marker (where, true);
1103 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1106 mouse_add_new_tempo_event (where);
1110 mouse_add_new_meter_event (pixel_to_frame (event->button.x));
1118 switch (mouse_mode) {
1120 switch (item_type) {
1121 case AutomationTrackItem:
1122 dynamic_cast<AutomationTimeAxisView*>(clicked_axisview)->add_automation_event
1136 // Gain only makes sense for audio regions
1138 if (!dynamic_cast<AudioRegionView*>(clicked_regionview)) {
1142 switch (item_type) {
1144 dynamic_cast<AudioRegionView*>(clicked_regionview)->add_gain_point_event (item, event);
1148 case AutomationTrackItem:
1149 dynamic_cast<AutomationTimeAxisView*>(clicked_axisview)->
1150 add_automation_event (item, event, where, event->button.y);
1160 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1161 if (scrubbing_direction == 0) {
1162 /* no drag, just a click */
1163 switch (item_type) {
1165 play_selected_region ();
1171 /* make sure we stop */
1172 session->request_transport_speed (0.0);
1186 switch (mouse_mode) {
1189 switch (item_type) {
1191 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1193 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier|Keyboard::SecondaryModifier))) {
1196 // Button2 click is unused
1209 // x_style_paste (where, 1.0);
1229 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1235 switch (item_type) {
1236 case ControlPointItem:
1237 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1238 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1239 cp->set_visible (true);
1243 at_y = cp->get_y ();
1244 cp->item()->i2w (at_x, at_y);
1248 fraction = 1.0 - (cp->get_y() / cp->line().height());
1250 set_verbose_canvas_cursor (cp->line().get_verbose_cursor_string (fraction), at_x, at_y);
1251 show_verbose_canvas_cursor ();
1253 if (is_drawable() && !_scrubbing) {
1254 track_canvas.get_window()->set_cursor (*fader_cursor);
1260 if (mouse_mode == MouseGain) {
1261 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1263 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredGainLine.get();
1264 if (is_drawable()) {
1265 track_canvas.get_window()->set_cursor (*fader_cursor);
1270 case AutomationLineItem:
1271 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1273 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1275 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredAutomationLine.get();
1277 if (is_drawable()) {
1278 track_canvas.get_window()->set_cursor (*fader_cursor);
1283 case RegionViewNameHighlight:
1284 if (is_drawable() && mouse_mode == MouseObject) {
1285 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1289 case StartSelectionTrimItem:
1290 case EndSelectionTrimItem:
1293 case ImageFrameHandleStartItem:
1294 case ImageFrameHandleEndItem:
1295 case MarkerViewHandleStartItem:
1296 case MarkerViewHandleEndItem:
1299 if (is_drawable()) {
1300 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1304 case PlayheadCursorItem:
1305 if (is_drawable()) {
1306 track_canvas.get_window()->set_cursor (*grabber_cursor);
1310 case RegionViewName:
1312 /* when the name is not an active item, the entire name highlight is for trimming */
1314 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1315 if (mouse_mode == MouseObject && is_drawable()) {
1316 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1322 case AutomationTrackItem:
1323 if (is_drawable()) {
1324 Gdk::Cursor *cursor;
1325 switch (mouse_mode) {
1327 cursor = selector_cursor;
1330 cursor = zoom_cursor;
1333 cursor = cross_hair_cursor;
1337 track_canvas.get_window()->set_cursor (*cursor);
1339 AutomationTimeAxisView* atv;
1340 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1341 clear_entered_track = false;
1342 set_entered_track (atv);
1348 case RangeMarkerBarItem:
1349 case TransportMarkerBarItem:
1350 case CdMarkerBarItem:
1353 if (is_drawable()) {
1354 time_canvas.get_window()->set_cursor (*timebar_cursor);
1359 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1362 entered_marker = marker;
1363 marker->set_color_rgba (ARDOUR_UI::config()->canvasvar_EnteredMarker.get());
1365 case MeterMarkerItem:
1366 case TempoMarkerItem:
1367 if (is_drawable()) {
1368 time_canvas.get_window()->set_cursor (*timebar_cursor);
1371 case FadeInHandleItem:
1372 case FadeOutHandleItem:
1373 if (mouse_mode == MouseObject) {
1374 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1376 rect->property_fill_color_rgba() = 0;
1377 rect->property_outline_pixels() = 1;
1386 /* second pass to handle entered track status in a comprehensible way.
1389 switch (item_type) {
1391 case AutomationLineItem:
1392 case ControlPointItem:
1393 /* these do not affect the current entered track state */
1394 clear_entered_track = false;
1397 case AutomationTrackItem:
1398 /* handled above already */
1402 set_entered_track (0);
1410 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1419 switch (item_type) {
1420 case ControlPointItem:
1421 cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
1422 if (cp->line().the_list()->interpolation() != AutomationList::Discrete) {
1423 if (cp->line().npoints() > 1 && !cp->selected()) {
1424 cp->set_visible (false);
1428 if (is_drawable()) {
1429 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1432 hide_verbose_canvas_cursor ();
1435 case RegionViewNameHighlight:
1436 case StartSelectionTrimItem:
1437 case EndSelectionTrimItem:
1438 case PlayheadCursorItem:
1441 case ImageFrameHandleStartItem:
1442 case ImageFrameHandleEndItem:
1443 case MarkerViewHandleStartItem:
1444 case MarkerViewHandleEndItem:
1447 if (is_drawable()) {
1448 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1453 case AutomationLineItem:
1454 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1456 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1458 line->property_fill_color_rgba() = al->get_line_color();
1460 if (is_drawable()) {
1461 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1465 case RegionViewName:
1466 /* see enter_handler() for notes */
1467 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1468 if (is_drawable() && mouse_mode == MouseObject) {
1469 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1474 case RangeMarkerBarItem:
1475 case TransportMarkerBarItem:
1476 case CdMarkerBarItem:
1480 if (is_drawable()) {
1481 time_canvas.get_window()->set_cursor (*timebar_cursor);
1486 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1490 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
1491 location_flags_changed (loc, this);
1494 case MeterMarkerItem:
1495 case TempoMarkerItem:
1497 if (is_drawable()) {
1498 time_canvas.get_window()->set_cursor (*timebar_cursor);
1503 case FadeInHandleItem:
1504 case FadeOutHandleItem:
1505 rv = static_cast<RegionView*>(item->get_data ("regionview"));
1507 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1509 rect->property_fill_color_rgba() = rv->get_fill_color();
1510 rect->property_outline_pixels() = 0;
1515 case AutomationTrackItem:
1516 if (is_drawable()) {
1517 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1518 clear_entered_track = true;
1519 Glib::signal_idle().connect (mem_fun(*this, &Editor::left_automation_track));
1531 Editor::left_automation_track ()
1533 if (clear_entered_track) {
1534 set_entered_track (0);
1535 clear_entered_track = false;
1541 Editor::motion_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type, bool from_autoscroll)
1543 if (event->motion.is_hint) {
1546 /* We call this so that MOTION_NOTIFY events continue to be
1547 delivered to the canvas. We need to do this because we set
1548 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
1549 the density of the events, at the expense of a round-trip
1550 to the server. Given that this will mostly occur on cases
1551 where DISPLAY = :0.0, and given the cost of what the motion
1552 event might do, its a good tradeoff.
1555 track_canvas.get_pointer (x, y);
1558 if (current_stepping_trackview) {
1559 /* don't keep the persistent stepped trackview if the mouse moves */
1560 current_stepping_trackview = 0;
1561 step_timeout.disconnect ();
1564 if (session && session->actively_recording()) {
1565 /* Sorry. no dragging stuff around while we record */
1569 drag_info.item_type = item_type;
1570 drag_info.last_pointer_x = drag_info.current_pointer_x;
1571 drag_info.last_pointer_y = drag_info.current_pointer_y;
1572 drag_info.current_pointer_frame = event_frame (event, &drag_info.current_pointer_x,
1573 &drag_info.current_pointer_y);
1575 switch (mouse_mode) {
1581 if (scrubbing_direction == 0) {
1583 session->request_locate (drag_info.current_pointer_frame, false);
1584 session->request_transport_speed (0.1);
1585 scrubbing_direction = 1;
1589 if (last_scrub_x > drag_info.current_pointer_x) {
1591 /* pointer moved to the left */
1593 if (scrubbing_direction > 0) {
1595 /* we reversed direction to go backwards */
1598 scrub_reverse_distance += (int) (last_scrub_x - drag_info.current_pointer_x);
1602 /* still moving to the left (backwards) */
1604 scrub_reversals = 0;
1605 scrub_reverse_distance = 0;
1607 delta = 0.01 * (last_scrub_x - drag_info.current_pointer_x);
1608 session->request_transport_speed (session->transport_speed() - delta);
1612 /* pointer moved to the right */
1614 if (scrubbing_direction < 0) {
1615 /* we reversed direction to go forward */
1618 scrub_reverse_distance += (int) (drag_info.current_pointer_x - last_scrub_x);
1621 /* still moving to the right */
1623 scrub_reversals = 0;
1624 scrub_reverse_distance = 0;
1626 delta = 0.01 * (drag_info.current_pointer_x - last_scrub_x);
1627 session->request_transport_speed (session->transport_speed() + delta);
1631 /* if there have been more than 2 opposite motion moves detected, or one that moves
1632 back more than 10 pixels, reverse direction
1635 if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
1637 if (scrubbing_direction > 0) {
1638 /* was forwards, go backwards */
1639 session->request_transport_speed (-0.1);
1640 scrubbing_direction = -1;
1642 /* was backwards, go forwards */
1643 session->request_transport_speed (0.1);
1644 scrubbing_direction = 1;
1647 scrub_reverse_distance = 0;
1648 scrub_reversals = 0;
1652 last_scrub_x = drag_info.current_pointer_x;
1660 if (!from_autoscroll && drag_info.item) {
1661 /* item != 0 is the best test i can think of for dragging.
1663 if (!drag_info.move_threshold_passed) {
1665 bool x_threshold_passed = (::llabs ((nframes64_t) (drag_info.current_pointer_x - drag_info.grab_x)) > 4LL);
1666 bool y_threshold_passed = (::llabs ((nframes64_t) (drag_info.current_pointer_y - drag_info.grab_y)) > 4LL);
1668 drag_info.move_threshold_passed = (x_threshold_passed || y_threshold_passed);
1670 // and change the initial grab loc/frame if this drag info wants us to
1672 if (drag_info.want_move_threshold && drag_info.move_threshold_passed) {
1673 drag_info.grab_frame = drag_info.current_pointer_frame;
1674 drag_info.grab_x = drag_info.current_pointer_x;
1675 drag_info.grab_y = drag_info.current_pointer_y;
1676 drag_info.last_pointer_frame = drag_info.grab_frame;
1677 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
1682 switch (item_type) {
1683 case PlayheadCursorItem:
1685 case ControlPointItem:
1686 case TempoMarkerItem:
1687 case MeterMarkerItem:
1688 case RegionViewNameHighlight:
1689 case StartSelectionTrimItem:
1690 case EndSelectionTrimItem:
1693 case AutomationLineItem:
1694 case FadeInHandleItem:
1695 case FadeOutHandleItem:
1698 case ImageFrameHandleStartItem:
1699 case ImageFrameHandleEndItem:
1700 case MarkerViewHandleStartItem:
1701 case MarkerViewHandleEndItem:
1704 if (drag_info.item && (event->motion.state & Gdk::BUTTON1_MASK ||
1705 (event->motion.state & Gdk::BUTTON2_MASK))) {
1706 if (!from_autoscroll) {
1707 maybe_autoscroll (event);
1709 (this->*(drag_info.motion_callback)) (item, event);
1718 switch (mouse_mode) {
1724 if (drag_info.item && (event->motion.state & GDK_BUTTON1_MASK ||
1725 (event->motion.state & GDK_BUTTON2_MASK))) {
1726 if (!from_autoscroll) {
1727 maybe_autoscroll (event);
1729 (this->*(drag_info.motion_callback)) (item, event);
1740 track_canvas_motion (event);
1741 // drag_info.last_pointer_frame = drag_info.current_pointer_frame;
1749 Editor::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
1751 if (drag_info.item == 0) {
1752 fatal << _("programming error: start_grab called without drag item") << endmsg;
1758 cursor = grabber_cursor;
1761 // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
1763 if (event->button.button == 2) {
1764 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
1765 drag_info.y_constrained = true;
1766 drag_info.x_constrained = false;
1768 drag_info.y_constrained = false;
1769 drag_info.x_constrained = true;
1772 drag_info.x_constrained = false;
1773 drag_info.y_constrained = false;
1776 drag_info.grab_frame = event_frame (event, &drag_info.grab_x, &drag_info.grab_y);
1777 drag_info.last_pointer_frame = drag_info.grab_frame;
1778 drag_info.current_pointer_frame = drag_info.grab_frame;
1779 drag_info.current_pointer_x = drag_info.grab_x;
1780 drag_info.current_pointer_y = drag_info.grab_y;
1781 drag_info.last_pointer_x = drag_info.current_pointer_x;
1782 drag_info.last_pointer_y = drag_info.current_pointer_y;
1783 drag_info.cumulative_x_drag = 0;
1784 drag_info.cumulative_y_drag = 0;
1785 drag_info.first_move = true;
1786 drag_info.move_threshold_passed = false;
1787 drag_info.want_move_threshold = false;
1788 drag_info.pointer_frame_offset = 0;
1789 drag_info.brushing = false;
1790 drag_info.copied_location = 0;
1792 drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK,
1794 event->button.time);
1796 if (session && session->transport_rolling()) {
1797 drag_info.was_rolling = true;
1799 drag_info.was_rolling = false;
1802 switch (snap_type) {
1803 case SnapToRegionStart:
1804 case SnapToRegionEnd:
1805 case SnapToRegionSync:
1806 case SnapToRegionBoundary:
1807 build_region_boundary_cache ();
1815 Editor::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t time)
1817 drag_info.item->ungrab (0);
1818 drag_info.item = new_item;
1821 cursor = grabber_cursor;
1824 drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK, *cursor, time);
1828 Editor::end_grab (ArdourCanvas::Item* item, GdkEvent* event)
1830 bool did_drag = false;
1832 stop_canvas_autoscroll ();
1834 if (drag_info.item == 0) {
1838 drag_info.item->ungrab (event->button.time);
1840 if (drag_info.finished_callback) {
1841 drag_info.last_pointer_x = drag_info.current_pointer_x;
1842 drag_info.last_pointer_y = drag_info.current_pointer_y;
1843 (this->*(drag_info.finished_callback)) (item, event);
1846 did_drag = !drag_info.first_move;
1848 hide_verbose_canvas_cursor();
1851 drag_info.copy = false;
1852 drag_info.motion_callback = 0;
1853 drag_info.finished_callback = 0;
1854 drag_info.last_trackview = 0;
1855 drag_info.last_frame_position = 0;
1856 drag_info.grab_frame = 0;
1857 drag_info.last_pointer_frame = 0;
1858 drag_info.current_pointer_frame = 0;
1859 drag_info.brushing = false;
1861 if (drag_info.copied_location) {
1862 delete drag_info.copied_location;
1863 drag_info.copied_location = 0;
1870 Editor::start_fade_in_grab (ArdourCanvas::Item* item, GdkEvent* event)
1872 drag_info.item = item;
1873 drag_info.motion_callback = &Editor::fade_in_drag_motion_callback;
1874 drag_info.finished_callback = &Editor::fade_in_drag_finished_callback;
1878 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
1879 fatal << _("programming error: fade in canvas item has no regionview data pointer!") << endmsg;
1883 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1885 drag_info.pointer_frame_offset = drag_info.grab_frame - ((nframes_t) arv->audio_region()->fade_in()->back()->when + arv->region()->position());
1889 Editor::fade_in_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1891 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1893 nframes_t fade_length;
1895 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1896 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1902 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1906 if (pos < (arv->region()->position() + 64)) {
1907 fade_length = 64; // this should be a minimum defined somewhere
1908 } else if (pos > arv->region()->last_frame()) {
1909 fade_length = arv->region()->length();
1911 fade_length = pos - arv->region()->position();
1913 /* mapover the region selection */
1915 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1917 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1923 tmp->reset_fade_in_shape_width (fade_length);
1926 show_verbose_duration_cursor (arv->region()->position(), arv->region()->position() + fade_length, 10);
1928 drag_info.first_move = false;
1932 Editor::fade_in_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1934 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1936 nframes_t fade_length;
1938 if (drag_info.first_move) return;
1940 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1941 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1946 if (pos < (arv->region()->position() + 64)) {
1947 fade_length = 64; // this should be a minimum defined somewhere
1948 } else if (pos > arv->region()->last_frame()) {
1949 fade_length = arv->region()->length();
1951 fade_length = pos - arv->region()->position();
1954 begin_reversible_command (_("change fade in length"));
1956 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1958 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1964 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
1965 XMLNode &before = alist->get_state();
1967 tmp->audio_region()->set_fade_in_length (fade_length);
1969 XMLNode &after = alist->get_state();
1970 session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
1973 commit_reversible_command ();
1977 Editor::start_fade_out_grab (ArdourCanvas::Item* item, GdkEvent* event)
1979 drag_info.item = item;
1980 drag_info.motion_callback = &Editor::fade_out_drag_motion_callback;
1981 drag_info.finished_callback = &Editor::fade_out_drag_finished_callback;
1985 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
1986 fatal << _("programming error: fade out canvas item has no regionview data pointer!") << endmsg;
1990 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1992 drag_info.pointer_frame_offset = drag_info.grab_frame - (arv->region()->length() - (nframes_t) arv->audio_region()->fade_out()->back()->when + arv->region()->position());
1996 Editor::fade_out_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1998 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
2000 nframes_t fade_length;
2002 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2003 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2008 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2012 if (pos > (arv->region()->last_frame() - 64)) {
2013 fade_length = 64; // this should really be a minimum fade defined somewhere
2015 else if (pos < arv->region()->position()) {
2016 fade_length = arv->region()->length();
2019 fade_length = arv->region()->last_frame() - pos;
2022 /* mapover the region selection */
2024 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2026 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2032 tmp->reset_fade_out_shape_width (fade_length);
2035 show_verbose_duration_cursor (arv->region()->last_frame() - fade_length, arv->region()->last_frame(), 10);
2037 drag_info.first_move = false;
2041 Editor::fade_out_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2043 if (drag_info.first_move) return;
2045 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
2047 nframes_t fade_length;
2049 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2050 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2056 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2060 if (pos > (arv->region()->last_frame() - 64)) {
2061 fade_length = 64; // this should really be a minimum fade defined somewhere
2063 else if (pos < arv->region()->position()) {
2064 fade_length = arv->region()->length();
2067 fade_length = arv->region()->last_frame() - pos;
2070 begin_reversible_command (_("change fade out length"));
2072 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2074 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2080 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
2081 XMLNode &before = alist->get_state();
2083 tmp->audio_region()->set_fade_out_length (fade_length);
2085 XMLNode &after = alist->get_state();
2086 session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2089 commit_reversible_command ();
2093 Editor::start_cursor_grab (ArdourCanvas::Item* item, GdkEvent* event)
2095 drag_info.item = item;
2096 drag_info.motion_callback = &Editor::cursor_drag_motion_callback;
2097 drag_info.finished_callback = &Editor::cursor_drag_finished_callback;
2101 if ((drag_info.data = (item->get_data ("cursor"))) == 0) {
2102 fatal << _("programming error: cursor canvas item has no cursor data pointer!") << endmsg;
2106 Cursor* cursor = (Cursor *) drag_info.data;
2108 if (cursor == playhead_cursor) {
2109 _dragging_playhead = true;
2111 if (session && drag_info.was_rolling) {
2112 session->request_stop ();
2115 if (session && session->is_auditioning()) {
2116 session->cancel_audition ();
2120 drag_info.pointer_frame_offset = drag_info.grab_frame - cursor->current_frame;
2122 show_verbose_time_cursor (cursor->current_frame, 10);
2126 Editor::cursor_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2128 Cursor* cursor = (Cursor *) drag_info.data;
2129 nframes_t adjusted_frame;
2131 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2132 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2138 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2139 if (cursor == playhead_cursor) {
2140 snap_to (adjusted_frame);
2144 if (adjusted_frame == drag_info.last_pointer_frame) return;
2146 cursor->set_position (adjusted_frame);
2148 UpdateAllTransportClocks (cursor->current_frame);
2150 show_verbose_time_cursor (cursor->current_frame, 10);
2152 drag_info.last_pointer_frame = adjusted_frame;
2153 drag_info.first_move = false;
2157 Editor::cursor_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2159 if (drag_info.first_move) return;
2161 cursor_drag_motion_callback (item, event);
2163 _dragging_playhead = false;
2165 if (item == &playhead_cursor->canvas_item) {
2167 session->request_locate (playhead_cursor->current_frame, drag_info.was_rolling);
2173 Editor::update_marker_drag_item (Location *location)
2175 double x1 = frame_to_pixel (location->start());
2176 double x2 = frame_to_pixel (location->end());
2178 if (location->is_mark()) {
2179 marker_drag_line_points.front().set_x(x1);
2180 marker_drag_line_points.back().set_x(x1);
2181 marker_drag_line->property_points() = marker_drag_line_points;
2184 range_marker_drag_rect->property_x1() = x1;
2185 range_marker_drag_rect->property_x2() = x2;
2190 Editor::start_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2194 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2195 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2201 Location *location = find_location_from_marker (marker, is_start);
2203 drag_info.item = item;
2204 drag_info.data = marker;
2205 drag_info.motion_callback = &Editor::marker_drag_motion_callback;
2206 drag_info.finished_callback = &Editor::marker_drag_finished_callback;
2210 _dragging_edit_point = true;
2212 drag_info.copied_location = new Location (*location);
2213 drag_info.pointer_frame_offset = drag_info.grab_frame - (is_start ? location->start() : location->end());
2215 update_marker_drag_item (location);
2217 if (location->is_mark()) {
2218 // marker_drag_line->show();
2219 // marker_drag_line->raise_to_top();
2221 range_marker_drag_rect->show();
2222 range_marker_drag_rect->raise_to_top();
2226 show_verbose_time_cursor (location->start(), 10);
2228 show_verbose_time_cursor (location->end(), 10);
2231 Selection::Operation op = Keyboard::selection_type (event->button.state);
2234 case Selection::Toggle:
2235 selection->toggle (marker);
2237 case Selection::Set:
2238 selection->set (marker);
2240 case Selection::Extend:
2241 selection->add (marker);
2243 case Selection::Add:
2244 selection->add (marker);
2250 Editor::marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2253 Marker* marker = (Marker *) drag_info.data;
2254 Location *real_location;
2255 Location *copy_location;
2257 bool move_both = false;
2260 if (drag_info.pointer_frame_offset <= drag_info.current_pointer_frame) {
2261 newframe = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2266 nframes_t next = newframe;
2268 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2269 snap_to (newframe, 0, true);
2272 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
2276 /* call this to find out if its the start or end */
2278 if ((real_location = find_location_from_marker (marker, is_start)) == 0) {
2282 if (real_location->locked()) {
2286 /* use the copy that we're "dragging" around */
2288 copy_location = drag_info.copied_location;
2290 f_delta = copy_location->end() - copy_location->start();
2292 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2296 if (copy_location->is_mark()) {
2299 copy_location->set_start (newframe);
2303 if (is_start) { // start-of-range marker
2306 copy_location->set_start (newframe);
2307 copy_location->set_end (newframe + f_delta);
2308 } else if (newframe < copy_location->end()) {
2309 copy_location->set_start (newframe);
2311 snap_to (next, 1, true);
2312 copy_location->set_end (next);
2313 copy_location->set_start (newframe);
2316 } else { // end marker
2319 copy_location->set_end (newframe);
2320 copy_location->set_start (newframe - f_delta);
2321 } else if (newframe > copy_location->start()) {
2322 copy_location->set_end (newframe);
2324 } else if (newframe > 0) {
2325 snap_to (next, -1, true);
2326 copy_location->set_start (next);
2327 copy_location->set_end (newframe);
2332 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
2333 drag_info.first_move = false;
2335 update_marker_drag_item (copy_location);
2337 LocationMarkers* lm = find_location_markers (real_location);
2338 lm->set_position (copy_location->start(), copy_location->end());
2339 edit_point_clock.set (copy_location->start());
2341 show_verbose_time_cursor (newframe, 10);
2345 Editor::marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2347 if (drag_info.first_move) {
2348 marker_drag_motion_callback (item, event);
2352 _dragging_edit_point = false;
2354 Marker* marker = (Marker *) drag_info.data;
2357 begin_reversible_command ( _("move marker") );
2358 XMLNode &before = session->locations()->get_state();
2360 Location * location = find_location_from_marker (marker, is_start);
2364 if (location->locked()) {
2368 if (location->is_mark()) {
2369 location->set_start (drag_info.copied_location->start());
2371 location->set (drag_info.copied_location->start(), drag_info.copied_location->end());
2375 XMLNode &after = session->locations()->get_state();
2376 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
2377 commit_reversible_command ();
2379 marker_drag_line->hide();
2380 range_marker_drag_rect->hide();
2384 Editor::start_meter_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2387 MeterMarker* meter_marker;
2389 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2390 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2394 meter_marker = dynamic_cast<MeterMarker*> (marker);
2396 MetricSection& section (meter_marker->meter());
2398 if (!section.movable()) {
2402 drag_info.item = item;
2403 drag_info.copy = false;
2404 drag_info.data = marker;
2405 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2406 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2410 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2412 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2416 Editor::start_meter_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2419 MeterMarker* meter_marker;
2421 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2422 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2426 meter_marker = dynamic_cast<MeterMarker*> (marker);
2428 // create a dummy marker for visual representation of moving the copy.
2429 // The actual copying is not done before we reach the finish callback.
2431 snprintf (name, sizeof(name), "%g/%g", meter_marker->meter().beats_per_bar(), meter_marker->meter().note_divisor ());
2432 MeterMarker* new_marker = new MeterMarker(*this, *meter_group, ARDOUR_UI::config()->canvasvar_MeterMarker.get(), name,
2433 *new MeterSection(meter_marker->meter()));
2435 drag_info.item = &new_marker->the_item();
2436 drag_info.copy = true;
2437 drag_info.data = new_marker;
2438 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2439 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2443 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2445 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2449 Editor::meter_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2451 MeterMarker* marker = (MeterMarker *) drag_info.data;
2452 nframes_t adjusted_frame;
2454 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2455 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2461 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2462 snap_to (adjusted_frame);
2465 if (adjusted_frame == drag_info.last_pointer_frame) return;
2467 marker->set_position (adjusted_frame);
2470 drag_info.last_pointer_frame = adjusted_frame;
2471 drag_info.first_move = false;
2473 show_verbose_time_cursor (adjusted_frame, 10);
2477 Editor::meter_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2479 if (drag_info.first_move) return;
2481 meter_marker_drag_motion_callback (drag_info.item, event);
2483 MeterMarker* marker = (MeterMarker *) drag_info.data;
2486 TempoMap& map (session->tempo_map());
2487 map.bbt_time (drag_info.last_pointer_frame, when);
2489 if (drag_info.copy == true) {
2490 begin_reversible_command (_("copy meter mark"));
2491 XMLNode &before = map.get_state();
2492 map.add_meter (marker->meter(), when);
2493 XMLNode &after = map.get_state();
2494 session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2495 commit_reversible_command ();
2497 // delete the dummy marker we used for visual representation of copying.
2498 // a new visual marker will show up automatically.
2501 begin_reversible_command (_("move meter mark"));
2502 XMLNode &before = map.get_state();
2503 map.move_meter (marker->meter(), when);
2504 XMLNode &after = map.get_state();
2505 session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2506 commit_reversible_command ();
2511 Editor::start_tempo_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2514 TempoMarker* tempo_marker;
2516 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2517 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2521 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2522 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2526 MetricSection& section (tempo_marker->tempo());
2528 if (!section.movable()) {
2532 drag_info.item = item;
2533 drag_info.copy = false;
2534 drag_info.data = marker;
2535 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2536 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2540 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2541 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2545 Editor::start_tempo_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2548 TempoMarker* tempo_marker;
2550 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2551 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2555 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2556 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2560 // create a dummy marker for visual representation of moving the copy.
2561 // The actual copying is not done before we reach the finish callback.
2563 snprintf (name, sizeof (name), "%.2f", tempo_marker->tempo().beats_per_minute());
2564 TempoMarker* new_marker = new TempoMarker(*this, *tempo_group, ARDOUR_UI::config()->canvasvar_TempoMarker.get(), name,
2565 *new TempoSection(tempo_marker->tempo()));
2567 drag_info.item = &new_marker->the_item();
2568 drag_info.copy = true;
2569 drag_info.data = new_marker;
2570 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2571 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2575 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2577 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2581 Editor::tempo_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2583 TempoMarker* marker = (TempoMarker *) drag_info.data;
2584 nframes_t adjusted_frame;
2586 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2587 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2593 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2594 snap_to (adjusted_frame);
2597 if (adjusted_frame == drag_info.last_pointer_frame) return;
2599 /* OK, we've moved far enough to make it worth actually move the thing. */
2601 marker->set_position (adjusted_frame);
2603 show_verbose_time_cursor (adjusted_frame, 10);
2605 drag_info.last_pointer_frame = adjusted_frame;
2606 drag_info.first_move = false;
2610 Editor::tempo_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2612 if (drag_info.first_move) return;
2614 tempo_marker_drag_motion_callback (drag_info.item, event);
2616 TempoMarker* marker = (TempoMarker *) drag_info.data;
2619 TempoMap& map (session->tempo_map());
2620 map.bbt_time (drag_info.last_pointer_frame, when);
2622 if (drag_info.copy == true) {
2623 begin_reversible_command (_("copy tempo mark"));
2624 XMLNode &before = map.get_state();
2625 map.add_tempo (marker->tempo(), when);
2626 XMLNode &after = map.get_state();
2627 session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2628 commit_reversible_command ();
2630 // delete the dummy marker we used for visual representation of copying.
2631 // a new visual marker will show up automatically.
2634 begin_reversible_command (_("move tempo mark"));
2635 XMLNode &before = map.get_state();
2636 map.move_tempo (marker->tempo(), when);
2637 XMLNode &after = map.get_state();
2638 session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2639 commit_reversible_command ();
2644 Editor::remove_gain_control_point (ArdourCanvas::Item*item, GdkEvent* event)
2646 ControlPoint* control_point;
2648 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2649 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2653 // We shouldn't remove the first or last gain point
2654 if (control_point->line().is_last_point(*control_point) ||
2655 control_point->line().is_first_point(*control_point)) {
2659 control_point->line().remove_point (*control_point);
2663 Editor::remove_control_point (ArdourCanvas::Item* item, GdkEvent* event)
2665 ControlPoint* control_point;
2667 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2668 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2672 control_point->line().remove_point (*control_point);
2676 Editor::start_control_point_grab (ArdourCanvas::Item* item, GdkEvent* event)
2678 ControlPoint* control_point;
2680 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2681 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2685 drag_info.item = item;
2686 drag_info.data = control_point;
2687 drag_info.motion_callback = &Editor::control_point_drag_motion_callback;
2688 drag_info.finished_callback = &Editor::control_point_drag_finished_callback;
2690 start_grab (event, fader_cursor);
2692 // start the grab at the center of the control point so
2693 // the point doesn't 'jump' to the mouse after the first drag
2694 drag_info.grab_x = control_point->get_x();
2695 drag_info.grab_y = control_point->get_y();
2696 control_point->line().parent_group().i2w(drag_info.grab_x, drag_info.grab_y);
2697 track_canvas.w2c(drag_info.grab_x, drag_info.grab_y, drag_info.grab_x, drag_info.grab_y);
2699 drag_info.grab_frame = pixel_to_frame(drag_info.grab_x);
2701 control_point->line().start_drag (control_point, drag_info.grab_frame, 0);
2703 double fraction = 1.0 - ((control_point->get_y() - control_point->line().y_position()) / (double)control_point->line().height());
2704 set_verbose_canvas_cursor (control_point->line().get_verbose_cursor_string (fraction),
2705 drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
2707 show_verbose_canvas_cursor ();
2711 Editor::control_point_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2713 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2715 double dx = drag_info.current_pointer_x - drag_info.last_pointer_x;
2716 double dy = drag_info.current_pointer_y - drag_info.last_pointer_y;
2718 if (event->button.state & Keyboard::SecondaryModifier) {
2723 double cx = drag_info.grab_x + drag_info.cumulative_x_drag + dx;
2724 double cy = drag_info.grab_y + drag_info.cumulative_y_drag + dy;
2726 // calculate zero crossing point. back off by .01 to stay on the
2727 // positive side of zero
2729 double zero_gain_y = (1.0 - ZERO_GAIN_FRACTION) * cp->line().height() - .01;
2730 cp->line().parent_group().i2w(_unused, zero_gain_y);
2732 // make sure we hit zero when passing through
2733 if ((cy < zero_gain_y and (cy - dy) > zero_gain_y)
2734 or (cy > zero_gain_y and (cy - dy) < zero_gain_y)) {
2738 if (drag_info.x_constrained) {
2739 cx = drag_info.grab_x;
2741 if (drag_info.y_constrained) {
2742 cy = drag_info.grab_y;
2745 drag_info.cumulative_x_drag = cx - drag_info.grab_x;
2746 drag_info.cumulative_y_drag = cy - drag_info.grab_y;
2748 cp->line().parent_group().w2i (cx, cy);
2752 cy = min ((double) (cp->line().y_position() + cp->line().height()), cy);
2754 //translate cx to frames
2755 nframes_t cx_frames = unit_to_frame (cx);
2757 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier()) && !drag_info.x_constrained) {
2758 snap_to (cx_frames);
2761 const double fraction = 1.0 - ((cy - cp->line().y_position()) / (double)cp->line().height());
2765 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) {
2771 cp->line().point_drag (*cp, cx_frames , fraction, push);
2773 set_verbose_canvas_cursor_text (cp->line().get_verbose_cursor_string (fraction));
2775 drag_info.first_move = false;
2779 Editor::control_point_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2781 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2783 if (drag_info.first_move) {
2787 if ((event->type == GDK_BUTTON_RELEASE) && (event->button.button == 1) && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2788 reset_point_selection ();
2792 control_point_drag_motion_callback (item, event);
2794 cp->line().end_drag (cp);
2798 Editor::start_line_grab_from_regionview (ArdourCanvas::Item* item, GdkEvent* event)
2800 switch (mouse_mode) {
2802 assert(dynamic_cast<AudioRegionView*>(clicked_regionview));
2803 start_line_grab (dynamic_cast<AudioRegionView*>(clicked_regionview)->get_gain_line(), event);
2811 Editor::start_line_grab_from_line (ArdourCanvas::Item* item, GdkEvent* event)
2815 if ((al = reinterpret_cast<AutomationLine*> (item->get_data ("line"))) == 0) {
2816 fatal << _("programming error: line canvas item has no line pointer!") << endmsg;
2820 start_line_grab (al, event);
2824 Editor::start_line_grab (AutomationLine* line, GdkEvent* event)
2828 nframes_t frame_within_region;
2830 /* need to get x coordinate in terms of parent (TimeAxisItemView)
2834 cx = event->button.x;
2835 cy = event->button.y;
2836 line->parent_group().w2i (cx, cy);
2837 frame_within_region = (nframes_t) floor (cx * frames_per_unit);
2839 if (!line->control_points_adjacent (frame_within_region, current_line_drag_info.before,
2840 current_line_drag_info.after)) {
2841 /* no adjacent points */
2845 drag_info.item = &line->grab_item();
2846 drag_info.data = line;
2847 drag_info.motion_callback = &Editor::line_drag_motion_callback;
2848 drag_info.finished_callback = &Editor::line_drag_finished_callback;
2850 start_grab (event, fader_cursor);
2852 const double fraction = 1.0 - ((cy - line->y_position()) / (double)line->height());
2854 line->start_drag (0, drag_info.grab_frame, fraction);
2856 set_verbose_canvas_cursor (line->get_verbose_cursor_string (fraction),
2857 drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
2858 show_verbose_canvas_cursor ();
2862 Editor::line_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2864 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
2866 double dy = drag_info.current_pointer_y - drag_info.last_pointer_y;
2868 if (event->button.state & Keyboard::SecondaryModifier) {
2872 double cx = drag_info.current_pointer_x;
2873 double cy = drag_info.grab_y + drag_info.cumulative_y_drag + dy;
2875 // calculate zero crossing point. back off by .01 to stay on the
2876 // positive side of zero
2878 double zero_gain_y = (1.0 - ZERO_GAIN_FRACTION) * line->height() - .01;
2879 line->parent_group().i2w(_unused, zero_gain_y);
2881 // make sure we hit zero when passing through
2882 if ((cy < zero_gain_y and (cy - dy) > zero_gain_y)
2883 or (cy > zero_gain_y and (cy - dy) < zero_gain_y)) {
2887 drag_info.cumulative_y_drag = cy - drag_info.grab_y;
2889 line->parent_group().w2i (cx, cy);
2892 cy = min ((double) line->height(), cy);
2894 const double fraction = 1.0 - ((cy - line->y_position()) / (double)line->height());
2898 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) {
2904 line->line_drag (current_line_drag_info.before, current_line_drag_info.after, fraction, push);
2906 set_verbose_canvas_cursor_text (line->get_verbose_cursor_string (fraction));
2910 Editor::line_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2912 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
2913 line_drag_motion_callback (item, event);
2918 Editor::start_region_grab (ArdourCanvas::Item* item, GdkEvent* event)
2920 if (selection->regions.empty() || clicked_regionview == 0) {
2924 drag_info.copy = false;
2925 drag_info.item = item;
2926 drag_info.data = clicked_regionview;
2928 if (Config->get_edit_mode() == Splice) {
2929 drag_info.motion_callback = &Editor::region_drag_splice_motion_callback;
2930 drag_info.finished_callback = &Editor::region_drag_splice_finished_callback;
2932 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2933 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2939 TimeAxisView* tvp = clicked_axisview;
2940 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2942 if (tv && tv->is_track()) {
2943 speed = tv->get_diskstream()->speed();
2946 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2947 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2948 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2949 // we want a move threshold
2950 drag_info.want_move_threshold = true;
2952 show_verbose_time_cursor (drag_info.last_frame_position, 10);
2954 begin_reversible_command (_("move region(s)"));
2958 Editor::start_create_region_grab (ArdourCanvas::Item* item, GdkEvent* event)
2960 drag_info.copy = false;
2961 drag_info.item = item;
2962 drag_info.data = clicked_axisview;
2963 drag_info.last_trackview = clicked_axisview;
2964 drag_info.motion_callback = &Editor::create_region_drag_motion_callback;
2965 drag_info.finished_callback = &Editor::create_region_drag_finished_callback;
2971 Editor::start_region_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2973 if (selection->regions.empty() || clicked_regionview == 0) {
2977 drag_info.copy = true;
2978 drag_info.item = item;
2979 drag_info.data = clicked_regionview;
2983 TimeAxisView* tv = &clicked_regionview->get_time_axis_view();
2984 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
2987 if (rtv && rtv->is_track()) {
2988 speed = rtv->get_diskstream()->speed();
2991 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2992 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2993 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2994 // we want a move threshold
2995 drag_info.want_move_threshold = true;
2996 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2997 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2998 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3002 Editor::start_region_brush_grab (ArdourCanvas::Item* item, GdkEvent* event)
3004 if (selection->regions.empty() || clicked_regionview == 0 || Config->get_edit_mode() == Splice) {
3008 drag_info.copy = false;
3009 drag_info.item = item;
3010 drag_info.data = clicked_regionview;
3011 drag_info.motion_callback = &Editor::region_drag_motion_callback;
3012 drag_info.finished_callback = &Editor::region_drag_finished_callback;
3017 TimeAxisView* tvp = clicked_axisview;
3018 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
3020 if (tv && tv->is_track()) {
3021 speed = tv->get_diskstream()->speed();
3024 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
3025 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
3026 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
3027 // we want a move threshold
3028 drag_info.want_move_threshold = true;
3029 drag_info.brushing = true;
3031 begin_reversible_command (_("Drag region brush"));
3035 Editor::possibly_copy_regions_during_grab (GdkEvent* event)
3037 if (drag_info.copy && drag_info.move_threshold_passed && drag_info.want_move_threshold) {
3039 drag_info.want_move_threshold = false; // don't copy again
3041 /* duplicate the region(s) */
3043 vector<RegionView*> new_regionviews;
3045 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3052 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
3053 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
3056 nrv = new AudioRegionView (*arv);
3058 nrv = new MidiRegionView (*mrv);
3063 nrv->get_canvas_group()->show ();
3065 new_regionviews.push_back (nrv);
3068 if (new_regionviews.empty()) {
3072 /* reset selection to new regionviews */
3074 selection->set (new_regionviews);
3076 /* reset drag_info data to reflect the fact that we are dragging the copies */
3078 drag_info.data = new_regionviews.front();
3080 swap_grab (new_regionviews.front()->get_canvas_group (), 0, event->motion.time);
3085 Editor::check_region_drag_possible (RouteTimeAxisView** tv)
3087 /* Which trackview is this ? */
3089 TimeAxisView* tvp = trackview_by_y_position (drag_info.current_pointer_y);
3090 (*tv) = dynamic_cast<RouteTimeAxisView*>(tvp);
3092 /* The region motion is only processed if the pointer is over
3096 if (!(*tv) || !(*tv)->is_track()) {
3097 /* To make sure we hide the verbose canvas cursor when the mouse is
3098 not held over and audiotrack.
3100 hide_verbose_canvas_cursor ();
3107 struct RegionSelectionByPosition {
3108 bool operator() (RegionView*a, RegionView* b) {
3109 return a->region()->position () < b->region()->position();
3114 Editor::region_drag_splice_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
3116 RouteTimeAxisView* tv;
3118 if (!check_region_drag_possible (&tv)) {
3122 if (!drag_info.move_threshold_passed) {
3128 if (drag_info.current_pointer_x - drag_info.grab_x > 0) {
3134 RegionSelection copy (selection->regions);
3136 RegionSelectionByPosition cmp;
3139 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
3141 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
3147 boost::shared_ptr<Playlist> playlist;
3149 if ((playlist = atv->playlist()) == 0) {
3153 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
3158 if (drag_info.current_pointer_frame < (*i)->region()->last_frame() + 1) {
3162 if (drag_info.current_pointer_frame > (*i)->region()->first_frame()) {
3168 playlist->shuffle ((*i)->region(), dir);
3170 drag_info.grab_x = drag_info.current_pointer_x;
3175 Editor::region_drag_splice_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3180 Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
3184 RegionView* rv = reinterpret_cast<RegionView*> (drag_info.data);
3185 nframes_t pending_region_position = 0;
3186 int32_t pointer_y_span = 0, canvas_pointer_y_span = 0, original_pointer_order;
3187 int32_t visible_y_high = 0, visible_y_low = 512; //high meaning higher numbered.. not the height on the screen
3188 bool clamp_y_axis = false;
3189 vector<int32_t> height_list(512) ;
3190 vector<int32_t>::iterator j;
3191 RouteTimeAxisView* tv;
3193 possibly_copy_regions_during_grab (event);
3195 if (!check_region_drag_possible (&tv)) {
3199 original_pointer_order = drag_info.last_trackview->order;
3201 /************************************************************
3203 ************************************************************/
3205 if (drag_info.brushing) {
3206 clamp_y_axis = true;
3211 if ((pointer_y_span = (drag_info.last_trackview->order - tv->order)) != 0) {
3213 int32_t children = 0, numtracks = 0;
3214 // XXX hard coding track limit, oh my, so very very bad
3215 bitset <1024> tracks (0x00);
3216 /* get a bitmask representing the visible tracks */
3218 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
3219 TimeAxisView *tracklist_timeview;
3220 tracklist_timeview = (*i);
3221 RouteTimeAxisView* rtv2 = dynamic_cast<RouteTimeAxisView*>(tracklist_timeview);
3222 TimeAxisView::Children children_list;
3224 /* zeroes are audio tracks. ones are other types. */
3226 if (!rtv2->hidden()) {
3228 if (visible_y_high < rtv2->order) {
3229 visible_y_high = rtv2->order;
3231 if (visible_y_low > rtv2->order) {
3232 visible_y_low = rtv2->order;
3235 if (!rtv2->is_track()) {
3236 tracks = tracks |= (0x01 << rtv2->order);
3239 height_list[rtv2->order] = (*i)->height;
3241 if ((children_list = rtv2->get_child_list()).size() > 0) {
3242 for (TimeAxisView::Children::iterator j = children_list.begin(); j != children_list.end(); ++j) {
3243 tracks = tracks |= (0x01 << (rtv2->order + children));
3244 height_list[rtv2->order + children] = (*j)->height;
3252 /* find the actual span according to the canvas */
3254 canvas_pointer_y_span = pointer_y_span;
3255 if (drag_info.last_trackview->order >= tv->order) {
3257 for (y = tv->order; y < drag_info.last_trackview->order; y++) {
3258 if (height_list[y] == 0 ) {
3259 canvas_pointer_y_span--;
3264 for (y = drag_info.last_trackview->order;y <= tv->order; y++) {
3265 if ( height_list[y] == 0 ) {
3266 canvas_pointer_y_span++;
3271 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3272 RegionView* rv2 = (*i);
3273 double ix1, ix2, iy1, iy2;
3276 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3277 rv2->get_canvas_group()->i2w (ix1, iy1);
3278 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3279 RouteTimeAxisView* rtv2 = dynamic_cast<RouteTimeAxisView*>(tvp2);
3281 if (rtv2->order != original_pointer_order) {
3282 /* this isn't the pointer track */
3284 if (canvas_pointer_y_span > 0) {
3286 /* moving up the canvas */
3287 if ((rtv2->order - canvas_pointer_y_span) >= visible_y_low) {
3289 int32_t visible_tracks = 0;
3290 while (visible_tracks < canvas_pointer_y_span ) {
3293 while (height_list[rtv2->order - (visible_tracks - n)] == 0) {
3294 /* we're passing through a hidden track */
3299 if (tracks[rtv2->order - (canvas_pointer_y_span - n)] != 0x00) {
3300 clamp_y_axis = true;
3304 clamp_y_axis = true;
3307 } else if (canvas_pointer_y_span < 0) {
3309 /*moving down the canvas*/
3311 if ((rtv2->order - (canvas_pointer_y_span - n)) <= visible_y_high) { // we will overflow
3314 int32_t visible_tracks = 0;
3316 while (visible_tracks > canvas_pointer_y_span ) {
3319 while (height_list[rtv2->order - (visible_tracks - n)] == 0) {
3323 if ( tracks[rtv2->order - ( canvas_pointer_y_span - n)] != 0x00) {
3324 clamp_y_axis = true;
3329 clamp_y_axis = true;
3335 /* this is the pointer's track */
3336 if ((rtv2->order - pointer_y_span) > visible_y_high) { // we will overflow
3337 clamp_y_axis = true;
3338 } else if ((rtv2->order - pointer_y_span) < visible_y_low) { // we will underflow
3339 clamp_y_axis = true;
3347 } else if (drag_info.last_trackview == tv) {
3348 clamp_y_axis = true;
3352 if (!clamp_y_axis) {
3353 drag_info.last_trackview = tv;
3356 /************************************************************
3358 ************************************************************/
3360 /* compute the amount of pointer motion in frames, and where
3361 the region would be if we moved it by that much.
3364 if (drag_info.move_threshold_passed) {
3366 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
3368 nframes_t sync_frame;
3369 nframes_t sync_offset;
3372 pending_region_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
3374 sync_offset = rv->region()->sync_offset (sync_dir);
3375 sync_frame = rv->region()->adjust_to_sync (pending_region_position);
3377 /* we snap if the snap modifier is not enabled.
3380 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3381 snap_to (sync_frame);
3384 if (sync_frame - sync_offset <= sync_frame) {
3385 pending_region_position = sync_frame + (sync_dir*sync_offset);
3387 pending_region_position = 0;
3391 pending_region_position = 0;
3394 if (pending_region_position > max_frames - rv->region()->length()) {
3395 pending_region_position = drag_info.last_frame_position;
3398 // printf ("3: pending_region_position= %lu %lu\n", pending_region_position, drag_info.last_frame_position );
3400 if (pending_region_position != drag_info.last_frame_position && !drag_info.x_constrained) {
3402 /* now compute the canvas unit distance we need to move the regionview
3403 to make it appear at the new location.
3406 if (pending_region_position > drag_info.last_frame_position) {
3407 x_delta = ((double) (pending_region_position - drag_info.last_frame_position) / frames_per_unit);
3409 x_delta = -((double) (drag_info.last_frame_position - pending_region_position) / frames_per_unit);
3412 drag_info.last_frame_position = pending_region_position;
3419 /* threshold not passed */
3424 /*************************************************************
3426 ************************************************************/
3428 if (x_delta == 0 && (pointer_y_span == 0)) {
3429 /* haven't reached next snap point, and we're not switching
3430 trackviews. nothing to do.
3437 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3439 RegionView* rv2 = (*i);
3441 // If any regionview is at zero, we need to know so we can stop further leftward motion.
3443 double ix1, ix2, iy1, iy2;
3444 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3445 rv2->get_canvas_group()->i2w (ix1, iy1);
3454 /*************************************************************
3456 ************************************************************/
3460 if (drag_info.first_move) {
3461 if (drag_info.move_threshold_passed) {
3472 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
3473 const list<RegionView*>& layered_regions = selection->regions.by_layer();
3475 for (list<RegionView*>::const_iterator i = layered_regions.begin(); i != layered_regions.end(); ++i) {
3477 RegionView* rv = (*i);
3478 double ix1, ix2, iy1, iy2;
3479 int32_t temp_pointer_y_span = pointer_y_span;
3481 /* get item BBox, which will be relative to parent. so we have
3482 to query on a child, then convert to world coordinates using
3486 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3487 rv->get_canvas_group()->i2w (ix1, iy1);
3488 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3489 RouteTimeAxisView* canvas_rtv = dynamic_cast<RouteTimeAxisView*>(tvp2);
3490 RouteTimeAxisView* temp_rtv;
3492 if ((pointer_y_span != 0) && !clamp_y_axis) {
3495 for (j = height_list.begin(); j!= height_list.end(); j++) {
3496 if (x == canvas_rtv->order) {
3497 /* we found the track the region is on */
3498 if (x != original_pointer_order) {
3499 /*this isn't from the same track we're dragging from */
3500 temp_pointer_y_span = canvas_pointer_y_span;
3502 while (temp_pointer_y_span > 0) {
3503 /* we're moving up canvas-wise,
3504 so we need to find the next track height
3506 if (j != height_list.begin()) {
3509 if (x != original_pointer_order) {
3510 /* we're not from the dragged track, so ignore hidden tracks. */
3512 temp_pointer_y_span++;
3516 temp_pointer_y_span--;
3518 while (temp_pointer_y_span < 0) {
3520 if (x != original_pointer_order) {
3522 temp_pointer_y_span--;
3526 if (j != height_list.end()) {
3529 temp_pointer_y_span++;
3531 /* find out where we'll be when we move and set height accordingly */
3533 tvp2 = trackview_by_y_position (iy1 + y_delta);
3534 temp_rtv = dynamic_cast<RouteTimeAxisView*>(tvp2);
3535 rv->set_y_position_and_height (0, temp_rtv->height);
3537 /* if you un-comment the following, the region colours will follow the track colours whilst dragging,
3538 personally, i think this can confuse things, but never mind.
3541 //const GdkColor& col (temp_rtv->view->get_region_color());
3542 //rv->set_color (const_cast<GdkColor&>(col));
3549 /* prevent the regionview from being moved to before
3550 the zero position on the canvas.
3555 if (-x_delta > ix1) {
3558 } else if ((x_delta > 0) && (rv->region()->last_frame() > max_frames - x_delta)) {
3559 x_delta = max_frames - rv->region()->last_frame();
3563 if (drag_info.first_move) {
3565 /* hide any dependent views */
3567 rv->get_time_axis_view().hide_dependent_views (*rv);
3569 /* this is subtle. raising the regionview itself won't help,
3570 because raise_to_top() just puts the item on the top of
3571 its parent's stack. so, we need to put the trackview canvas_display group
3572 on the top, since its parent is the whole canvas.
3575 rv->get_canvas_group()->raise_to_top();
3576 rv->get_time_axis_view().canvas_display->raise_to_top();
3577 cursor_group->raise_to_top();
3579 rv->fake_set_opaque (true);
3582 if (drag_info.brushing) {
3583 mouse_brush_insert_region (rv, pending_region_position);
3585 rv->move (x_delta, y_delta);
3588 } /* foreach region */
3592 if (drag_info.first_move && drag_info.move_threshold_passed) {
3593 cursor_group->raise_to_top();
3594 drag_info.first_move = false;
3597 if (x_delta != 0 && !drag_info.brushing) {
3598 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3603 Editor::region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3606 RegionView* rvdi = reinterpret_cast<RegionView *> (drag_info.data);
3607 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
3608 bool nocommit = true;
3610 RouteTimeAxisView* rtv;
3611 bool regionview_y_movement;
3612 bool regionview_x_movement;
3613 vector<RegionView*> copies;
3614 list <boost::shared_ptr<Playlist > > used_playlists;
3615 list <sigc::connection > used_connections;
3616 bool preserve_selection = false;
3618 /* first_move is set to false if the regionview has been moved in the
3622 if (drag_info.first_move) {
3629 /* The regionview has been moved at some stage during the grab so we need
3630 to account for any mouse movement between this event and the last one.
3633 region_drag_motion_callback (item, event);
3635 if (Config->get_edit_mode() == Splice && !pre_drag_region_selection.empty()) {
3636 selection->set (pre_drag_region_selection);
3637 pre_drag_region_selection.clear ();
3640 if (drag_info.brushing) {
3641 /* all changes were made during motion event handlers */
3643 if (drag_info.copy) {
3644 for (list<RegionView*>::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
3645 copies.push_back (*i);
3652 /* adjust for track speed */
3655 rtv = dynamic_cast<RouteTimeAxisView*> (drag_info.last_trackview);
3656 if (rtv && rtv->get_diskstream()) {
3657 speed = rtv->get_diskstream()->speed();
3660 regionview_x_movement = (drag_info.last_frame_position != (nframes_t) (rvdi->region()->position()/speed));
3661 regionview_y_movement = (drag_info.last_trackview != &rvdi->get_time_axis_view());
3663 //printf ("last_frame: %s position is %lu %g\n", rv->get_time_axis_view().name().c_str(), drag_info.last_frame_position, speed);
3664 //printf ("last_rackview: %s \n", drag_info.last_trackview->name().c_str());
3668 if (drag_info.copy) {
3669 if (drag_info.x_constrained) {
3670 op_string = _("fixed time region copy");
3672 op_string = _("region copy");
3675 if (drag_info.x_constrained) {
3676 op_string = _("fixed time region drag");
3678 op_string = _("region drag");
3682 begin_reversible_command (op_string);
3684 if (regionview_y_movement) {
3686 /* moved to a different track. */
3688 vector<RegionView*> new_selection;
3690 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ) {
3692 RegionView* rv = (*i);
3694 double ix1, ix2, iy1, iy2;
3696 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3697 rv->get_canvas_group()->i2w (ix1, iy1);
3699 RouteTimeAxisView* rtv2 = dynamic_cast<RouteTimeAxisView*>(trackview_by_y_position (iy1));
3701 boost::shared_ptr<Playlist> from_playlist = rv->region()->playlist();
3702 boost::shared_ptr<Playlist> to_playlist = rtv2->playlist();
3704 where = (nframes_t) (unit_to_frame (ix1) * speed);
3705 boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region()));
3707 if (! to_playlist->frozen()) {
3709 we haven't seen this playlist before.
3710 we want to freeze it because we don't want to relayer per-region.
3711 its much better to do that just once if the playlist is large.
3715 connect so the selection is changed when the new regionview finally appears (after thaw).
3716 keep track of it so we can disconnect later.
3719 sigc::connection c = rtv2->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_and_select_new_region_view));
3720 used_connections.push_back (c);
3723 session->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
3725 /* remember used playlists so we can thaw them later */
3726 used_playlists.push_back(to_playlist);
3727 to_playlist->freeze();
3730 /* undo the previous hide_dependent_views so that xfades don't
3731 disappear on copying regions
3734 rv->get_time_axis_view().reveal_dependent_views (*rv);
3736 if (!drag_info.copy) {
3738 /* the region that used to be in the old playlist is not
3739 moved to the new one - we make a copy of it. as a result,
3740 any existing editor for the region should no longer be
3744 rv->hide_region_editor();
3745 rv->fake_set_opaque (false);
3747 session->add_command (new MementoCommand<Playlist>(*from_playlist, &from_playlist->get_state(), 0));
3748 from_playlist->remove_region ((rv->region()));
3749 session->add_command (new MementoCommand<Playlist>(*from_playlist, 0, &from_playlist->get_state()));
3753 /* the regionview we dragged around is a temporary copy, queue it for deletion */
3755 copies.push_back (rv);
3758 latest_regionviews.clear ();
3759 sigc::connection c = rtv2->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3760 session->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
3762 to_playlist->add_region (new_region, where);
3763 session->add_command (new MementoCommand<Playlist>(*to_playlist, 0, &to_playlist->get_state()));
3766 if (!latest_regionviews.empty()) {
3767 new_selection.insert (new_selection.end(), latest_regionviews.begin(), latest_regionviews.end());
3770 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
3771 was selected in all of them, then removing it from the playlist will have removed all
3772 trace of it from the selection (i.e. there were N regions selected, we removed 1,
3773 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
3774 corresponding regionview, and the selection is now empty).
3776 this could have invalidated any and all iterators into the region selection.
3778 the heuristic we use here is: if the region selection is empty, break out of the loop
3779 here. if the region selection is not empty, then restart the loop because we know that
3780 we must have removed at least the region(view) we've just been working on as well as any
3781 that we processed on previous iterations.
3783 EXCEPT .... if we are doing a copy drag, then the selection hasn't been modified and
3784 we can just iterate.
3788 if (drag_info.copy) {
3791 if (selection->regions.empty()) {
3795 XXX see above .. but we just froze the playlists.. we have to keep iterating, right?
3798 //i = selection->regions.by_layer().begin();
3806 /* motion within a single track */
3808 list<RegionView*> regions = selection->regions.by_layer();
3810 for (list<RegionView*>::iterator i = regions.begin(); i != regions.end(); ++i) {
3812 RegionView* rv = (*i);
3813 boost::shared_ptr<Playlist> to_playlist = (*i)->region()->playlist();
3814 RouteTimeAxisView* from_rtv = dynamic_cast<RouteTimeAxisView*> (&(rv->get_time_axis_view()));
3816 if (!rv->region()->can_move()) {
3820 if (regionview_x_movement) {
3821 double ownspeed = 1.0;
3823 if (from_rtv && from_rtv->get_diskstream()) {
3824 ownspeed = from_rtv->get_diskstream()->speed();
3827 /* base the new region position on the current position of the regionview.*/
3829 double ix1, ix2, iy1, iy2;
3831 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3832 rv->get_canvas_group()->i2w (ix1, iy1);
3833 where = (nframes_t) (unit_to_frame (ix1) * ownspeed);
3837 where = rv->region()->position();
3840 if (! to_playlist->frozen()) {
3841 sigc::connection c = from_rtv->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_and_select_new_region_view));
3842 used_connections.push_back (c);
3845 session->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
3847 used_playlists.push_back(to_playlist);
3848 to_playlist->freeze();
3851 if (drag_info.copy) {
3853 boost::shared_ptr<Region> newregion;
3854 boost::shared_ptr<Region> ar;
3855 boost::shared_ptr<Region> mr;
3857 if ((ar = boost::dynamic_pointer_cast<AudioRegion>(rv->region())) != 0) {
3858 newregion = RegionFactory::create (ar);
3859 } else if ((mr = boost::dynamic_pointer_cast<MidiRegion>(rv->region())) != 0) {
3860 newregion = RegionFactory::create (mr);
3865 latest_regionviews.clear ();
3866 sigc::connection c = rtv->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3867 to_playlist->add_region (newregion, (nframes_t) (where * rtv->get_diskstream()->speed()));
3870 if (!latest_regionviews.empty()) {
3871 // XXX why just the first one ? we only expect one
3872 rtv->reveal_dependent_views (*latest_regionviews.front());
3873 selection->add (latest_regionviews);
3878 /* just change the model */
3880 rv->region()->set_position (where, (void*) this);
3881 preserve_selection = true;
3888 if (! preserve_selection) {
3889 //selection->clear_regions();
3891 while (used_playlists.size() > 0) {
3893 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
3896 if (used_connections.size()) {
3897 sigc::connection c = used_connections.front();
3899 used_connections.pop_front();
3903 session->add_command (new MementoCommand<Playlist>(*(*i), 0, &(*i)->get_state()));
3904 used_playlists.pop_front();
3910 commit_reversible_command ();
3913 for (vector<RegionView*>::iterator x = copies.begin(); x != copies.end(); ++x) {
3920 Editor::create_region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
3922 if (drag_info.move_threshold_passed) {
3923 if (drag_info.first_move) {
3924 // TODO: create region-create-drag region view here
3925 drag_info.first_move = false;
3928 // TODO: resize region-create-drag region view here
3933 Editor::create_region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3935 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (drag_info.last_trackview);
3939 const boost::shared_ptr<MidiDiskstream> diskstream =
3940 boost::dynamic_pointer_cast<MidiDiskstream>(mtv->view()->trackview().track()->diskstream());
3943 warning << "Cannot create non-MIDI region" << endl;
3947 if (drag_info.first_move) {
3948 begin_reversible_command (_("create region"));
3949 XMLNode &before = mtv->playlist()->get_state();
3951 nframes_t start = drag_info.grab_frame;
3952 snap_to (start, -1);
3953 const Meter& m = session->tempo_map().meter_at(start);
3954 const Tempo& t = session->tempo_map().tempo_at(start);
3955 double length = floor (m.frames_per_bar(t, session->frame_rate()));
3957 boost::shared_ptr<Source> src = session->create_midi_source_for_session(*diskstream.get());
3959 mtv->playlist()->add_region (boost::dynamic_pointer_cast<MidiRegion>
3960 (RegionFactory::create(src, 0, (nframes_t) length,
3961 PBD::basename_nosuffix(src->name()))), start);
3962 XMLNode &after = mtv->playlist()->get_state();
3963 session->add_command(new MementoCommand<Playlist>(*mtv->playlist().get(), &before, &after));
3964 commit_reversible_command();
3967 create_region_drag_motion_callback (item, event);
3968 // TODO: create region-create-drag region here
3973 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
3975 /* Either add to or set the set the region selection, unless
3976 this is an alignment click (control used)
3979 if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
3980 TimeAxisView* tv = &rv.get_time_axis_view();
3981 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
3983 if (rtv && rtv->is_track()) {
3984 speed = rtv->get_diskstream()->speed();
3987 nframes64_t where = get_preferred_edit_position();
3991 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
3993 align_region (rv.region(), SyncPoint, (nframes_t) (where * speed));
3995 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
3997 align_region (rv.region(), End, (nframes_t) (where * speed));
4001 align_region (rv.region(), Start, (nframes_t) (where * speed));
4008 Editor::show_verbose_time_cursor (nframes_t frame, double offset, double xpos, double ypos)
4014 nframes_t frame_rate;
4021 switch (Profile->get_small_screen() ? ARDOUR_UI::instance()->primary_clock.mode () : ARDOUR_UI::instance()->secondary_clock.mode ()) {
4022 case AudioClock::BBT:
4023 session->bbt_time (frame, bbt);
4024 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, bbt.bars, bbt.beats, bbt.ticks);
4027 case AudioClock::SMPTE:
4028 session->smpte_time (frame, smpte);
4029 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
4032 case AudioClock::MinSec:
4033 /* XXX this is copied from show_verbose_duration_cursor() */
4034 frame_rate = session->frame_rate();
4035 hours = frame / (frame_rate * 3600);
4036 frame = frame % (frame_rate * 3600);
4037 mins = frame / (frame_rate * 60);
4038 frame = frame % (frame_rate * 60);
4039 secs = (float) frame / (float) frame_rate;
4040 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
4044 snprintf (buf, sizeof(buf), "%u", frame);
4048 if (xpos >= 0 && ypos >=0) {
4049 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
4052 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
4054 show_verbose_canvas_cursor ();
4058 Editor::show_verbose_duration_cursor (nframes_t start, nframes_t end, double offset, double xpos, double ypos)
4065 nframes_t distance, frame_rate;
4067 Meter meter_at_start(session->tempo_map().meter_at(start));
4073 switch (ARDOUR_UI::instance()->secondary_clock.mode ()) {
4074 case AudioClock::BBT:
4075 session->bbt_time (start, sbbt);
4076 session->bbt_time (end, ebbt);
4079 /* XXX this computation won't work well if the
4080 user makes a selection that spans any meter changes.
4083 ebbt.bars -= sbbt.bars;
4084 if (ebbt.beats >= sbbt.beats) {
4085 ebbt.beats -= sbbt.beats;
4088 ebbt.beats = int(meter_at_start.beats_per_bar()) + ebbt.beats - sbbt.beats;
4090 if (ebbt.ticks >= sbbt.ticks) {
4091 ebbt.ticks -= sbbt.ticks;
4094 ebbt.ticks = int(Meter::ticks_per_beat) + ebbt.ticks - sbbt.ticks;
4097 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, ebbt.bars, ebbt.beats, ebbt.ticks);
4100 case AudioClock::SMPTE:
4101 session->smpte_duration (end - start, smpte);
4102 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
4105 case AudioClock::MinSec:
4106 /* XXX this stuff should be elsewhere.. */
4107 distance = end - start;
4108 frame_rate = session->frame_rate();
4109 hours = distance / (frame_rate * 3600);
4110 distance = distance % (frame_rate * 3600);
4111 mins = distance / (frame_rate * 60);
4112 distance = distance % (frame_rate * 60);
4113 secs = (float) distance / (float) frame_rate;
4114 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
4118 snprintf (buf, sizeof(buf), "%u", end - start);
4122 if (xpos >= 0 && ypos >=0) {
4123 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
4126 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
4128 show_verbose_canvas_cursor ();
4132 Editor::collect_new_region_view (RegionView* rv)
4134 latest_regionviews.push_back (rv);
4138 Editor::collect_and_select_new_region_view (RegionView* rv)
4141 latest_regionviews.push_back (rv);
4145 Editor::start_selection_grab (ArdourCanvas::Item* item, GdkEvent* event)
4147 if (clicked_regionview == 0) {
4151 /* lets try to create new Region for the selection */
4153 vector<boost::shared_ptr<AudioRegion> > new_regions;
4154 create_region_from_selection (new_regions);
4156 if (new_regions.empty()) {
4160 /* XXX fix me one day to use all new regions */
4162 boost::shared_ptr<Region> region (new_regions.front());
4164 /* add it to the current stream/playlist.
4166 tricky: the streamview for the track will add a new regionview. we will
4167 catch the signal it sends when it creates the regionview to
4168 set the regionview we want to then drag.
4171 latest_regionviews.clear();
4172 sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
4174 /* A selection grab currently creates two undo/redo operations, one for
4175 creating the new region and another for moving it.
4178 begin_reversible_command (_("selection grab"));
4180 boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
4182 XMLNode *before = &(playlist->get_state());
4183 clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
4184 XMLNode *after = &(playlist->get_state());
4185 session->add_command(new MementoCommand<Playlist>(*playlist, before, after));
4187 commit_reversible_command ();
4191 if (latest_regionviews.empty()) {
4192 /* something went wrong */
4196 /* we need to deselect all other regionviews, and select this one
4197 i'm ignoring undo stuff, because the region creation will take care of it
4199 selection->set (latest_regionviews);
4201 drag_info.item = latest_regionviews.front()->get_canvas_group();
4202 drag_info.data = latest_regionviews.front();
4203 drag_info.motion_callback = &Editor::region_drag_motion_callback;
4204 drag_info.finished_callback = &Editor::region_drag_finished_callback;
4208 drag_info.last_trackview = clicked_axisview;
4209 drag_info.last_frame_position = latest_regionviews.front()->region()->position();
4210 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
4212 show_verbose_time_cursor (drag_info.last_frame_position, 10);
4216 Editor::cancel_selection ()
4218 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4219 (*i)->hide_selection ();
4221 selection->clear ();
4222 clicked_selection = 0;
4226 Editor::start_selection_op (ArdourCanvas::Item* item, GdkEvent* event, SelectionOp op)
4228 nframes_t start = 0;
4235 drag_info.item = item;
4236 drag_info.motion_callback = &Editor::drag_selection;
4237 drag_info.finished_callback = &Editor::end_selection_op;
4242 case CreateSelection:
4243 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
4244 drag_info.copy = true;
4246 drag_info.copy = false;
4248 start_grab (event, selector_cursor);
4251 case SelectionStartTrim:
4252 if (clicked_axisview) {
4253 clicked_axisview->order_selection_trims (item, true);
4255 start_grab (event, trimmer_cursor);
4256 start = selection->time[clicked_selection].start;
4257 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
4260 case SelectionEndTrim:
4261 if (clicked_axisview) {
4262 clicked_axisview->order_selection_trims (item, false);
4264 start_grab (event, trimmer_cursor);
4265 end = selection->time[clicked_selection].end;
4266 drag_info.pointer_frame_offset = drag_info.grab_frame - end;
4270 start = selection->time[clicked_selection].start;
4272 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
4276 if (selection_op == SelectionMove) {
4277 show_verbose_time_cursor(start, 10);
4279 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4284 Editor::drag_selection (ArdourCanvas::Item* item, GdkEvent* event)
4286 nframes_t start = 0;
4289 nframes_t pending_position;
4291 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
4292 pending_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
4294 pending_position = 0;
4297 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4298 snap_to (pending_position);
4301 /* only alter selection if the current frame is
4302 different from the last frame position (adjusted)
4305 if (pending_position == drag_info.last_pointer_frame) return;
4307 switch (selection_op) {
4308 case CreateSelection:
4310 if (drag_info.first_move) {
4311 snap_to (drag_info.grab_frame);
4314 if (pending_position < drag_info.grab_frame) {
4315 start = pending_position;
4316 end = drag_info.grab_frame;
4318 end = pending_position;
4319 start = drag_info.grab_frame;
4322 /* first drag: Either add to the selection
4323 or create a new selection->
4326 if (drag_info.first_move) {
4328 begin_reversible_command (_("range selection"));
4330 if (drag_info.copy) {
4331 /* adding to the selection */
4332 clicked_selection = selection->add (start, end);
4333 drag_info.copy = false;
4335 /* new selection-> */
4336 clicked_selection = selection->set (clicked_axisview, start, end);
4341 case SelectionStartTrim:
4343 if (drag_info.first_move) {
4344 begin_reversible_command (_("trim selection start"));
4347 start = selection->time[clicked_selection].start;
4348 end = selection->time[clicked_selection].end;
4350 if (pending_position > end) {
4353 start = pending_position;
4357 case SelectionEndTrim:
4359 if (drag_info.first_move) {
4360 begin_reversible_command (_("trim selection end"));
4363 start = selection->time[clicked_selection].start;
4364 end = selection->time[clicked_selection].end;
4366 if (pending_position < start) {
4369 end = pending_position;
4376 if (drag_info.first_move) {
4377 begin_reversible_command (_("move selection"));
4380 start = selection->time[clicked_selection].start;
4381 end = selection->time[clicked_selection].end;
4383 length = end - start;
4385 start = pending_position;
4388 end = start + length;
4393 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
4394 start_canvas_autoscroll (1);
4398 selection->replace (clicked_selection, start, end);
4401 drag_info.last_pointer_frame = pending_position;
4402 drag_info.first_move = false;
4404 if (selection_op == SelectionMove) {
4405 show_verbose_time_cursor(start, 10);
4407 show_verbose_time_cursor(pending_position, 10);
4412 Editor::end_selection_op (ArdourCanvas::Item* item, GdkEvent* event)
4414 if (!drag_info.first_move) {
4415 drag_selection (item, event);
4416 /* XXX this is not object-oriented programming at all. ick */
4417 if (selection->time.consolidate()) {
4418 selection->TimeChanged ();
4420 commit_reversible_command ();
4422 /* just a click, no pointer movement.*/
4424 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
4426 selection->clear_time();
4431 /* XXX what happens if its a music selection? */
4432 session->set_audio_range (selection->time);
4433 stop_canvas_autoscroll ();
4437 Editor::start_trim (ArdourCanvas::Item* item, GdkEvent* event)
4440 TimeAxisView* tvp = clicked_axisview;
4441 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4443 if (tv && tv->is_track()) {
4444 speed = tv->get_diskstream()->speed();
4447 nframes_t region_start = (nframes_t) (clicked_regionview->region()->position() / speed);
4448 nframes_t region_end = (nframes_t) (clicked_regionview->region()->last_frame() / speed);
4449 nframes_t region_length = (nframes_t) (clicked_regionview->region()->length() / speed);
4451 //drag_info.item = clicked_regionview->get_name_highlight();
4452 drag_info.item = item;
4453 drag_info.motion_callback = &Editor::trim_motion_callback;
4454 drag_info.finished_callback = &Editor::trim_finished_callback;
4456 start_grab (event, trimmer_cursor);
4458 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
4459 trim_op = ContentsTrim;
4461 /* These will get overridden for a point trim.*/
4462 if (drag_info.current_pointer_frame < (region_start + region_length/2)) {
4463 /* closer to start */
4464 trim_op = StartTrim;
4465 } else if (drag_info.current_pointer_frame > (region_end - region_length/2)) {
4473 show_verbose_time_cursor(region_start, 10);
4476 show_verbose_time_cursor(region_end, 10);
4479 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4485 Editor::trim_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
4487 RegionView* rv = clicked_regionview;
4488 nframes_t frame_delta = 0;
4489 bool left_direction;
4490 bool obey_snap = !Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier());
4492 /* snap modifier works differently here..
4493 its' current state has to be passed to the
4494 various trim functions in order to work properly
4498 TimeAxisView* tvp = clicked_axisview;
4499 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4500 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
4502 if (tv && tv->is_track()) {
4503 speed = tv->get_diskstream()->speed();
4506 if (drag_info.last_pointer_frame > drag_info.current_pointer_frame) {
4507 left_direction = true;
4509 left_direction = false;
4513 snap_to (drag_info.current_pointer_frame);
4516 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
4520 if (drag_info.first_move) {
4526 trim_type = "Region start trim";
4529 trim_type = "Region end trim";
4532 trim_type = "Region content trim";
4536 begin_reversible_command (trim_type);
4538 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4539 (*i)->fake_set_opaque(false);
4540 (*i)->region()->freeze ();
4542 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
4544 arv->temporarily_hide_envelope ();
4546 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4547 insert_result = motion_frozen_playlists.insert (pl);
4548 if (insert_result.second) {
4549 session->add_command(new MementoCommand<Playlist>(*pl, &pl->get_state(), 0));
4555 if (left_direction) {
4556 frame_delta = (drag_info.last_pointer_frame - drag_info.current_pointer_frame);
4558 frame_delta = (drag_info.current_pointer_frame - drag_info.last_pointer_frame);
4563 if ((left_direction == false) && (drag_info.current_pointer_frame <= rv->region()->first_frame()/speed)) {
4566 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4567 single_start_trim (**i, frame_delta, left_direction, obey_snap);
4573 if ((left_direction == true) && (drag_info.current_pointer_frame > (nframes_t) (rv->region()->last_frame()/speed))) {
4576 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4577 single_end_trim (**i, frame_delta, left_direction, obey_snap);
4584 bool swap_direction = false;
4586 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
4587 swap_direction = true;
4590 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4591 i != selection->regions.by_layer().end(); ++i)
4593 single_contents_trim (**i, frame_delta, left_direction, swap_direction, obey_snap);
4601 show_verbose_time_cursor((nframes_t) (rv->region()->position()/speed), 10);
4604 show_verbose_time_cursor((nframes_t) (rv->region()->last_frame()/speed), 10);
4607 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4611 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4612 drag_info.first_move = false;
4616 Editor::single_contents_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool swap_direction, bool obey_snap)
4618 boost::shared_ptr<Region> region (rv.region());
4620 if (region->locked()) {
4624 nframes_t new_bound;
4627 TimeAxisView* tvp = clicked_axisview;
4628 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4630 if (tv && tv->is_track()) {
4631 speed = tv->get_diskstream()->speed();
4634 if (left_direction) {
4635 if (swap_direction) {
4636 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4638 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4641 if (swap_direction) {
4642 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4644 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4649 snap_to (new_bound);
4651 region->trim_start ((nframes_t) (new_bound * speed), this);
4652 rv.region_changed (StartChanged);
4656 Editor::single_start_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool obey_snap)
4658 boost::shared_ptr<Region> region (rv.region());
4660 if (region->locked()) {
4664 nframes_t new_bound;
4667 TimeAxisView* tvp = clicked_axisview;
4668 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4670 if (tv && tv->is_track()) {
4671 speed = tv->get_diskstream()->speed();
4674 if (left_direction) {
4675 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4677 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4681 snap_to (new_bound, (left_direction ? 0 : 1));
4684 region->trim_front ((nframes_t) (new_bound * speed), this);
4686 rv.region_changed (Change (LengthChanged|PositionChanged|StartChanged));
4690 Editor::single_end_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool obey_snap)
4692 boost::shared_ptr<Region> region (rv.region());
4694 if (region->locked()) {
4698 nframes_t new_bound;
4701 TimeAxisView* tvp = clicked_axisview;
4702 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4704 if (tv && tv->is_track()) {
4705 speed = tv->get_diskstream()->speed();
4708 if (left_direction) {
4709 new_bound = (nframes_t) ((region->last_frame() + 1)/speed) - frame_delta;
4711 new_bound = (nframes_t) ((region->last_frame() + 1)/speed) + frame_delta;
4715 snap_to (new_bound);
4717 region->trim_end ((nframes_t) (new_bound * speed), this);
4718 rv.region_changed (LengthChanged);
4722 Editor::trim_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
4724 if (!drag_info.first_move) {
4725 trim_motion_callback (item, event);
4727 if (!selection->selected (clicked_regionview)) {
4728 thaw_region_after_trim (*clicked_regionview);
4731 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4732 i != selection->regions.by_layer().end(); ++i)
4734 thaw_region_after_trim (**i);
4735 (*i)->fake_set_opaque (true);
4739 for (set<boost::shared_ptr<Playlist> >::iterator p = motion_frozen_playlists.begin(); p != motion_frozen_playlists.end(); ++p) {
4741 session->add_command (new MementoCommand<Playlist>(*(*p).get(), 0, &(*p)->get_state()));
4744 motion_frozen_playlists.clear ();
4746 commit_reversible_command();
4748 /* no mouse movement */
4754 Editor::point_trim (GdkEvent* event)
4756 RegionView* rv = clicked_regionview;
4757 nframes_t new_bound = drag_info.current_pointer_frame;
4759 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4760 snap_to (new_bound);
4763 /* Choose action dependant on which button was pressed */
4764 switch (event->button.button) {
4766 trim_op = StartTrim;
4767 begin_reversible_command (_("Start point trim"));
4769 if (selection->selected (rv)) {
4771 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4772 i != selection->regions.by_layer().end(); ++i)
4774 if (!(*i)->region()->locked()) {
4775 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4776 XMLNode &before = pl->get_state();
4777 (*i)->region()->trim_front (new_bound, this);
4778 XMLNode &after = pl->get_state();
4779 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4785 if (!rv->region()->locked()) {
4786 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
4787 XMLNode &before = pl->get_state();
4788 rv->region()->trim_front (new_bound, this);
4789 XMLNode &after = pl->get_state();
4790 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4794 commit_reversible_command();
4799 begin_reversible_command (_("End point trim"));
4801 if (selection->selected (rv)) {
4803 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
4805 if (!(*i)->region()->locked()) {
4806 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4807 XMLNode &before = pl->get_state();
4808 (*i)->region()->trim_end (new_bound, this);
4809 XMLNode &after = pl->get_state();
4810 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4816 if (!rv->region()->locked()) {
4817 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
4818 XMLNode &before = pl->get_state();
4819 rv->region()->trim_end (new_bound, this);
4820 XMLNode &after = pl->get_state();
4821 session->add_command (new MementoCommand<Playlist>(*pl.get(), &before, &after));
4825 commit_reversible_command();
4834 Editor::thaw_region_after_trim (RegionView& rv)
4836 boost::shared_ptr<Region> region (rv.region());
4838 if (region->locked()) {
4842 region->thaw (_("trimmed region"));
4843 XMLNode &after = region->playlist()->get_state();
4844 session->add_command (new MementoCommand<Playlist>(*(region->playlist()), 0, &after));
4846 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(&rv);
4848 arv->unhide_envelope ();
4852 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* event)
4857 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
4858 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
4862 Location* location = find_location_from_marker (marker, is_start);
4863 location->set_hidden (true, this);
4868 Editor::start_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event, RangeMarkerOp op)
4874 drag_info.item = item;
4875 drag_info.motion_callback = &Editor::drag_range_markerbar_op;
4876 drag_info.finished_callback = &Editor::end_range_markerbar_op;
4878 range_marker_op = op;
4880 if (!temp_location) {
4881 temp_location = new Location;
4885 case CreateRangeMarker:
4886 case CreateTransportMarker:
4887 case CreateCDMarker:
4889 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
4890 drag_info.copy = true;
4892 drag_info.copy = false;
4894 start_grab (event, selector_cursor);
4898 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4903 Editor::drag_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4905 nframes_t start = 0;
4907 ArdourCanvas::SimpleRect *crect = (range_marker_op == CreateRangeMarker) ? range_bar_drag_rect: transport_bar_drag_rect;
4909 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4910 snap_to (drag_info.current_pointer_frame);
4913 /* only alter selection if the current frame is
4914 different from the last frame position.
4917 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4919 switch (range_marker_op) {
4920 case CreateRangeMarker:
4921 case CreateTransportMarker:
4922 case CreateCDMarker:
4923 if (drag_info.first_move) {
4924 snap_to (drag_info.grab_frame);
4927 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4928 start = drag_info.current_pointer_frame;
4929 end = drag_info.grab_frame;
4931 end = drag_info.current_pointer_frame;
4932 start = drag_info.grab_frame;
4935 /* first drag: Either add to the selection
4936 or create a new selection.
4939 if (drag_info.first_move) {
4941 temp_location->set (start, end);
4945 update_marker_drag_item (temp_location);
4946 range_marker_drag_rect->show();
4947 range_marker_drag_rect->raise_to_top();
4953 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
4954 start_canvas_autoscroll (1);
4958 temp_location->set (start, end);
4960 double x1 = frame_to_pixel (start);
4961 double x2 = frame_to_pixel (end);
4962 crect->property_x1() = x1;
4963 crect->property_x2() = x2;
4965 update_marker_drag_item (temp_location);
4968 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4969 drag_info.first_move = false;
4971 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4976 Editor::end_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4978 Location * newloc = 0;
4982 if (!drag_info.first_move) {
4983 drag_range_markerbar_op (item, event);
4985 switch (range_marker_op) {
4986 case CreateRangeMarker:
4987 case CreateCDMarker:
4989 begin_reversible_command (_("new range marker"));
4990 XMLNode &before = session->locations()->get_state();
4991 session->locations()->next_available_name(rangename,"unnamed");
4992 if (range_marker_op == CreateCDMarker) {
4993 flags = Location::IsRangeMarker|Location::IsCDMarker;
4996 flags = Location::IsRangeMarker;
4998 newloc = new Location(temp_location->start(), temp_location->end(), rangename, (Location::Flags) flags);
4999 session->locations()->add (newloc, true);
5000 XMLNode &after = session->locations()->get_state();
5001 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
5002 commit_reversible_command ();
5004 range_bar_drag_rect->hide();
5005 range_marker_drag_rect->hide();
5009 case CreateTransportMarker:
5010 // popup menu to pick loop or punch
5011 new_transport_marker_context_menu (&event->button, item);
5016 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
5018 if (Keyboard::no_modifier_keys_pressed (&event->button) && range_marker_op != CreateCDMarker) {
5023 start = session->locations()->first_mark_before (drag_info.grab_frame);
5024 end = session->locations()->first_mark_after (drag_info.grab_frame);
5026 if (end == max_frames) {
5027 end = session->current_end_frame ();
5031 start = session->current_start_frame ();
5034 switch (mouse_mode) {
5036 /* find the two markers on either side and then make the selection from it */
5037 select_all_within (start, end, 0.0f, FLT_MAX, track_views, Selection::Set);
5041 /* find the two markers on either side of the click and make the range out of it */
5042 selection->set (0, start, end);
5051 stop_canvas_autoscroll ();
5057 Editor::start_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
5059 drag_info.item = item;
5060 drag_info.motion_callback = &Editor::drag_mouse_zoom;
5061 drag_info.finished_callback = &Editor::end_mouse_zoom;
5063 start_grab (event, zoom_cursor);
5065 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5069 Editor::drag_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
5074 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
5075 snap_to (drag_info.current_pointer_frame);
5077 if (drag_info.first_move) {
5078 snap_to (drag_info.grab_frame);
5082 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
5084 /* base start and end on initial click position */
5085 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
5086 start = drag_info.current_pointer_frame;
5087 end = drag_info.grab_frame;
5089 end = drag_info.current_pointer_frame;
5090 start = drag_info.grab_frame;
5095 if (drag_info.first_move) {
5097 zoom_rect->raise_to_top();
5100 reposition_zoom_rect(start, end);
5102 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
5103 drag_info.first_move = false;
5105 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5110 Editor::end_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
5112 if (!drag_info.first_move) {
5113 drag_mouse_zoom (item, event);
5115 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
5116 temporal_zoom_by_frame (drag_info.grab_frame, drag_info.last_pointer_frame, "mouse zoom");
5118 temporal_zoom_by_frame (drag_info.last_pointer_frame, drag_info.grab_frame, "mouse zoom");
5121 temporal_zoom_to_frame (false, drag_info.grab_frame);
5123 temporal_zoom_step (false);
5124 center_screen (drag_info.grab_frame);
5132 Editor::reposition_zoom_rect (nframes_t start, nframes_t end)
5134 double x1 = frame_to_pixel (start);
5135 double x2 = frame_to_pixel (end);
5136 double y2 = full_canvas_height - 1.0;
5138 zoom_rect->property_x1() = x1;
5139 zoom_rect->property_y1() = 1.0;
5140 zoom_rect->property_x2() = x2;
5141 zoom_rect->property_y2() = y2;
5145 Editor::start_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
5147 drag_info.item = item;
5148 drag_info.motion_callback = &Editor::drag_rubberband_select;
5149 drag_info.finished_callback = &Editor::end_rubberband_select;
5151 start_grab (event, cross_hair_cursor);
5153 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5157 Editor::drag_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
5164 /* use a bigger drag threshold than the default */
5166 if (abs ((int) (drag_info.current_pointer_frame - drag_info.grab_frame)) < 8) {
5170 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
5171 if (drag_info.first_move) {
5172 snap_to (drag_info.grab_frame);
5174 snap_to (drag_info.current_pointer_frame);
5177 /* base start and end on initial click position */
5179 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
5180 start = drag_info.current_pointer_frame;
5181 end = drag_info.grab_frame;
5183 end = drag_info.current_pointer_frame;
5184 start = drag_info.grab_frame;
5187 if (drag_info.current_pointer_y < drag_info.grab_y) {
5188 y1 = drag_info.current_pointer_y;
5189 y2 = drag_info.grab_y;
5191 y2 = drag_info.current_pointer_y;
5192 y1 = drag_info.grab_y;
5196 if (start != end || y1 != y2) {
5198 double x1 = frame_to_pixel (start);
5199 double x2 = frame_to_pixel (end);
5201 rubberband_rect->property_x1() = x1;
5202 rubberband_rect->property_y1() = y1;
5203 rubberband_rect->property_x2() = x2;
5204 rubberband_rect->property_y2() = y2;
5206 rubberband_rect->show();
5207 rubberband_rect->raise_to_top();
5209 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
5210 drag_info.first_move = false;
5212 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5217 Editor::end_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
5219 if (!drag_info.first_move) {
5221 drag_rubberband_select (item, event);
5224 if (drag_info.current_pointer_y < drag_info.grab_y) {
5225 y1 = drag_info.current_pointer_y;
5226 y2 = drag_info.grab_y;
5229 y2 = drag_info.current_pointer_y;
5230 y1 = drag_info.grab_y;
5234 Selection::Operation op = Keyboard::selection_type (event->button.state);
5237 begin_reversible_command (_("rubberband selection"));
5239 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
5240 commit = select_all_within (drag_info.grab_frame, drag_info.last_pointer_frame, y1, y2, track_views, op);
5242 commit = select_all_within (drag_info.last_pointer_frame, drag_info.grab_frame, y1, y2, track_views, op);
5246 commit_reversible_command ();
5250 selection->clear_tracks();
5251 selection->clear_regions();
5252 selection->clear_points ();
5253 selection->clear_lines ();
5256 rubberband_rect->hide();
5261 Editor::mouse_rename_region (ArdourCanvas::Item* item, GdkEvent* event)
5263 using namespace Gtkmm2ext;
5265 ArdourPrompter prompter (false);
5267 prompter.set_prompt (_("Name for region:"));
5268 prompter.set_initial_text (clicked_regionview->region()->name());
5269 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
5270 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
5271 prompter.show_all ();
5272 switch (prompter.run ()) {
5273 case Gtk::RESPONSE_ACCEPT:
5275 prompter.get_result(str);
5277 clicked_regionview->region()->set_name (str);
5285 Editor::start_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
5287 drag_info.item = item;
5288 drag_info.motion_callback = &Editor::time_fx_motion;
5289 drag_info.finished_callback = &Editor::end_time_fx;
5293 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5297 Editor::time_fx_motion (ArdourCanvas::Item *item, GdkEvent* event)
5299 RegionView* rv = clicked_regionview;
5301 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
5302 snap_to (drag_info.current_pointer_frame);
5305 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
5309 if (drag_info.current_pointer_frame > rv->region()->position()) {
5310 rv->get_time_axis_view().show_timestretch (rv->region()->position(), drag_info.current_pointer_frame);
5313 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
5314 drag_info.first_move = false;
5316 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5320 Editor::end_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
5322 clicked_regionview->get_time_axis_view().hide_timestretch ();
5324 if (drag_info.first_move) {
5328 if (drag_info.last_pointer_frame < clicked_regionview->region()->position()) {
5329 /* backwards drag of the left edge - not usable */
5333 nframes_t newlen = drag_info.last_pointer_frame - clicked_regionview->region()->position();
5335 float percentage = (double) newlen / (double) clicked_regionview->region()->length();
5337 #ifndef USE_RUBBERBAND
5338 // Soundtouch uses percentage / 100 instead of normal (/ 1)
5339 if (clicked_regionview->region()->data_type() == DataType::AUDIO) {
5340 percentage = (float) ((double) newlen - (double) clicked_regionview->region()->length()) / ((double) newlen) * 100.0f;
5344 begin_reversible_command (_("timestretch"));
5346 // XXX how do timeFX on multiple regions ?
5349 rs.add (clicked_regionview);
5351 if (time_stretch (rs, percentage) == 0) {
5352 session->commit_reversible_command ();
5357 Editor::mouse_brush_insert_region (RegionView* rv, nframes_t pos)
5359 /* no brushing without a useful snap setting */
5362 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
5365 switch (snap_mode) {
5367 return; /* can't work because it allows region to be placed anywhere */
5372 switch (snap_type) {
5380 /* don't brush a copy over the original */
5382 if (pos == rv->region()->position()) {
5386 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&arv->get_time_axis_view());
5388 if (rtv == 0 || !rtv->is_track()) {
5392 boost::shared_ptr<Playlist> playlist = rtv->playlist();
5393 double speed = rtv->get_diskstream()->speed();
5395 XMLNode &before = playlist->get_state();
5396 playlist->add_region (boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (arv->audio_region())), (nframes_t) (pos * speed));
5397 XMLNode &after = playlist->get_state();
5398 session->add_command(new MementoCommand<Playlist>(*playlist.get(), &before, &after));
5400 // playlist is frozen, so we have to update manually
5402 playlist->Modified(); /* EMIT SIGNAL */
5406 Editor::track_height_step_timeout ()
5409 struct timeval delta;
5411 gettimeofday (&now, 0);
5412 timersub (&now, &last_track_height_step_timestamp, &delta);
5414 if (delta.tv_sec * 1000000 + delta.tv_usec > 250000) { /* milliseconds */
5415 current_stepping_trackview = 0;