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::which_grabber_cursor ()
221 switch (_edit_point) {
223 return grabber_edit_point_cursor;
226 return grabber_cursor;
232 Editor::set_canvas_cursor ()
234 switch (mouse_mode) {
236 current_canvas_cursor = selector_cursor;
240 current_canvas_cursor = which_grabber_cursor();
244 current_canvas_cursor = cross_hair_cursor;
248 current_canvas_cursor = zoom_cursor;
252 current_canvas_cursor = time_fx_cursor; // just use playhead
256 current_canvas_cursor = speaker_cursor;
260 set_midi_edit_cursor (current_midi_edit_mode());
265 track_canvas->get_window()->set_cursor(*current_canvas_cursor);
270 Editor::set_mouse_mode (MouseMode m, bool force)
272 if (drag_info.item) {
276 if (!force && m == mouse_mode) {
284 if (mouse_mode != MouseRange) {
286 /* in all modes except range, hide the range selection,
287 show the object (region) selection.
290 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
291 (*i)->set_should_show_selection (true);
293 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
294 (*i)->hide_selection ();
300 in range mode,show the range selection.
303 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
304 if ((*i)->get_selected()) {
305 (*i)->show_selection (selection->time);
310 /* XXX the hack of unsetting all other buttons should go
311 away once GTK2 allows us to use regular radio buttons drawn like
312 normal buttons, rather than my silly GroupedButton hack.
315 ignore_mouse_mode_toggle = true;
317 switch (mouse_mode) {
319 mouse_select_button.set_active (true);
323 mouse_move_button.set_active (true);
327 mouse_gain_button.set_active (true);
331 mouse_zoom_button.set_active (true);
335 mouse_timefx_button.set_active (true);
339 mouse_audition_button.set_active (true);
343 mouse_note_button.set_active (true);
344 set_midi_edit_cursor (current_midi_edit_mode());
348 if (mouse_mode == MouseNote)
349 midi_toolbar_frame.show();
351 midi_toolbar_frame.hide();
353 ignore_mouse_mode_toggle = false;
355 set_canvas_cursor ();
359 Editor::step_mouse_mode (bool next)
361 switch (current_mouse_mode()) {
363 if (next) set_mouse_mode (MouseRange);
364 else set_mouse_mode (MouseTimeFX);
368 if (next) set_mouse_mode (MouseZoom);
369 else set_mouse_mode (MouseObject);
373 if (next) set_mouse_mode (MouseGain);
374 else set_mouse_mode (MouseRange);
378 if (next) set_mouse_mode (MouseTimeFX);
379 else set_mouse_mode (MouseZoom);
383 if (next) set_mouse_mode (MouseAudition);
384 else set_mouse_mode (MouseGain);
388 if (next) set_mouse_mode (MouseObject);
389 else set_mouse_mode (MouseTimeFX);
393 if (next) set_mouse_mode (MouseObject);
394 else set_mouse_mode (MouseAudition);
400 Editor::midi_edit_mode_toggled (MidiEditMode m)
402 if (ignore_midi_edit_mode_toggle) {
408 if (midi_tool_pencil_button.get_active())
409 set_midi_edit_mode (m);
413 if (midi_tool_select_button.get_active())
414 set_midi_edit_mode (m);
418 if (midi_tool_erase_button.get_active())
419 set_midi_edit_mode (m);
426 set_midi_edit_cursor(m);
431 Editor::set_midi_edit_mode (MidiEditMode m, bool force)
433 if (drag_info.item) {
437 if (!force && m == midi_edit_mode) {
445 ignore_midi_edit_mode_toggle = true;
447 switch (midi_edit_mode) {
449 midi_tool_pencil_button.set_active (true);
453 midi_tool_select_button.set_active (true);
457 midi_tool_erase_button.set_active (true);
461 ignore_midi_edit_mode_toggle = false;
463 set_midi_edit_cursor (current_midi_edit_mode());
466 track_canvas->get_window()->set_cursor(*current_canvas_cursor);
471 Editor::set_midi_edit_cursor (MidiEditMode m)
473 switch (midi_edit_mode) {
475 current_canvas_cursor = midi_pencil_cursor;
479 current_canvas_cursor = midi_select_cursor;
483 current_canvas_cursor = midi_erase_cursor;
489 Editor::button_selection (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
491 /* in object/audition/timefx mode, any button press sets
492 the selection if the object can be selected. this is a
493 bit of hack, because we want to avoid this if the
494 mouse operation is a region alignment.
496 note: not dbl-click or triple-click
499 if (((mouse_mode != MouseObject) &&
500 (mouse_mode != MouseAudition || item_type != RegionItem) &&
501 (mouse_mode != MouseTimeFX || item_type != RegionItem) &&
502 (mouse_mode != MouseRange)) ||
504 (event->type != GDK_BUTTON_PRESS && event->type != GDK_BUTTON_RELEASE || event->button.button > 3)) {
509 if (event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) {
511 if ((event->button.state & Keyboard::RelevantModifierKeyMask) && event->button.button != 1) {
513 /* almost no selection action on modified button-2 or button-3 events */
515 if (item_type != RegionItem && event->button.button != 2) {
521 Selection::Operation op = Keyboard::selection_type (event->button.state);
522 bool press = (event->type == GDK_BUTTON_PRESS);
524 // begin_reversible_command (_("select on click"));
528 if (mouse_mode != MouseRange) {
529 set_selected_regionview_from_click (press, op, true);
530 } else if (event->type == GDK_BUTTON_PRESS) {
531 set_selected_track_as_side_effect ();
535 case RegionViewNameHighlight:
537 if (mouse_mode != MouseRange) {
538 set_selected_regionview_from_click (press, op, true);
539 } else if (event->type == GDK_BUTTON_PRESS) {
540 set_selected_track_as_side_effect ();
545 case FadeInHandleItem:
547 case FadeOutHandleItem:
549 if (mouse_mode != MouseRange) {
550 set_selected_regionview_from_click (press, op, true);
551 } else if (event->type == GDK_BUTTON_PRESS) {
552 set_selected_track_as_side_effect ();
556 case ControlPointItem:
557 set_selected_track_as_side_effect ();
558 if (mouse_mode != MouseRange) {
559 set_selected_control_point_from_click (op, false);
564 /* for context click or range selection, select track */
565 if (event->button.button == 3) {
566 set_selected_track_as_side_effect ();
567 } else if (event->type == GDK_BUTTON_PRESS && mouse_mode == MouseRange) {
568 set_selected_track_as_side_effect ();
572 case AutomationTrackItem:
573 set_selected_track_as_side_effect (true);
582 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
584 track_canvas->grab_focus();
586 if (session && session->actively_recording()) {
590 button_selection (item, event, item_type);
592 if (drag_info.item == 0 &&
593 (Keyboard::is_delete_event (&event->button) ||
594 Keyboard::is_context_menu_event (&event->button) ||
595 Keyboard::is_edit_event (&event->button))) {
597 /* handled by button release */
601 switch (event->button.button) {
604 if (event->type == GDK_BUTTON_PRESS) {
606 if (drag_info.item) {
607 drag_info.item->ungrab (event->button.time);
610 /* single mouse clicks on any of these item types operate
611 independent of mouse mode, mostly because they are
612 not on the main track canvas or because we want
617 case PlayheadCursorItem:
618 start_cursor_grab (item, event);
622 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
623 hide_marker (item, event);
625 start_marker_grab (item, event);
629 case TempoMarkerItem:
630 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
631 start_tempo_marker_copy_grab (item, event);
633 start_tempo_marker_grab (item, event);
637 case MeterMarkerItem:
638 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
639 start_meter_marker_copy_grab (item, event);
641 start_meter_marker_grab (item, event);
651 case RangeMarkerBarItem:
652 start_range_markerbar_op (item, event, CreateRangeMarker);
656 case CdMarkerBarItem:
657 start_range_markerbar_op (item, event, CreateCDMarker);
661 case TransportMarkerBarItem:
662 start_range_markerbar_op (item, event, CreateTransportMarker);
671 switch (mouse_mode) {
674 case StartSelectionTrimItem:
675 start_selection_op (item, event, SelectionStartTrim);
678 case EndSelectionTrimItem:
679 start_selection_op (item, event, SelectionEndTrim);
683 if (Keyboard::modifier_state_contains
684 (event->button.state, Keyboard::ModifierMask(Keyboard::SecondaryModifier))) {
685 // contains and not equals because I can't use alt as a modifier alone.
686 start_selection_grab (item, event);
687 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
688 /* grab selection for moving */
689 start_selection_op (item, event, SelectionMove);
691 /* this was debated, but decided the more common action was to
692 make a new selection */
693 start_selection_op (item, event, CreateSelection);
698 start_selection_op (item, event, CreateSelection);
704 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)) &&
705 event->type == GDK_BUTTON_PRESS) {
707 start_rubberband_select (item, event);
709 } else if (event->type == GDK_BUTTON_PRESS) {
712 case FadeInHandleItem:
713 start_fade_in_grab (item, event);
716 case FadeOutHandleItem:
717 start_fade_out_grab (item, event);
721 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
722 start_region_copy_grab (item, event);
723 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
724 start_region_brush_grab (item, event);
726 start_region_grab (item, event);
730 case RegionViewNameHighlight:
731 start_trim (item, event);
736 /* rename happens on edit clicks */
737 start_trim (clicked_regionview->get_name_highlight(), event);
741 case ControlPointItem:
742 start_control_point_grab (item, event);
746 case AutomationLineItem:
747 start_line_grab_from_line (item, event);
752 case AutomationTrackItem:
753 start_rubberband_select (item, event);
757 case ImageFrameHandleStartItem:
758 imageframe_start_handle_op(item, event) ;
761 case ImageFrameHandleEndItem:
762 imageframe_end_handle_op(item, event) ;
765 case MarkerViewHandleStartItem:
766 markerview_item_start_handle_op(item, event) ;
769 case MarkerViewHandleEndItem:
770 markerview_item_end_handle_op(item, event) ;
774 start_markerview_grab(item, event) ;
777 start_imageframe_grab(item, event) ;
795 // start_line_grab_from_regionview (item, event);
799 start_line_grab_from_line (item, event);
802 case ControlPointItem:
803 start_control_point_grab (item, event);
814 case ControlPointItem:
815 start_control_point_grab (item, event);
818 case AutomationLineItem:
819 start_line_grab_from_line (item, event);
823 // XXX need automation mode to identify which
825 // start_line_grab_from_regionview (item, event);
835 if (event->type == GDK_BUTTON_PRESS) {
836 start_mouse_zoom (item, event);
843 if (item_type == RegionItem) {
844 start_time_fx (item, event);
851 scrub_reverse_distance = 0;
852 last_scrub_x = event->button.x;
853 scrubbing_direction = 0;
854 track_canvas->get_window()->set_cursor (*transparent_cursor);
855 /* rest handled in motion & release */
859 start_create_region_grab (item, event);
868 switch (mouse_mode) {
870 if (event->type == GDK_BUTTON_PRESS) {
873 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
874 start_region_copy_grab (item, event);
876 start_region_grab (item, event);
880 case ControlPointItem:
881 start_control_point_grab (item, event);
892 case RegionViewNameHighlight:
893 start_trim (item, event);
898 start_trim (clicked_regionview->get_name_highlight(), event);
909 if (event->type == GDK_BUTTON_PRESS) {
910 /* relax till release */
917 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
918 temporal_zoom_session();
920 temporal_zoom_to_frame (true, event_frame(event));
943 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
945 nframes_t where = event_frame (event, 0, 0);
946 AutomationTimeAxisView* atv = 0;
948 /* no action if we're recording */
950 if (session && session->actively_recording()) {
954 /* first, see if we're finishing a drag ... */
956 if (drag_info.item) {
957 if (end_grab (item, event)) {
958 /* grab dragged, so do nothing else */
963 button_selection (item, event, item_type);
965 /* edit events get handled here */
967 if (drag_info.item == 0 && Keyboard::is_edit_event (&event->button)) {
973 case TempoMarkerItem:
974 edit_tempo_marker (item);
977 case MeterMarkerItem:
978 edit_meter_marker (item);
982 if (clicked_regionview->name_active()) {
983 return mouse_rename_region (item, event);
993 /* context menu events get handled here */
995 if (Keyboard::is_context_menu_event (&event->button)) {
997 if (drag_info.item == 0) {
999 /* no matter which button pops up the context menu, tell the menu
1000 widget to use button 1 to drive menu selection.
1003 switch (item_type) {
1005 case FadeInHandleItem:
1007 case FadeOutHandleItem:
1008 popup_fade_context_menu (1, event->button.time, item, item_type);
1012 popup_track_context_menu (1, event->button.time, item_type, false, where);
1016 case RegionViewNameHighlight:
1017 case RegionViewName:
1018 popup_track_context_menu (1, event->button.time, item_type, false, where);
1022 popup_track_context_menu (1, event->button.time, item_type, true, where);
1025 case AutomationTrackItem:
1026 popup_track_context_menu (1, event->button.time, item_type, false, where);
1030 case RangeMarkerBarItem:
1031 case TransportMarkerBarItem:
1032 case CdMarkerBarItem:
1035 popup_ruler_menu (pixel_to_frame(event->button.x), item_type);
1039 marker_context_menu (&event->button, item);
1042 case TempoMarkerItem:
1043 tm_marker_context_menu (&event->button, item);
1046 case MeterMarkerItem:
1047 tm_marker_context_menu (&event->button, item);
1050 case CrossfadeViewItem:
1051 popup_track_context_menu (1, event->button.time, item_type, false, where);
1055 case ImageFrameItem:
1056 popup_imageframe_edit_menu(1, event->button.time, item, true) ;
1058 case ImageFrameTimeAxisItem:
1059 popup_imageframe_edit_menu(1, event->button.time, item, false) ;
1061 case MarkerViewItem:
1062 popup_marker_time_axis_edit_menu(1, event->button.time, item, true) ;
1064 case MarkerTimeAxisItem:
1065 popup_marker_time_axis_edit_menu(1, event->button.time, item, false) ;
1077 /* delete events get handled here */
1079 if (drag_info.item == 0 && Keyboard::is_delete_event (&event->button)) {
1081 switch (item_type) {
1082 case TempoMarkerItem:
1083 remove_tempo_marker (item);
1086 case MeterMarkerItem:
1087 remove_meter_marker (item);
1091 remove_marker (*item, event);
1095 if (mouse_mode == MouseObject) {
1096 remove_clicked_region ();
1100 case ControlPointItem:
1101 if (mouse_mode == MouseGain) {
1102 remove_gain_control_point (item, event);
1104 remove_control_point (item, event);
1114 switch (event->button.button) {
1117 switch (item_type) {
1118 /* see comments in button_press_handler */
1119 case PlayheadCursorItem:
1122 case AutomationLineItem:
1123 case StartSelectionTrimItem:
1124 case EndSelectionTrimItem:
1128 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1129 snap_to (where, 0, true);
1131 mouse_add_new_marker (where);
1134 case CdMarkerBarItem:
1135 // if we get here then a dragged range wasn't done
1136 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1137 snap_to (where, 0, true);
1139 mouse_add_new_marker (where, true);
1143 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1146 mouse_add_new_tempo_event (where);
1150 mouse_add_new_meter_event (pixel_to_frame (event->button.x));
1158 switch (mouse_mode) {
1160 switch (item_type) {
1161 case AutomationTrackItem:
1162 atv = dynamic_cast<AutomationTimeAxisView*>(clicked_routeview);
1164 atv->add_automation_event (item, event, where, event->button.y);
1176 // Gain only makes sense for audio regions
1178 if (!dynamic_cast<AudioRegionView*>(clicked_regionview)) {
1182 switch (item_type) {
1184 dynamic_cast<AudioRegionView*>(clicked_regionview)->add_gain_point_event (item, event);
1188 case AutomationTrackItem:
1189 dynamic_cast<AutomationTimeAxisView*>(clicked_axisview)->
1190 add_automation_event (item, event, where, event->button.y);
1200 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1201 if (scrubbing_direction == 0) {
1202 /* no drag, just a click */
1203 switch (item_type) {
1205 play_selected_region ();
1211 /* make sure we stop */
1212 session->request_transport_speed (0.0);
1226 switch (mouse_mode) {
1229 switch (item_type) {
1231 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1233 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier|Keyboard::SecondaryModifier))) {
1236 // Button2 click is unused
1249 // x_style_paste (where, 1.0);
1269 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1275 if (last_item_entered != item) {
1276 last_item_entered = item;
1277 last_item_entered_n = 0;
1280 switch (item_type) {
1281 case ControlPointItem:
1282 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1283 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1284 cp->set_visible (true);
1288 at_y = cp->get_y ();
1289 cp->item()->i2w (at_x, at_y);
1293 fraction = 1.0 - (cp->get_y() / cp->line().height());
1295 if (is_drawable() && !_scrubbing) {
1296 track_canvas->get_window()->set_cursor (*fader_cursor);
1299 last_item_entered_n++;
1300 set_verbose_canvas_cursor (cp->line().get_verbose_cursor_string (fraction), at_x, at_y);
1301 if (last_item_entered_n < 10) {
1302 show_verbose_canvas_cursor ();
1308 if (mouse_mode == MouseGain) {
1309 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1311 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredGainLine.get();
1312 if (is_drawable()) {
1313 track_canvas->get_window()->set_cursor (*fader_cursor);
1318 case AutomationLineItem:
1319 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1321 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1323 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredAutomationLine.get();
1325 if (is_drawable()) {
1326 track_canvas->get_window()->set_cursor (*fader_cursor);
1331 case RegionViewNameHighlight:
1332 if (is_drawable() && mouse_mode == MouseObject) {
1333 track_canvas->get_window()->set_cursor (*trimmer_cursor);
1337 case StartSelectionTrimItem:
1338 case EndSelectionTrimItem:
1341 case ImageFrameHandleStartItem:
1342 case ImageFrameHandleEndItem:
1343 case MarkerViewHandleStartItem:
1344 case MarkerViewHandleEndItem:
1347 if (is_drawable()) {
1348 track_canvas->get_window()->set_cursor (*trimmer_cursor);
1352 case PlayheadCursorItem:
1353 if (is_drawable()) {
1354 switch (_edit_point) {
1356 track_canvas->get_window()->set_cursor (*grabber_edit_point_cursor);
1359 track_canvas->get_window()->set_cursor (*grabber_cursor);
1365 case RegionViewName:
1367 /* when the name is not an active item, the entire name highlight is for trimming */
1369 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1370 if (mouse_mode == MouseObject && is_drawable()) {
1371 track_canvas->get_window()->set_cursor (*trimmer_cursor);
1377 case AutomationTrackItem:
1378 if (is_drawable()) {
1379 Gdk::Cursor *cursor;
1380 switch (mouse_mode) {
1382 cursor = selector_cursor;
1385 cursor = zoom_cursor;
1388 cursor = cross_hair_cursor;
1392 track_canvas->get_window()->set_cursor (*cursor);
1394 AutomationTimeAxisView* atv;
1395 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1396 clear_entered_track = false;
1397 set_entered_track (atv);
1403 case RangeMarkerBarItem:
1404 case TransportMarkerBarItem:
1405 case CdMarkerBarItem:
1408 if (is_drawable()) {
1409 time_canvas->get_window()->set_cursor (*timebar_cursor);
1414 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1417 entered_marker = marker;
1418 marker->set_color_rgba (ARDOUR_UI::config()->canvasvar_EnteredMarker.get());
1420 case MeterMarkerItem:
1421 case TempoMarkerItem:
1422 if (is_drawable()) {
1423 time_canvas->get_window()->set_cursor (*timebar_cursor);
1426 case FadeInHandleItem:
1427 case FadeOutHandleItem:
1428 if (mouse_mode == MouseObject) {
1429 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1431 rect->property_fill_color_rgba() = 0;
1432 rect->property_outline_pixels() = 1;
1441 /* second pass to handle entered track status in a comprehensible way.
1444 switch (item_type) {
1446 case AutomationLineItem:
1447 case ControlPointItem:
1448 /* these do not affect the current entered track state */
1449 clear_entered_track = false;
1452 case AutomationTrackItem:
1453 /* handled above already */
1457 set_entered_track (0);
1465 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1474 switch (item_type) {
1475 case ControlPointItem:
1476 cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
1477 if (cp->line().the_list()->interpolation() != AutomationList::Discrete) {
1478 if (cp->line().npoints() > 1 && !cp->selected()) {
1479 cp->set_visible (false);
1483 if (is_drawable()) {
1484 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1487 hide_verbose_canvas_cursor ();
1490 case RegionViewNameHighlight:
1491 case StartSelectionTrimItem:
1492 case EndSelectionTrimItem:
1493 case PlayheadCursorItem:
1496 case ImageFrameHandleStartItem:
1497 case ImageFrameHandleEndItem:
1498 case MarkerViewHandleStartItem:
1499 case MarkerViewHandleEndItem:
1502 if (is_drawable()) {
1503 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1508 case AutomationLineItem:
1509 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1511 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1513 line->property_fill_color_rgba() = al->get_line_color();
1515 if (is_drawable()) {
1516 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1520 case RegionViewName:
1521 /* see enter_handler() for notes */
1522 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1523 if (is_drawable() && mouse_mode == MouseObject) {
1524 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1529 case RangeMarkerBarItem:
1530 case TransportMarkerBarItem:
1531 case CdMarkerBarItem:
1535 if (is_drawable()) {
1536 time_canvas->get_window()->set_cursor (*timebar_cursor);
1541 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1545 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
1546 location_flags_changed (loc, this);
1549 case MeterMarkerItem:
1550 case TempoMarkerItem:
1552 if (is_drawable()) {
1553 time_canvas->get_window()->set_cursor (*timebar_cursor);
1558 case FadeInHandleItem:
1559 case FadeOutHandleItem:
1560 rv = static_cast<RegionView*>(item->get_data ("regionview"));
1562 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1564 rect->property_fill_color_rgba() = rv->get_fill_color();
1565 rect->property_outline_pixels() = 0;
1570 case AutomationTrackItem:
1571 if (is_drawable()) {
1572 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1573 clear_entered_track = true;
1574 Glib::signal_idle().connect (mem_fun(*this, &Editor::left_automation_track));
1586 Editor::left_automation_track ()
1588 if (clear_entered_track) {
1589 set_entered_track (0);
1590 clear_entered_track = false;
1596 Editor::motion_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type, bool from_autoscroll)
1598 if (event->motion.is_hint) {
1601 /* We call this so that MOTION_NOTIFY events continue to be
1602 delivered to the canvas. We need to do this because we set
1603 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
1604 the density of the events, at the expense of a round-trip
1605 to the server. Given that this will mostly occur on cases
1606 where DISPLAY = :0.0, and given the cost of what the motion
1607 event might do, its a good tradeoff.
1610 track_canvas->get_pointer (x, y);
1613 if (current_stepping_trackview) {
1614 /* don't keep the persistent stepped trackview if the mouse moves */
1615 current_stepping_trackview = 0;
1616 step_timeout.disconnect ();
1619 if (session && session->actively_recording()) {
1620 /* Sorry. no dragging stuff around while we record */
1624 drag_info.item_type = item_type;
1625 drag_info.last_pointer_x = drag_info.current_pointer_x;
1626 drag_info.last_pointer_y = drag_info.current_pointer_y;
1627 drag_info.current_pointer_frame = event_frame (event, &drag_info.current_pointer_x,
1628 &drag_info.current_pointer_y);
1630 switch (mouse_mode) {
1636 if (scrubbing_direction == 0) {
1638 session->request_locate (drag_info.current_pointer_frame, false);
1639 session->request_transport_speed (0.1);
1640 scrubbing_direction = 1;
1644 if (last_scrub_x > drag_info.current_pointer_x) {
1646 /* pointer moved to the left */
1648 if (scrubbing_direction > 0) {
1650 /* we reversed direction to go backwards */
1653 scrub_reverse_distance += (int) (last_scrub_x - drag_info.current_pointer_x);
1657 /* still moving to the left (backwards) */
1659 scrub_reversals = 0;
1660 scrub_reverse_distance = 0;
1662 delta = 0.01 * (last_scrub_x - drag_info.current_pointer_x);
1663 session->request_transport_speed (session->transport_speed() - delta);
1667 /* pointer moved to the right */
1669 if (scrubbing_direction < 0) {
1670 /* we reversed direction to go forward */
1673 scrub_reverse_distance += (int) (drag_info.current_pointer_x - last_scrub_x);
1676 /* still moving to the right */
1678 scrub_reversals = 0;
1679 scrub_reverse_distance = 0;
1681 delta = 0.01 * (drag_info.current_pointer_x - last_scrub_x);
1682 session->request_transport_speed (session->transport_speed() + delta);
1686 /* if there have been more than 2 opposite motion moves detected, or one that moves
1687 back more than 10 pixels, reverse direction
1690 if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
1692 if (scrubbing_direction > 0) {
1693 /* was forwards, go backwards */
1694 session->request_transport_speed (-0.1);
1695 scrubbing_direction = -1;
1697 /* was backwards, go forwards */
1698 session->request_transport_speed (0.1);
1699 scrubbing_direction = 1;
1702 scrub_reverse_distance = 0;
1703 scrub_reversals = 0;
1707 last_scrub_x = drag_info.current_pointer_x;
1715 if (!from_autoscroll && drag_info.item) {
1716 /* item != 0 is the best test i can think of for dragging.
1718 if (!drag_info.move_threshold_passed) {
1720 bool x_threshold_passed = (::llabs ((nframes64_t) (drag_info.current_pointer_x - drag_info.grab_x)) > 4LL);
1721 bool y_threshold_passed = (::llabs ((nframes64_t) (drag_info.current_pointer_y - drag_info.grab_y)) > 4LL);
1723 drag_info.move_threshold_passed = (x_threshold_passed || y_threshold_passed);
1725 // and change the initial grab loc/frame if this drag info wants us to
1727 if (drag_info.want_move_threshold && drag_info.move_threshold_passed) {
1728 drag_info.grab_frame = drag_info.current_pointer_frame;
1729 drag_info.grab_x = drag_info.current_pointer_x;
1730 drag_info.grab_y = drag_info.current_pointer_y;
1731 drag_info.last_pointer_frame = drag_info.grab_frame;
1732 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
1737 switch (item_type) {
1738 case PlayheadCursorItem:
1740 case ControlPointItem:
1741 case TempoMarkerItem:
1742 case MeterMarkerItem:
1743 case RegionViewNameHighlight:
1744 case StartSelectionTrimItem:
1745 case EndSelectionTrimItem:
1748 case AutomationLineItem:
1749 case FadeInHandleItem:
1750 case FadeOutHandleItem:
1753 case ImageFrameHandleStartItem:
1754 case ImageFrameHandleEndItem:
1755 case MarkerViewHandleStartItem:
1756 case MarkerViewHandleEndItem:
1759 if (drag_info.item && (event->motion.state & Gdk::BUTTON1_MASK ||
1760 (event->motion.state & Gdk::BUTTON2_MASK))) {
1761 if (!from_autoscroll) {
1762 maybe_autoscroll (event);
1764 (this->*(drag_info.motion_callback)) (item, event);
1773 switch (mouse_mode) {
1779 if (drag_info.item && (event->motion.state & GDK_BUTTON1_MASK ||
1780 (event->motion.state & GDK_BUTTON2_MASK))) {
1781 if (!from_autoscroll) {
1782 maybe_autoscroll (event);
1784 (this->*(drag_info.motion_callback)) (item, event);
1795 track_canvas_motion (event);
1796 // drag_info.last_pointer_frame = drag_info.current_pointer_frame;
1804 Editor::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
1806 if (drag_info.item == 0) {
1807 fatal << _("programming error: start_grab called without drag item") << endmsg;
1813 cursor = which_grabber_cursor ();
1816 // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
1818 if (event->button.button == 2) {
1819 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
1820 drag_info.y_constrained = true;
1821 drag_info.x_constrained = false;
1823 drag_info.y_constrained = false;
1824 drag_info.x_constrained = true;
1827 drag_info.x_constrained = false;
1828 drag_info.y_constrained = false;
1831 drag_info.grab_frame = event_frame (event, &drag_info.grab_x, &drag_info.grab_y);
1832 drag_info.last_pointer_frame = drag_info.grab_frame;
1833 drag_info.current_pointer_frame = drag_info.grab_frame;
1834 drag_info.current_pointer_x = drag_info.grab_x;
1835 drag_info.current_pointer_y = drag_info.grab_y;
1836 drag_info.last_pointer_x = drag_info.current_pointer_x;
1837 drag_info.last_pointer_y = drag_info.current_pointer_y;
1838 drag_info.cumulative_x_drag = 0;
1839 drag_info.cumulative_y_drag = 0;
1840 drag_info.first_move = true;
1841 drag_info.move_threshold_passed = false;
1842 drag_info.want_move_threshold = false;
1843 drag_info.pointer_frame_offset = 0;
1844 drag_info.brushing = false;
1845 drag_info.copied_location = 0;
1847 drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK,
1849 event->button.time);
1851 if (session && session->transport_rolling()) {
1852 drag_info.was_rolling = true;
1854 drag_info.was_rolling = false;
1857 switch (snap_type) {
1858 case SnapToRegionStart:
1859 case SnapToRegionEnd:
1860 case SnapToRegionSync:
1861 case SnapToRegionBoundary:
1862 build_region_boundary_cache ();
1870 Editor::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t time)
1872 drag_info.item->ungrab (0);
1873 drag_info.item = new_item;
1876 cursor = which_grabber_cursor ();
1879 drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK, *cursor, time);
1883 Editor::end_grab (ArdourCanvas::Item* item, GdkEvent* event)
1885 bool did_drag = false;
1887 stop_canvas_autoscroll ();
1889 if (drag_info.item == 0) {
1893 drag_info.item->ungrab (event->button.time);
1895 if (drag_info.finished_callback) {
1896 drag_info.last_pointer_x = drag_info.current_pointer_x;
1897 drag_info.last_pointer_y = drag_info.current_pointer_y;
1898 (this->*(drag_info.finished_callback)) (item, event);
1901 did_drag = !drag_info.first_move;
1903 hide_verbose_canvas_cursor();
1906 drag_info.copy = false;
1907 drag_info.motion_callback = 0;
1908 drag_info.finished_callback = 0;
1909 drag_info.last_trackview = 0;
1910 drag_info.last_frame_position = 0;
1911 drag_info.grab_frame = 0;
1912 drag_info.last_pointer_frame = 0;
1913 drag_info.current_pointer_frame = 0;
1914 drag_info.brushing = false;
1916 if (drag_info.copied_location) {
1917 delete drag_info.copied_location;
1918 drag_info.copied_location = 0;
1925 Editor::start_fade_in_grab (ArdourCanvas::Item* item, GdkEvent* event)
1927 drag_info.item = item;
1928 drag_info.motion_callback = &Editor::fade_in_drag_motion_callback;
1929 drag_info.finished_callback = &Editor::fade_in_drag_finished_callback;
1933 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
1934 fatal << _("programming error: fade in canvas item has no regionview data pointer!") << endmsg;
1938 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1940 drag_info.pointer_frame_offset = drag_info.grab_frame - ((nframes_t) arv->audio_region()->fade_in()->back()->when + arv->region()->position());
1944 Editor::fade_in_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1946 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1948 nframes_t fade_length;
1950 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1951 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1957 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1961 if (pos < (arv->region()->position() + 64)) {
1962 fade_length = 64; // this should be a minimum defined somewhere
1963 } else if (pos > arv->region()->last_frame()) {
1964 fade_length = arv->region()->length();
1966 fade_length = pos - arv->region()->position();
1968 /* mapover the region selection */
1970 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1972 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1978 tmp->reset_fade_in_shape_width (fade_length);
1981 show_verbose_duration_cursor (arv->region()->position(), arv->region()->position() + fade_length, 10);
1983 drag_info.first_move = false;
1987 Editor::fade_in_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1989 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1991 nframes_t fade_length;
1993 if (drag_info.first_move) return;
1995 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1996 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2001 if (pos < (arv->region()->position() + 64)) {
2002 fade_length = 64; // this should be a minimum defined somewhere
2003 } else if (pos > arv->region()->last_frame()) {
2004 fade_length = arv->region()->length();
2006 fade_length = pos - arv->region()->position();
2009 begin_reversible_command (_("change fade in length"));
2011 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2013 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2019 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
2020 XMLNode &before = alist->get_state();
2022 tmp->audio_region()->set_fade_in_length (fade_length);
2023 tmp->audio_region()->set_fade_in_active (true);
2025 XMLNode &after = alist->get_state();
2026 session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2029 commit_reversible_command ();
2033 Editor::start_fade_out_grab (ArdourCanvas::Item* item, GdkEvent* event)
2035 drag_info.item = item;
2036 drag_info.motion_callback = &Editor::fade_out_drag_motion_callback;
2037 drag_info.finished_callback = &Editor::fade_out_drag_finished_callback;
2041 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
2042 fatal << _("programming error: fade out canvas item has no regionview data pointer!") << endmsg;
2046 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
2048 drag_info.pointer_frame_offset = drag_info.grab_frame - (arv->region()->length() - (nframes_t) arv->audio_region()->fade_out()->back()->when + arv->region()->position());
2052 Editor::fade_out_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2054 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
2056 nframes_t fade_length;
2058 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2059 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2064 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2068 if (pos > (arv->region()->last_frame() - 64)) {
2069 fade_length = 64; // this should really be a minimum fade defined somewhere
2071 else if (pos < arv->region()->position()) {
2072 fade_length = arv->region()->length();
2075 fade_length = arv->region()->last_frame() - pos;
2078 /* mapover the region selection */
2080 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2082 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2088 tmp->reset_fade_out_shape_width (fade_length);
2091 show_verbose_duration_cursor (arv->region()->last_frame() - fade_length, arv->region()->last_frame(), 10);
2093 drag_info.first_move = false;
2097 Editor::fade_out_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2099 if (drag_info.first_move) return;
2101 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
2103 nframes_t fade_length;
2105 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2106 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2112 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2116 if (pos > (arv->region()->last_frame() - 64)) {
2117 fade_length = 64; // this should really be a minimum fade defined somewhere
2119 else if (pos < arv->region()->position()) {
2120 fade_length = arv->region()->length();
2123 fade_length = arv->region()->last_frame() - pos;
2126 begin_reversible_command (_("change fade out length"));
2128 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2130 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2136 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
2137 XMLNode &before = alist->get_state();
2139 tmp->audio_region()->set_fade_out_length (fade_length);
2140 tmp->audio_region()->set_fade_out_active (true);
2142 XMLNode &after = alist->get_state();
2143 session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2146 commit_reversible_command ();
2150 Editor::start_cursor_grab (ArdourCanvas::Item* item, GdkEvent* event)
2152 drag_info.item = item;
2153 drag_info.motion_callback = &Editor::cursor_drag_motion_callback;
2154 drag_info.finished_callback = &Editor::cursor_drag_finished_callback;
2158 if ((drag_info.data = (item->get_data ("cursor"))) == 0) {
2159 fatal << _("programming error: cursor canvas item has no cursor data pointer!") << endmsg;
2163 Cursor* cursor = (Cursor *) drag_info.data;
2165 if (cursor == playhead_cursor) {
2166 _dragging_playhead = true;
2168 if (session && drag_info.was_rolling) {
2169 session->request_stop ();
2172 if (session && session->is_auditioning()) {
2173 session->cancel_audition ();
2177 drag_info.pointer_frame_offset = drag_info.grab_frame - cursor->current_frame;
2179 show_verbose_time_cursor (cursor->current_frame, 10);
2183 Editor::cursor_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2185 Cursor* cursor = (Cursor *) drag_info.data;
2186 nframes_t adjusted_frame;
2188 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2189 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2195 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2196 if (cursor == playhead_cursor) {
2197 snap_to (adjusted_frame);
2201 if (adjusted_frame == drag_info.last_pointer_frame) return;
2203 cursor->set_position (adjusted_frame);
2205 UpdateAllTransportClocks (cursor->current_frame);
2207 show_verbose_time_cursor (cursor->current_frame, 10);
2209 drag_info.last_pointer_frame = adjusted_frame;
2210 drag_info.first_move = false;
2214 Editor::cursor_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2216 if (drag_info.first_move) return;
2218 cursor_drag_motion_callback (item, event);
2220 _dragging_playhead = false;
2222 if (item == &playhead_cursor->canvas_item) {
2224 session->request_locate (playhead_cursor->current_frame, drag_info.was_rolling);
2230 Editor::update_marker_drag_item (Location *location)
2232 double x1 = frame_to_pixel (location->start());
2233 double x2 = frame_to_pixel (location->end());
2235 if (location->is_mark()) {
2236 marker_drag_line_points.front().set_x(x1);
2237 marker_drag_line_points.back().set_x(x1);
2238 marker_drag_line->property_points() = marker_drag_line_points;
2241 range_marker_drag_rect->property_x1() = x1;
2242 range_marker_drag_rect->property_x2() = x2;
2247 Editor::start_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2251 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2252 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2258 Location *location = find_location_from_marker (marker, is_start);
2260 drag_info.item = item;
2261 drag_info.data = marker;
2262 drag_info.motion_callback = &Editor::marker_drag_motion_callback;
2263 drag_info.finished_callback = &Editor::marker_drag_finished_callback;
2267 _dragging_edit_point = true;
2269 drag_info.copied_location = new Location (*location);
2270 drag_info.pointer_frame_offset = drag_info.grab_frame - (is_start ? location->start() : location->end());
2272 update_marker_drag_item (location);
2274 if (location->is_mark()) {
2275 // marker_drag_line->show();
2276 // marker_drag_line->raise_to_top();
2278 range_marker_drag_rect->show();
2279 range_marker_drag_rect->raise_to_top();
2283 show_verbose_time_cursor (location->start(), 10);
2285 show_verbose_time_cursor (location->end(), 10);
2288 Selection::Operation op = Keyboard::selection_type (event->button.state);
2291 case Selection::Toggle:
2292 selection->toggle (marker);
2294 case Selection::Set:
2295 selection->set (marker);
2297 case Selection::Extend:
2298 selection->add (marker);
2300 case Selection::Add:
2301 selection->add (marker);
2307 Editor::marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2310 Marker* marker = (Marker *) drag_info.data;
2311 Location *real_location;
2312 Location *copy_location;
2314 bool move_both = false;
2317 if (drag_info.pointer_frame_offset <= drag_info.current_pointer_frame) {
2318 newframe = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2323 nframes_t next = newframe;
2325 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2326 snap_to (newframe, 0, true);
2329 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
2333 /* call this to find out if its the start or end */
2335 if ((real_location = find_location_from_marker (marker, is_start)) == 0) {
2339 if (real_location->locked()) {
2343 /* use the copy that we're "dragging" around */
2345 copy_location = drag_info.copied_location;
2347 f_delta = copy_location->end() - copy_location->start();
2349 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2353 if (copy_location->is_mark()) {
2356 copy_location->set_start (newframe);
2360 if (is_start) { // start-of-range marker
2363 copy_location->set_start (newframe);
2364 copy_location->set_end (newframe + f_delta);
2365 } else if (newframe < copy_location->end()) {
2366 copy_location->set_start (newframe);
2368 snap_to (next, 1, true);
2369 copy_location->set_end (next);
2370 copy_location->set_start (newframe);
2373 } else { // end marker
2376 copy_location->set_end (newframe);
2377 copy_location->set_start (newframe - f_delta);
2378 } else if (newframe > copy_location->start()) {
2379 copy_location->set_end (newframe);
2381 } else if (newframe > 0) {
2382 snap_to (next, -1, true);
2383 copy_location->set_start (next);
2384 copy_location->set_end (newframe);
2389 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
2390 drag_info.first_move = false;
2392 update_marker_drag_item (copy_location);
2394 LocationMarkers* lm = find_location_markers (real_location);
2395 lm->set_position (copy_location->start(), copy_location->end());
2396 edit_point_clock.set (copy_location->start());
2398 show_verbose_time_cursor (newframe, 10);
2402 Editor::marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2404 if (drag_info.first_move) {
2405 marker_drag_motion_callback (item, event);
2409 _dragging_edit_point = false;
2411 Marker* marker = (Marker *) drag_info.data;
2414 begin_reversible_command ( _("move marker") );
2415 XMLNode &before = session->locations()->get_state();
2417 Location * location = find_location_from_marker (marker, is_start);
2421 if (location->locked()) {
2425 if (location->is_mark()) {
2426 location->set_start (drag_info.copied_location->start());
2428 location->set (drag_info.copied_location->start(), drag_info.copied_location->end());
2432 XMLNode &after = session->locations()->get_state();
2433 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
2434 commit_reversible_command ();
2436 marker_drag_line->hide();
2437 range_marker_drag_rect->hide();
2441 Editor::start_meter_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2444 MeterMarker* meter_marker;
2446 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2447 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2451 meter_marker = dynamic_cast<MeterMarker*> (marker);
2453 MetricSection& section (meter_marker->meter());
2455 if (!section.movable()) {
2459 drag_info.item = item;
2460 drag_info.copy = false;
2461 drag_info.data = marker;
2462 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2463 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2467 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2469 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2473 Editor::start_meter_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2476 MeterMarker* meter_marker;
2478 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2479 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2483 meter_marker = dynamic_cast<MeterMarker*> (marker);
2485 // create a dummy marker for visual representation of moving the copy.
2486 // The actual copying is not done before we reach the finish callback.
2488 snprintf (name, sizeof(name), "%g/%g", meter_marker->meter().beats_per_bar(), meter_marker->meter().note_divisor ());
2489 MeterMarker* new_marker = new MeterMarker(*this, *meter_group, ARDOUR_UI::config()->canvasvar_MeterMarker.get(), name,
2490 *new MeterSection(meter_marker->meter()));
2492 drag_info.item = &new_marker->the_item();
2493 drag_info.copy = true;
2494 drag_info.data = new_marker;
2495 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2496 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2500 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2502 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2506 Editor::meter_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2508 MeterMarker* marker = (MeterMarker *) drag_info.data;
2509 nframes_t adjusted_frame;
2511 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2512 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2518 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2519 snap_to (adjusted_frame);
2522 if (adjusted_frame == drag_info.last_pointer_frame) return;
2524 marker->set_position (adjusted_frame);
2527 drag_info.last_pointer_frame = adjusted_frame;
2528 drag_info.first_move = false;
2530 show_verbose_time_cursor (adjusted_frame, 10);
2534 Editor::meter_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2536 if (drag_info.first_move) return;
2538 meter_marker_drag_motion_callback (drag_info.item, event);
2540 MeterMarker* marker = (MeterMarker *) drag_info.data;
2543 TempoMap& map (session->tempo_map());
2544 map.bbt_time (drag_info.last_pointer_frame, when);
2546 if (drag_info.copy == true) {
2547 begin_reversible_command (_("copy meter mark"));
2548 XMLNode &before = map.get_state();
2549 map.add_meter (marker->meter(), when);
2550 XMLNode &after = map.get_state();
2551 session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2552 commit_reversible_command ();
2554 // delete the dummy marker we used for visual representation of copying.
2555 // a new visual marker will show up automatically.
2558 begin_reversible_command (_("move meter mark"));
2559 XMLNode &before = map.get_state();
2560 map.move_meter (marker->meter(), when);
2561 XMLNode &after = map.get_state();
2562 session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2563 commit_reversible_command ();
2568 Editor::start_tempo_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2571 TempoMarker* tempo_marker;
2573 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2574 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2578 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2579 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2583 MetricSection& section (tempo_marker->tempo());
2585 if (!section.movable()) {
2589 drag_info.item = item;
2590 drag_info.copy = false;
2591 drag_info.data = marker;
2592 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2593 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2597 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2598 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2602 Editor::start_tempo_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2605 TempoMarker* tempo_marker;
2607 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2608 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2612 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2613 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2617 // create a dummy marker for visual representation of moving the copy.
2618 // The actual copying is not done before we reach the finish callback.
2620 snprintf (name, sizeof (name), "%.2f", tempo_marker->tempo().beats_per_minute());
2621 TempoMarker* new_marker = new TempoMarker(*this, *tempo_group, ARDOUR_UI::config()->canvasvar_TempoMarker.get(), name,
2622 *new TempoSection(tempo_marker->tempo()));
2624 drag_info.item = &new_marker->the_item();
2625 drag_info.copy = true;
2626 drag_info.data = new_marker;
2627 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2628 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2632 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2634 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2638 Editor::tempo_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2640 TempoMarker* marker = (TempoMarker *) drag_info.data;
2641 nframes_t adjusted_frame;
2643 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2644 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2650 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2651 snap_to (adjusted_frame);
2654 if (adjusted_frame == drag_info.last_pointer_frame) return;
2656 /* OK, we've moved far enough to make it worth actually move the thing. */
2658 marker->set_position (adjusted_frame);
2660 show_verbose_time_cursor (adjusted_frame, 10);
2662 drag_info.last_pointer_frame = adjusted_frame;
2663 drag_info.first_move = false;
2667 Editor::tempo_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2669 if (drag_info.first_move) return;
2671 tempo_marker_drag_motion_callback (drag_info.item, event);
2673 TempoMarker* marker = (TempoMarker *) drag_info.data;
2676 TempoMap& map (session->tempo_map());
2677 map.bbt_time (drag_info.last_pointer_frame, when);
2679 if (drag_info.copy == true) {
2680 begin_reversible_command (_("copy tempo mark"));
2681 XMLNode &before = map.get_state();
2682 map.add_tempo (marker->tempo(), when);
2683 XMLNode &after = map.get_state();
2684 session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2685 commit_reversible_command ();
2687 // delete the dummy marker we used for visual representation of copying.
2688 // a new visual marker will show up automatically.
2691 begin_reversible_command (_("move tempo mark"));
2692 XMLNode &before = map.get_state();
2693 map.move_tempo (marker->tempo(), when);
2694 XMLNode &after = map.get_state();
2695 session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2696 commit_reversible_command ();
2701 Editor::remove_gain_control_point (ArdourCanvas::Item*item, GdkEvent* event)
2703 ControlPoint* control_point;
2705 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2706 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2710 // We shouldn't remove the first or last gain point
2711 if (control_point->line().is_last_point(*control_point) ||
2712 control_point->line().is_first_point(*control_point)) {
2716 control_point->line().remove_point (*control_point);
2720 Editor::remove_control_point (ArdourCanvas::Item* item, GdkEvent* event)
2722 ControlPoint* control_point;
2724 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2725 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2729 control_point->line().remove_point (*control_point);
2733 Editor::start_control_point_grab (ArdourCanvas::Item* item, GdkEvent* event)
2735 ControlPoint* control_point;
2737 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2738 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2742 drag_info.item = item;
2743 drag_info.data = control_point;
2744 drag_info.motion_callback = &Editor::control_point_drag_motion_callback;
2745 drag_info.finished_callback = &Editor::control_point_drag_finished_callback;
2747 start_grab (event, fader_cursor);
2749 // start the grab at the center of the control point so
2750 // the point doesn't 'jump' to the mouse after the first drag
2751 drag_info.grab_x = control_point->get_x();
2752 drag_info.grab_y = control_point->get_y();
2754 control_point->line().parent_group().i2w(drag_info.grab_x, drag_info.grab_y);
2755 track_canvas->w2c(drag_info.grab_x, drag_info.grab_y, drag_info.grab_x, drag_info.grab_y);
2757 drag_info.grab_frame = pixel_to_frame(drag_info.grab_x);
2759 control_point->line().start_drag (control_point, drag_info.grab_frame, 0);
2761 double fraction = 1.0 - ((control_point->get_y() - control_point->line().y_position()) / (double)control_point->line().height());
2762 set_verbose_canvas_cursor (control_point->line().get_verbose_cursor_string (fraction),
2763 drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
2765 show_verbose_canvas_cursor ();
2769 Editor::control_point_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2771 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2773 double dx = drag_info.current_pointer_x - drag_info.last_pointer_x;
2774 double dy = drag_info.current_pointer_y - drag_info.last_pointer_y;
2776 if (event->button.state & Keyboard::SecondaryModifier) {
2781 double cx = drag_info.grab_x + drag_info.cumulative_x_drag + dx;
2782 double cy = drag_info.grab_y + drag_info.cumulative_y_drag + dy;
2784 // calculate zero crossing point. back off by .01 to stay on the
2785 // positive side of zero
2787 double zero_gain_y = (1.0 - ZERO_GAIN_FRACTION) * cp->line().height() - .01;
2788 cp->line().parent_group().i2w(_unused, zero_gain_y);
2790 // make sure we hit zero when passing through
2791 if ((cy < zero_gain_y and (cy - dy) > zero_gain_y)
2792 or (cy > zero_gain_y and (cy - dy) < zero_gain_y)) {
2796 if (drag_info.x_constrained) {
2797 cx = drag_info.grab_x;
2799 if (drag_info.y_constrained) {
2800 cy = drag_info.grab_y;
2803 drag_info.cumulative_x_drag = cx - drag_info.grab_x;
2804 drag_info.cumulative_y_drag = cy - drag_info.grab_y;
2806 cp->line().parent_group().w2i (cx, cy);
2810 cy = min ((double) (cp->line().y_position() + cp->line().height()), cy);
2812 //translate cx to frames
2813 nframes_t cx_frames = unit_to_frame (cx);
2815 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier()) && !drag_info.x_constrained) {
2816 snap_to (cx_frames);
2819 const double fraction = 1.0 - ((cy - cp->line().y_position()) / (double)cp->line().height());
2823 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) {
2829 cp->line().point_drag (*cp, cx_frames , fraction, push);
2831 set_verbose_canvas_cursor_text (cp->line().get_verbose_cursor_string (fraction));
2833 drag_info.first_move = false;
2837 Editor::control_point_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2839 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2841 if (drag_info.first_move) {
2845 if ((event->type == GDK_BUTTON_RELEASE) && (event->button.button == 1) && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2846 reset_point_selection ();
2850 control_point_drag_motion_callback (item, event);
2852 cp->line().end_drag (cp);
2856 Editor::start_line_grab_from_regionview (ArdourCanvas::Item* item, GdkEvent* event)
2858 switch (mouse_mode) {
2860 assert(dynamic_cast<AudioRegionView*>(clicked_regionview));
2861 start_line_grab (dynamic_cast<AudioRegionView*>(clicked_regionview)->get_gain_line(), event);
2869 Editor::start_line_grab_from_line (ArdourCanvas::Item* item, GdkEvent* event)
2873 if ((al = reinterpret_cast<AutomationLine*> (item->get_data ("line"))) == 0) {
2874 fatal << _("programming error: line canvas item has no line pointer!") << endmsg;
2878 start_line_grab (al, event);
2882 Editor::start_line_grab (AutomationLine* line, GdkEvent* event)
2886 nframes_t frame_within_region;
2888 /* need to get x coordinate in terms of parent (TimeAxisItemView)
2892 cx = event->button.x;
2893 cy = event->button.y;
2894 line->parent_group().w2i (cx, cy);
2895 frame_within_region = (nframes_t) floor (cx * frames_per_unit);
2897 if (!line->control_points_adjacent (frame_within_region, current_line_drag_info.before,
2898 current_line_drag_info.after)) {
2899 /* no adjacent points */
2903 drag_info.item = &line->grab_item();
2904 drag_info.data = line;
2905 drag_info.motion_callback = &Editor::line_drag_motion_callback;
2906 drag_info.finished_callback = &Editor::line_drag_finished_callback;
2908 start_grab (event, fader_cursor);
2910 const double fraction = 1.0 - ((cy - line->y_position()) / (double)line->height());
2912 line->start_drag (0, drag_info.grab_frame, fraction);
2914 set_verbose_canvas_cursor (line->get_verbose_cursor_string (fraction),
2915 drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
2916 show_verbose_canvas_cursor ();
2920 Editor::line_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2922 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
2924 double dy = drag_info.current_pointer_y - drag_info.last_pointer_y;
2926 if (event->button.state & Keyboard::SecondaryModifier) {
2930 double cx = drag_info.current_pointer_x;
2931 double cy = drag_info.grab_y + drag_info.cumulative_y_drag + dy;
2933 // calculate zero crossing point. back off by .01 to stay on the
2934 // positive side of zero
2936 double zero_gain_y = (1.0 - ZERO_GAIN_FRACTION) * line->height() - .01;
2937 line->parent_group().i2w(_unused, zero_gain_y);
2939 // make sure we hit zero when passing through
2940 if ((cy < zero_gain_y and (cy - dy) > zero_gain_y)
2941 or (cy > zero_gain_y and (cy - dy) < zero_gain_y)) {
2945 drag_info.cumulative_y_drag = cy - drag_info.grab_y;
2947 line->parent_group().w2i (cx, cy);
2950 cy = min ((double) line->height(), cy);
2952 const double fraction = 1.0 - ((cy - line->y_position()) / (double)line->height());
2956 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) {
2962 line->line_drag (current_line_drag_info.before, current_line_drag_info.after, fraction, push);
2964 set_verbose_canvas_cursor_text (line->get_verbose_cursor_string (fraction));
2968 Editor::line_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2970 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
2971 line_drag_motion_callback (item, event);
2976 Editor::start_region_grab (ArdourCanvas::Item* item, GdkEvent* event)
2978 if (selection->regions.empty() || clicked_regionview == 0) {
2982 drag_info.copy = false;
2983 drag_info.item = item;
2984 drag_info.data = clicked_regionview;
2986 if (Config->get_edit_mode() == Splice) {
2987 drag_info.motion_callback = &Editor::region_drag_splice_motion_callback;
2988 drag_info.finished_callback = &Editor::region_drag_splice_finished_callback;
2990 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2991 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2997 TimeAxisView* tvp = clicked_axisview;
2998 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
3000 if (tv && tv->is_track()) {
3001 speed = tv->get_diskstream()->speed();
3004 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
3005 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
3006 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
3007 // we want a move threshold
3008 drag_info.want_move_threshold = true;
3010 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3012 begin_reversible_command (_("move region(s)"));
3016 Editor::start_create_region_grab (ArdourCanvas::Item* item, GdkEvent* event)
3018 drag_info.copy = false;
3019 drag_info.item = item;
3020 drag_info.data = clicked_axisview;
3021 drag_info.last_trackview = clicked_axisview;
3022 drag_info.motion_callback = &Editor::create_region_drag_motion_callback;
3023 drag_info.finished_callback = &Editor::create_region_drag_finished_callback;
3029 Editor::start_region_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
3031 if (selection->regions.empty() || clicked_regionview == 0) {
3035 drag_info.copy = true;
3036 drag_info.item = item;
3037 drag_info.data = clicked_regionview;
3041 TimeAxisView* tv = &clicked_regionview->get_time_axis_view();
3042 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
3045 if (rtv && rtv->is_track()) {
3046 speed = rtv->get_diskstream()->speed();
3049 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
3050 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
3051 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
3052 // we want a move threshold
3053 drag_info.want_move_threshold = true;
3054 drag_info.motion_callback = &Editor::region_drag_motion_callback;
3055 drag_info.finished_callback = &Editor::region_drag_finished_callback;
3056 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3060 Editor::start_region_brush_grab (ArdourCanvas::Item* item, GdkEvent* event)
3062 if (selection->regions.empty() || clicked_regionview == 0 || Config->get_edit_mode() == Splice) {
3066 drag_info.copy = false;
3067 drag_info.item = item;
3068 drag_info.data = clicked_regionview;
3069 drag_info.motion_callback = &Editor::region_drag_motion_callback;
3070 drag_info.finished_callback = &Editor::region_drag_finished_callback;
3075 TimeAxisView* tvp = clicked_axisview;
3076 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
3078 if (tv && tv->is_track()) {
3079 speed = tv->get_diskstream()->speed();
3082 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
3083 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
3084 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
3085 // we want a move threshold
3086 drag_info.want_move_threshold = true;
3087 drag_info.brushing = true;
3089 begin_reversible_command (_("Drag region brush"));
3093 Editor::possibly_copy_regions_during_grab (GdkEvent* event)
3095 if (drag_info.copy && drag_info.move_threshold_passed && drag_info.want_move_threshold) {
3097 drag_info.want_move_threshold = false; // don't copy again
3099 /* duplicate the region(s) */
3101 vector<RegionView*> new_regionviews;
3103 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3110 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
3111 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
3114 nrv = new AudioRegionView (*arv);
3116 nrv = new MidiRegionView (*mrv);
3121 nrv->get_canvas_group()->show ();
3123 new_regionviews.push_back (nrv);
3126 if (new_regionviews.empty()) {
3130 /* reset selection to new regionviews */
3132 selection->set (new_regionviews);
3134 /* reset drag_info data to reflect the fact that we are dragging the copies */
3136 drag_info.data = new_regionviews.front();
3138 swap_grab (new_regionviews.front()->get_canvas_group (), 0, event->motion.time);
3143 Editor::check_region_drag_possible (RouteTimeAxisView** tv)
3145 /* Which trackview is this ? */
3147 TimeAxisView* tvp = trackview_by_y_position (drag_info.current_pointer_y);
3148 (*tv) = dynamic_cast<RouteTimeAxisView*>(tvp);
3150 /* The region motion is only processed if the pointer is over
3154 if (!(*tv) || !(*tv)->is_track()) {
3155 /* To make sure we hide the verbose canvas cursor when the mouse is
3156 not held over and audiotrack.
3158 hide_verbose_canvas_cursor ();
3165 struct RegionSelectionByPosition {
3166 bool operator() (RegionView*a, RegionView* b) {
3167 return a->region()->position () < b->region()->position();
3172 Editor::region_drag_splice_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
3174 RouteTimeAxisView* tv;
3176 if (!check_region_drag_possible (&tv)) {
3180 if (!drag_info.move_threshold_passed) {
3186 if (drag_info.current_pointer_x - drag_info.grab_x > 0) {
3192 RegionSelection copy (selection->regions);
3194 RegionSelectionByPosition cmp;
3197 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
3199 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
3205 boost::shared_ptr<Playlist> playlist;
3207 if ((playlist = atv->playlist()) == 0) {
3211 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
3216 if (drag_info.current_pointer_frame < (*i)->region()->last_frame() + 1) {
3220 if (drag_info.current_pointer_frame > (*i)->region()->first_frame()) {
3226 playlist->shuffle ((*i)->region(), dir);
3228 drag_info.grab_x = drag_info.current_pointer_x;
3233 Editor::region_drag_splice_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3238 Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
3242 RegionView* rv = reinterpret_cast<RegionView*> (drag_info.data);
3243 nframes_t pending_region_position = 0;
3244 int32_t pointer_y_span = 0, canvas_pointer_y_span = 0, original_pointer_order;
3245 int32_t visible_y_high = 0, visible_y_low = 512; //high meaning higher numbered.. not the height on the screen
3246 bool clamp_y_axis = false;
3247 vector<int32_t> height_list(512) ;
3248 vector<int32_t>::iterator j;
3249 RouteTimeAxisView* tv;
3251 possibly_copy_regions_during_grab (event);
3253 if (!check_region_drag_possible (&tv)) {
3257 original_pointer_order = drag_info.last_trackview->order;
3259 /************************************************************
3261 ************************************************************/
3263 if (drag_info.brushing) {
3264 clamp_y_axis = true;
3269 if ((pointer_y_span = (drag_info.last_trackview->order - tv->order)) != 0) {
3271 int32_t children = 0, numtracks = 0;
3272 // XXX hard coding track limit, oh my, so very very bad
3273 bitset <1024> tracks (0x00);
3274 /* get a bitmask representing the visible tracks */
3276 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
3277 TimeAxisView *tracklist_timeview;
3278 tracklist_timeview = (*i);
3279 RouteTimeAxisView* rtv2 = dynamic_cast<RouteTimeAxisView*>(tracklist_timeview);
3280 TimeAxisView::Children children_list;
3282 /* zeroes are audio tracks. ones are other types. */
3284 if (!rtv2->hidden()) {
3286 if (visible_y_high < rtv2->order) {
3287 visible_y_high = rtv2->order;
3289 if (visible_y_low > rtv2->order) {
3290 visible_y_low = rtv2->order;
3293 if (!rtv2->is_track()) {
3294 tracks = tracks |= (0x01 << rtv2->order);
3297 height_list[rtv2->order] = (*i)->height;
3299 if ((children_list = rtv2->get_child_list()).size() > 0) {
3300 for (TimeAxisView::Children::iterator j = children_list.begin(); j != children_list.end(); ++j) {
3301 tracks = tracks |= (0x01 << (rtv2->order + children));
3302 height_list[rtv2->order + children] = (*j)->height;
3310 /* find the actual span according to the canvas */
3312 canvas_pointer_y_span = pointer_y_span;
3313 if (drag_info.last_trackview->order >= tv->order) {
3315 for (y = tv->order; y < drag_info.last_trackview->order; y++) {
3316 if (height_list[y] == 0 ) {
3317 canvas_pointer_y_span--;
3322 for (y = drag_info.last_trackview->order;y <= tv->order; y++) {
3323 if ( height_list[y] == 0 ) {
3324 canvas_pointer_y_span++;
3329 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3330 RegionView* rv2 = (*i);
3331 double ix1, ix2, iy1, iy2;
3334 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3335 rv2->get_canvas_group()->i2w (ix1, iy1);
3336 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3337 RouteTimeAxisView* rtv2 = dynamic_cast<RouteTimeAxisView*>(tvp2);
3339 if (rtv2->order != original_pointer_order) {
3340 /* this isn't the pointer track */
3342 if (canvas_pointer_y_span > 0) {
3344 /* moving up the canvas */
3345 if ((rtv2->order - canvas_pointer_y_span) >= visible_y_low) {
3347 int32_t visible_tracks = 0;
3348 while (visible_tracks < canvas_pointer_y_span ) {
3351 while (height_list[rtv2->order - (visible_tracks - n)] == 0) {
3352 /* we're passing through a hidden track */
3357 if (tracks[rtv2->order - (canvas_pointer_y_span - n)] != 0x00) {
3358 clamp_y_axis = true;
3362 clamp_y_axis = true;
3365 } else if (canvas_pointer_y_span < 0) {
3367 /*moving down the canvas*/
3369 if ((rtv2->order - (canvas_pointer_y_span - n)) <= visible_y_high) { // we will overflow
3372 int32_t visible_tracks = 0;
3374 while (visible_tracks > canvas_pointer_y_span ) {
3377 while (height_list[rtv2->order - (visible_tracks - n)] == 0) {
3381 if ( tracks[rtv2->order - ( canvas_pointer_y_span - n)] != 0x00) {
3382 clamp_y_axis = true;
3387 clamp_y_axis = true;
3393 /* this is the pointer's track */
3394 if ((rtv2->order - pointer_y_span) > visible_y_high) { // we will overflow
3395 clamp_y_axis = true;
3396 } else if ((rtv2->order - pointer_y_span) < visible_y_low) { // we will underflow
3397 clamp_y_axis = true;
3405 } else if (drag_info.last_trackview == tv) {
3406 clamp_y_axis = true;
3410 if (!clamp_y_axis) {
3411 drag_info.last_trackview = tv;
3414 /************************************************************
3416 ************************************************************/
3418 /* compute the amount of pointer motion in frames, and where
3419 the region would be if we moved it by that much.
3422 if ( drag_info.move_threshold_passed ) {
3424 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
3426 nframes_t sync_frame;
3427 nframes_t sync_offset;
3430 pending_region_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
3432 sync_offset = rv->region()->sync_offset (sync_dir);
3434 /* we don't handle a sync point that lies before zero.
3436 if (sync_dir >= 0 || (sync_dir < 0 && pending_region_position >= sync_offset)) {
3437 sync_frame = pending_region_position + (sync_dir*sync_offset);
3439 /* we snap if the snap modifier is not enabled.
3442 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3443 snap_to (sync_frame);
3446 pending_region_position = rv->region()->adjust_to_sync (sync_frame);
3449 pending_region_position = drag_info.last_frame_position;
3453 pending_region_position = 0;
3456 if (pending_region_position > max_frames - rv->region()->length()) {
3457 pending_region_position = drag_info.last_frame_position;
3460 // printf ("3: pending_region_position= %lu %lu\n", pending_region_position, drag_info.last_frame_position );
3462 bool x_move_allowed = ( !drag_info.x_constrained && (Config->get_edit_mode() != Lock)) || ( drag_info.x_constrained && (Config->get_edit_mode() == Lock)) ;
3463 if ( pending_region_position != drag_info.last_frame_position && x_move_allowed ) {
3465 /* now compute the canvas unit distance we need to move the regionview
3466 to make it appear at the new location.
3469 if (pending_region_position > drag_info.last_frame_position) {
3470 x_delta = ((double) (pending_region_position - drag_info.last_frame_position) / frames_per_unit);
3472 x_delta = -((double) (drag_info.last_frame_position - pending_region_position) / frames_per_unit);
3475 drag_info.last_frame_position = pending_region_position;
3482 /* threshold not passed */
3487 /*************************************************************
3489 ************************************************************/
3491 if (x_delta == 0 && (pointer_y_span == 0)) {
3492 /* haven't reached next snap point, and we're not switching
3493 trackviews. nothing to do.
3500 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3502 RegionView* rv2 = (*i);
3504 // If any regionview is at zero, we need to know so we can stop further leftward motion.
3506 double ix1, ix2, iy1, iy2;
3507 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3508 rv2->get_canvas_group()->i2w (ix1, iy1);
3517 /*************************************************************
3519 ************************************************************/
3523 if (drag_info.first_move) {
3524 if (drag_info.move_threshold_passed) {
3535 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
3536 const list<RegionView*>& layered_regions = selection->regions.by_layer();
3538 for (list<RegionView*>::const_iterator i = layered_regions.begin(); i != layered_regions.end(); ++i) {
3540 RegionView* rv = (*i);
3541 double ix1, ix2, iy1, iy2;
3542 int32_t temp_pointer_y_span = pointer_y_span;
3544 /* get item BBox, which will be relative to parent. so we have
3545 to query on a child, then convert to world coordinates using
3549 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3550 rv->get_canvas_group()->i2w (ix1, iy1);
3551 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3552 RouteTimeAxisView* canvas_rtv = dynamic_cast<RouteTimeAxisView*>(tvp2);
3553 RouteTimeAxisView* temp_rtv;
3555 if ((pointer_y_span != 0) && !clamp_y_axis) {
3558 for (j = height_list.begin(); j!= height_list.end(); j++) {
3559 if (x == canvas_rtv->order) {
3560 /* we found the track the region is on */
3561 if (x != original_pointer_order) {
3562 /*this isn't from the same track we're dragging from */
3563 temp_pointer_y_span = canvas_pointer_y_span;
3565 while (temp_pointer_y_span > 0) {
3566 /* we're moving up canvas-wise,
3567 so we need to find the next track height
3569 if (j != height_list.begin()) {
3572 if (x != original_pointer_order) {
3573 /* we're not from the dragged track, so ignore hidden tracks. */
3575 temp_pointer_y_span++;
3579 temp_pointer_y_span--;
3581 while (temp_pointer_y_span < 0) {
3583 if (x != original_pointer_order) {
3585 temp_pointer_y_span--;
3589 if (j != height_list.end()) {
3592 temp_pointer_y_span++;
3594 /* find out where we'll be when we move and set height accordingly */
3596 tvp2 = trackview_by_y_position (iy1 + y_delta);
3597 temp_rtv = dynamic_cast<RouteTimeAxisView*>(tvp2);
3598 rv->set_y_position_and_height (0, temp_rtv->height);
3600 /* if you un-comment the following, the region colours will follow the track colours whilst dragging,
3601 personally, i think this can confuse things, but never mind.
3604 //const GdkColor& col (temp_rtv->view->get_region_color());
3605 //rv->set_color (const_cast<GdkColor&>(col));
3612 /* prevent the regionview from being moved to before
3613 the zero position on the canvas.
3618 if (-x_delta > ix1) {
3621 } else if ((x_delta > 0) && (rv->region()->last_frame() > max_frames - x_delta)) {
3622 x_delta = max_frames - rv->region()->last_frame();
3626 if (drag_info.first_move) {
3628 /* hide any dependent views */
3630 rv->get_time_axis_view().hide_dependent_views (*rv);
3632 /* this is subtle. raising the regionview itself won't help,
3633 because raise_to_top() just puts the item on the top of
3634 its parent's stack. so, we need to put the trackview canvas_display group
3635 on the top, since its parent is the whole canvas.
3638 rv->get_canvas_group()->raise_to_top();
3639 rv->get_time_axis_view().canvas_display->raise_to_top();
3640 cursor_group->raise_to_top();
3642 rv->fake_set_opaque (true);
3645 if (drag_info.brushing) {
3646 mouse_brush_insert_region (rv, pending_region_position);
3648 rv->move (x_delta, y_delta);
3651 } /* foreach region */
3655 if (drag_info.first_move && drag_info.move_threshold_passed) {
3656 cursor_group->raise_to_top();
3657 drag_info.first_move = false;
3660 if (x_delta != 0 && !drag_info.brushing) {
3661 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3666 Editor::region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3669 RegionView* rvdi = reinterpret_cast<RegionView *> (drag_info.data);
3670 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
3671 bool nocommit = true;
3673 RouteTimeAxisView* rtv;
3674 bool regionview_y_movement;
3675 bool regionview_x_movement;
3676 vector<RegionView*> copies;
3677 list <boost::shared_ptr<Playlist > > used_playlists;
3678 list <sigc::connection > used_connections;
3679 bool preserve_selection = false;
3681 /* first_move is set to false if the regionview has been moved in the
3685 if (drag_info.first_move) {
3692 /* The regionview has been moved at some stage during the grab so we need
3693 to account for any mouse movement between this event and the last one.
3696 region_drag_motion_callback (item, event);
3698 if (Config->get_edit_mode() == Splice && !pre_drag_region_selection.empty()) {
3699 selection->set (pre_drag_region_selection);
3700 pre_drag_region_selection.clear ();
3703 if (drag_info.brushing) {
3704 /* all changes were made during motion event handlers */
3706 if (drag_info.copy) {
3707 for (list<RegionView*>::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
3708 copies.push_back (*i);
3715 /* adjust for track speed */
3718 rtv = dynamic_cast<RouteTimeAxisView*> (drag_info.last_trackview);
3719 if (rtv && rtv->get_diskstream()) {
3720 speed = rtv->get_diskstream()->speed();
3723 regionview_x_movement = (drag_info.last_frame_position != (nframes_t) (rvdi->region()->position()/speed));
3724 regionview_y_movement = (drag_info.last_trackview != &rvdi->get_time_axis_view());
3726 //printf ("last_frame: %s position is %lu %g\n", rv->get_time_axis_view().name().c_str(), drag_info.last_frame_position, speed);
3727 //printf ("last_rackview: %s \n", drag_info.last_trackview->name().c_str());
3731 if (drag_info.copy) {
3732 if (drag_info.x_constrained) {
3733 op_string = _("fixed time region copy");
3735 op_string = _("region copy");
3738 if (drag_info.x_constrained) {
3739 op_string = _("fixed time region drag");
3741 op_string = _("region drag");
3745 begin_reversible_command (op_string);
3747 if (regionview_y_movement) {
3749 /* moved to a different track. */
3751 vector<RegionView*> new_selection;
3753 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ) {
3755 RegionView* rv = (*i);
3757 double ix1, ix2, iy1, iy2;
3759 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3760 rv->get_canvas_group()->i2w (ix1, iy1);
3762 RouteTimeAxisView* rtv2 = dynamic_cast<RouteTimeAxisView*>(trackview_by_y_position (iy1));
3764 boost::shared_ptr<Playlist> from_playlist = rv->region()->playlist();
3765 boost::shared_ptr<Playlist> to_playlist = rtv2->playlist();
3767 where = (nframes_t) (unit_to_frame (ix1) * speed);
3768 boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region()));
3770 if (! to_playlist->frozen()) {
3772 we haven't seen this playlist before.
3773 we want to freeze it because we don't want to relayer per-region.
3774 its much better to do that just once if the playlist is large.
3778 connect so the selection is changed when the new regionview finally appears (after thaw).
3779 keep track of it so we can disconnect later.
3782 sigc::connection c = rtv2->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_and_select_new_region_view));
3783 used_connections.push_back (c);
3786 session->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
3788 /* remember used playlists so we can thaw them later */
3789 used_playlists.push_back(to_playlist);
3790 to_playlist->freeze();
3793 /* undo the previous hide_dependent_views so that xfades don't
3794 disappear on copying regions
3797 rv->get_time_axis_view().reveal_dependent_views (*rv);
3799 if (!drag_info.copy) {
3801 /* the region that used to be in the old playlist is not
3802 moved to the new one - we make a copy of it. as a result,
3803 any existing editor for the region should no longer be
3807 rv->hide_region_editor();
3808 rv->fake_set_opaque (false);
3810 session->add_command (new MementoCommand<Playlist>(*from_playlist, &from_playlist->get_state(), 0));
3811 from_playlist->remove_region ((rv->region()));
3812 session->add_command (new MementoCommand<Playlist>(*from_playlist, 0, &from_playlist->get_state()));
3816 /* the regionview we dragged around is a temporary copy, queue it for deletion */
3818 copies.push_back (rv);
3821 latest_regionviews.clear ();
3822 sigc::connection c = rtv2->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3823 session->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
3825 to_playlist->add_region (new_region, where);
3826 session->add_command (new MementoCommand<Playlist>(*to_playlist, 0, &to_playlist->get_state()));
3829 if (!latest_regionviews.empty()) {
3830 new_selection.insert (new_selection.end(), latest_regionviews.begin(), latest_regionviews.end());
3833 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
3834 was selected in all of them, then removing it from the playlist will have removed all
3835 trace of it from the selection (i.e. there were N regions selected, we removed 1,
3836 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
3837 corresponding regionview, and the selection is now empty).
3839 this could have invalidated any and all iterators into the region selection.
3841 the heuristic we use here is: if the region selection is empty, break out of the loop
3842 here. if the region selection is not empty, then restart the loop because we know that
3843 we must have removed at least the region(view) we've just been working on as well as any
3844 that we processed on previous iterations.
3846 EXCEPT .... if we are doing a copy drag, then the selection hasn't been modified and
3847 we can just iterate.
3851 if (drag_info.copy) {
3854 if (selection->regions.empty()) {
3858 XXX see above .. but we just froze the playlists.. we have to keep iterating, right?
3861 //i = selection->regions.by_layer().begin();
3869 /* motion within a single track */
3871 list<RegionView*> regions = selection->regions.by_layer();
3873 for (list<RegionView*>::iterator i = regions.begin(); i != regions.end(); ++i) {
3875 RegionView* rv = (*i);
3876 boost::shared_ptr<Playlist> to_playlist = (*i)->region()->playlist();
3877 RouteTimeAxisView* from_rtv = dynamic_cast<RouteTimeAxisView*> (&(rv->get_time_axis_view()));
3879 if (!rv->region()->can_move()) {
3883 if (regionview_x_movement) {
3884 double ownspeed = 1.0;
3886 if (from_rtv && from_rtv->get_diskstream()) {
3887 ownspeed = from_rtv->get_diskstream()->speed();
3890 /* base the new region position on the current position of the regionview.*/
3892 double ix1, ix2, iy1, iy2;
3894 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3895 rv->get_canvas_group()->i2w (ix1, iy1);
3896 where = (nframes_t) (unit_to_frame (ix1) * ownspeed);
3900 where = rv->region()->position();
3903 if (! to_playlist->frozen()) {
3904 sigc::connection c = from_rtv->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_and_select_new_region_view));
3905 used_connections.push_back (c);
3908 session->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
3910 used_playlists.push_back(to_playlist);
3911 to_playlist->freeze();
3914 if (drag_info.copy) {
3916 boost::shared_ptr<Region> newregion;
3917 boost::shared_ptr<Region> ar;
3918 boost::shared_ptr<Region> mr;
3920 if ((ar = boost::dynamic_pointer_cast<AudioRegion>(rv->region())) != 0) {
3921 newregion = RegionFactory::create (ar);
3922 } else if ((mr = boost::dynamic_pointer_cast<MidiRegion>(rv->region())) != 0) {
3923 newregion = RegionFactory::create (mr);
3928 latest_regionviews.clear ();
3929 sigc::connection c = rtv->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3930 to_playlist->add_region (newregion, (nframes_t) (where * rtv->get_diskstream()->speed()));
3933 if (!latest_regionviews.empty()) {
3934 // XXX why just the first one ? we only expect one
3935 rtv->reveal_dependent_views (*latest_regionviews.front());
3936 selection->add (latest_regionviews);
3941 /* just change the model */
3943 rv->region()->set_position (where, (void*) this);
3944 preserve_selection = true;
3951 if (! preserve_selection) {
3952 //selection->clear_regions();
3954 while (used_playlists.size() > 0) {
3956 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
3959 if (used_connections.size()) {
3960 sigc::connection c = used_connections.front();
3962 used_connections.pop_front();
3966 session->add_command (new MementoCommand<Playlist>(*(*i), 0, &(*i)->get_state()));
3967 used_playlists.pop_front();
3973 commit_reversible_command ();
3976 for (vector<RegionView*>::iterator x = copies.begin(); x != copies.end(); ++x) {
3983 Editor::create_region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
3985 if (drag_info.move_threshold_passed) {
3986 if (drag_info.first_move) {
3987 // TODO: create region-create-drag region view here
3988 drag_info.first_move = false;
3991 // TODO: resize region-create-drag region view here
3996 Editor::create_region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3998 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (drag_info.last_trackview);
4002 const boost::shared_ptr<MidiDiskstream> diskstream =
4003 boost::dynamic_pointer_cast<MidiDiskstream>(mtv->view()->trackview().track()->diskstream());
4006 warning << "Cannot create non-MIDI region" << endl;
4010 if (drag_info.first_move) {
4011 begin_reversible_command (_("create region"));
4012 XMLNode &before = mtv->playlist()->get_state();
4014 nframes_t start = drag_info.grab_frame;
4015 snap_to (start, -1);
4016 const Meter& m = session->tempo_map().meter_at(start);
4017 const Tempo& t = session->tempo_map().tempo_at(start);
4018 double length = floor (m.frames_per_bar(t, session->frame_rate()));
4020 boost::shared_ptr<Source> src = session->create_midi_source_for_session(*diskstream.get());
4022 mtv->playlist()->add_region (boost::dynamic_pointer_cast<MidiRegion>
4023 (RegionFactory::create(src, 0, (nframes_t) length,
4024 PBD::basename_nosuffix(src->name()))), start);
4025 XMLNode &after = mtv->playlist()->get_state();
4026 session->add_command(new MementoCommand<Playlist>(*mtv->playlist().get(), &before, &after));
4027 commit_reversible_command();
4030 create_region_drag_motion_callback (item, event);
4031 // TODO: create region-create-drag region here
4036 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
4038 /* Either add to or set the set the region selection, unless
4039 this is an alignment click (control used)
4042 if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
4043 TimeAxisView* tv = &rv.get_time_axis_view();
4044 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
4046 if (rtv && rtv->is_track()) {
4047 speed = rtv->get_diskstream()->speed();
4050 nframes64_t where = get_preferred_edit_position();
4054 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
4056 align_region (rv.region(), SyncPoint, (nframes_t) (where * speed));
4058 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
4060 align_region (rv.region(), End, (nframes_t) (where * speed));
4064 align_region (rv.region(), Start, (nframes_t) (where * speed));
4071 Editor::show_verbose_time_cursor (nframes_t frame, double offset, double xpos, double ypos)
4077 nframes_t frame_rate;
4086 if (Profile->get_sae() || Profile->get_small_screen()) {
4087 m = ARDOUR_UI::instance()->primary_clock.mode();
4089 m = ARDOUR_UI::instance()->secondary_clock.mode();
4093 case AudioClock::BBT:
4094 session->bbt_time (frame, bbt);
4095 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, bbt.bars, bbt.beats, bbt.ticks);
4098 case AudioClock::SMPTE:
4099 session->smpte_time (frame, smpte);
4100 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
4103 case AudioClock::MinSec:
4104 /* XXX this is copied from show_verbose_duration_cursor() */
4105 frame_rate = session->frame_rate();
4106 hours = frame / (frame_rate * 3600);
4107 frame = frame % (frame_rate * 3600);
4108 mins = frame / (frame_rate * 60);
4109 frame = frame % (frame_rate * 60);
4110 secs = (float) frame / (float) frame_rate;
4111 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
4115 snprintf (buf, sizeof(buf), "%u", frame);
4119 if (xpos >= 0 && ypos >=0) {
4120 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
4123 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
4125 show_verbose_canvas_cursor ();
4129 Editor::show_verbose_duration_cursor (nframes_t start, nframes_t end, double offset, double xpos, double ypos)
4136 nframes_t distance, frame_rate;
4138 Meter meter_at_start(session->tempo_map().meter_at(start));
4146 if (Profile->get_sae() || Profile->get_small_screen()) {
4147 m = ARDOUR_UI::instance()->primary_clock.mode ();
4149 m = ARDOUR_UI::instance()->secondary_clock.mode ();
4153 case AudioClock::BBT:
4154 session->bbt_time (start, sbbt);
4155 session->bbt_time (end, ebbt);
4158 /* XXX this computation won't work well if the
4159 user makes a selection that spans any meter changes.
4162 ebbt.bars -= sbbt.bars;
4163 if (ebbt.beats >= sbbt.beats) {
4164 ebbt.beats -= sbbt.beats;
4167 ebbt.beats = int(meter_at_start.beats_per_bar()) + ebbt.beats - sbbt.beats;
4169 if (ebbt.ticks >= sbbt.ticks) {
4170 ebbt.ticks -= sbbt.ticks;
4173 ebbt.ticks = int(Meter::ticks_per_beat) + ebbt.ticks - sbbt.ticks;
4176 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, ebbt.bars, ebbt.beats, ebbt.ticks);
4179 case AudioClock::SMPTE:
4180 session->smpte_duration (end - start, smpte);
4181 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
4184 case AudioClock::MinSec:
4185 /* XXX this stuff should be elsewhere.. */
4186 distance = end - start;
4187 frame_rate = session->frame_rate();
4188 hours = distance / (frame_rate * 3600);
4189 distance = distance % (frame_rate * 3600);
4190 mins = distance / (frame_rate * 60);
4191 distance = distance % (frame_rate * 60);
4192 secs = (float) distance / (float) frame_rate;
4193 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
4197 snprintf (buf, sizeof(buf), "%u", end - start);
4201 if (xpos >= 0 && ypos >=0) {
4202 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
4205 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
4208 show_verbose_canvas_cursor ();
4212 Editor::collect_new_region_view (RegionView* rv)
4214 latest_regionviews.push_back (rv);
4218 Editor::collect_and_select_new_region_view (RegionView* rv)
4221 latest_regionviews.push_back (rv);
4225 Editor::start_selection_grab (ArdourCanvas::Item* item, GdkEvent* event)
4227 if (clicked_regionview == 0) {
4231 /* lets try to create new Region for the selection */
4233 vector<boost::shared_ptr<Region> > new_regions;
4234 create_region_from_selection (new_regions);
4236 if (new_regions.empty()) {
4240 /* XXX fix me one day to use all new regions */
4242 boost::shared_ptr<Region> region (new_regions.front());
4244 /* add it to the current stream/playlist.
4246 tricky: the streamview for the track will add a new regionview. we will
4247 catch the signal it sends when it creates the regionview to
4248 set the regionview we want to then drag.
4251 latest_regionviews.clear();
4252 sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
4254 /* A selection grab currently creates two undo/redo operations, one for
4255 creating the new region and another for moving it.
4258 begin_reversible_command (_("selection grab"));
4260 boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
4262 XMLNode *before = &(playlist->get_state());
4263 clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
4264 XMLNode *after = &(playlist->get_state());
4265 session->add_command(new MementoCommand<Playlist>(*playlist, before, after));
4267 commit_reversible_command ();
4271 if (latest_regionviews.empty()) {
4272 /* something went wrong */
4276 /* we need to deselect all other regionviews, and select this one
4277 i'm ignoring undo stuff, because the region creation will take care of it
4279 selection->set (latest_regionviews);
4281 drag_info.item = latest_regionviews.front()->get_canvas_group();
4282 drag_info.data = latest_regionviews.front();
4283 drag_info.motion_callback = &Editor::region_drag_motion_callback;
4284 drag_info.finished_callback = &Editor::region_drag_finished_callback;
4288 drag_info.last_trackview = clicked_axisview;
4289 drag_info.last_frame_position = latest_regionviews.front()->region()->position();
4290 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
4292 show_verbose_time_cursor (drag_info.last_frame_position, 10);
4296 Editor::cancel_selection ()
4298 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4299 (*i)->hide_selection ();
4301 selection->clear ();
4302 clicked_selection = 0;
4306 Editor::start_selection_op (ArdourCanvas::Item* item, GdkEvent* event, SelectionOp op)
4308 nframes_t start = 0;
4315 drag_info.item = item;
4316 drag_info.motion_callback = &Editor::drag_selection;
4317 drag_info.finished_callback = &Editor::end_selection_op;
4322 case CreateSelection:
4323 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
4324 drag_info.copy = true;
4326 drag_info.copy = false;
4328 start_grab (event, selector_cursor);
4331 case SelectionStartTrim:
4332 if (clicked_axisview) {
4333 clicked_axisview->order_selection_trims (item, true);
4335 start_grab (event, trimmer_cursor);
4336 start = selection->time[clicked_selection].start;
4337 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
4340 case SelectionEndTrim:
4341 if (clicked_axisview) {
4342 clicked_axisview->order_selection_trims (item, false);
4344 start_grab (event, trimmer_cursor);
4345 end = selection->time[clicked_selection].end;
4346 drag_info.pointer_frame_offset = drag_info.grab_frame - end;
4350 start = selection->time[clicked_selection].start;
4352 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
4356 if (selection_op == SelectionMove) {
4357 show_verbose_time_cursor(start, 10);
4359 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4364 Editor::drag_selection (ArdourCanvas::Item* item, GdkEvent* event)
4366 nframes_t start = 0;
4369 nframes_t pending_position;
4371 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
4372 pending_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
4374 pending_position = 0;
4377 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4378 snap_to (pending_position);
4381 /* only alter selection if the current frame is
4382 different from the last frame position (adjusted)
4385 if (pending_position == drag_info.last_pointer_frame) return;
4387 switch (selection_op) {
4388 case CreateSelection:
4390 if (drag_info.first_move) {
4391 snap_to (drag_info.grab_frame);
4394 if (pending_position < drag_info.grab_frame) {
4395 start = pending_position;
4396 end = drag_info.grab_frame;
4398 end = pending_position;
4399 start = drag_info.grab_frame;
4402 /* first drag: Either add to the selection
4403 or create a new selection->
4406 if (drag_info.first_move) {
4408 begin_reversible_command (_("range selection"));
4410 if (drag_info.copy) {
4411 /* adding to the selection */
4412 clicked_selection = selection->add (start, end);
4413 drag_info.copy = false;
4415 /* new selection-> */
4416 clicked_selection = selection->set (clicked_axisview, start, end);
4421 case SelectionStartTrim:
4423 if (drag_info.first_move) {
4424 begin_reversible_command (_("trim selection start"));
4427 start = selection->time[clicked_selection].start;
4428 end = selection->time[clicked_selection].end;
4430 if (pending_position > end) {
4433 start = pending_position;
4437 case SelectionEndTrim:
4439 if (drag_info.first_move) {
4440 begin_reversible_command (_("trim selection end"));
4443 start = selection->time[clicked_selection].start;
4444 end = selection->time[clicked_selection].end;
4446 if (pending_position < start) {
4449 end = pending_position;
4456 if (drag_info.first_move) {
4457 begin_reversible_command (_("move selection"));
4460 start = selection->time[clicked_selection].start;
4461 end = selection->time[clicked_selection].end;
4463 length = end - start;
4465 start = pending_position;
4468 end = start + length;
4473 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
4474 start_canvas_autoscroll (1);
4478 selection->replace (clicked_selection, start, end);
4481 drag_info.last_pointer_frame = pending_position;
4482 drag_info.first_move = false;
4484 if (selection_op == SelectionMove) {
4485 show_verbose_time_cursor(start, 10);
4487 show_verbose_time_cursor(pending_position, 10);
4492 Editor::end_selection_op (ArdourCanvas::Item* item, GdkEvent* event)
4494 if (!drag_info.first_move) {
4495 drag_selection (item, event);
4496 /* XXX this is not object-oriented programming at all. ick */
4497 if (selection->time.consolidate()) {
4498 selection->TimeChanged ();
4500 commit_reversible_command ();
4502 /* just a click, no pointer movement.*/
4504 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
4506 selection->clear_time();
4511 /* XXX what happens if its a music selection? */
4512 session->set_audio_range (selection->time);
4513 stop_canvas_autoscroll ();
4517 Editor::start_trim (ArdourCanvas::Item* item, GdkEvent* event)
4520 TimeAxisView* tvp = clicked_axisview;
4521 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4523 if (tv && tv->is_track()) {
4524 speed = tv->get_diskstream()->speed();
4527 nframes_t region_start = (nframes_t) (clicked_regionview->region()->position() / speed);
4528 nframes_t region_end = (nframes_t) (clicked_regionview->region()->last_frame() / speed);
4529 nframes_t region_length = (nframes_t) (clicked_regionview->region()->length() / speed);
4531 //drag_info.item = clicked_regionview->get_name_highlight();
4532 drag_info.item = item;
4533 drag_info.motion_callback = &Editor::trim_motion_callback;
4534 drag_info.finished_callback = &Editor::trim_finished_callback;
4536 start_grab (event, trimmer_cursor);
4538 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
4539 trim_op = ContentsTrim;
4541 /* These will get overridden for a point trim.*/
4542 if (drag_info.current_pointer_frame < (region_start + region_length/2)) {
4543 /* closer to start */
4544 trim_op = StartTrim;
4545 } else if (drag_info.current_pointer_frame > (region_end - region_length/2)) {
4553 show_verbose_time_cursor(region_start, 10);
4556 show_verbose_time_cursor(region_end, 10);
4559 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4565 Editor::trim_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
4567 RegionView* rv = clicked_regionview;
4568 nframes_t frame_delta = 0;
4569 bool left_direction;
4570 bool obey_snap = !Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier());
4572 /* snap modifier works differently here..
4573 its' current state has to be passed to the
4574 various trim functions in order to work properly
4578 TimeAxisView* tvp = clicked_axisview;
4579 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4580 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
4582 if (tv && tv->is_track()) {
4583 speed = tv->get_diskstream()->speed();
4586 if (drag_info.last_pointer_frame > drag_info.current_pointer_frame) {
4587 left_direction = true;
4589 left_direction = false;
4593 snap_to (drag_info.current_pointer_frame);
4596 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
4600 if (drag_info.first_move) {
4606 trim_type = "Region start trim";
4609 trim_type = "Region end trim";
4612 trim_type = "Region content trim";
4616 begin_reversible_command (trim_type);
4618 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4619 (*i)->fake_set_opaque(false);
4620 (*i)->region()->freeze ();
4622 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
4624 arv->temporarily_hide_envelope ();
4626 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4627 insert_result = motion_frozen_playlists.insert (pl);
4628 if (insert_result.second) {
4629 session->add_command(new MementoCommand<Playlist>(*pl, &pl->get_state(), 0));
4635 if (left_direction) {
4636 frame_delta = (drag_info.last_pointer_frame - drag_info.current_pointer_frame);
4638 frame_delta = (drag_info.current_pointer_frame - drag_info.last_pointer_frame);
4643 if ((left_direction == false) && (drag_info.current_pointer_frame <= rv->region()->first_frame()/speed)) {
4646 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4647 single_start_trim (**i, frame_delta, left_direction, obey_snap);
4653 if ((left_direction == true) && (drag_info.current_pointer_frame > (nframes_t) (rv->region()->last_frame()/speed))) {
4656 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4657 single_end_trim (**i, frame_delta, left_direction, obey_snap);
4664 bool swap_direction = false;
4666 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
4667 swap_direction = true;
4670 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4671 i != selection->regions.by_layer().end(); ++i)
4673 single_contents_trim (**i, frame_delta, left_direction, swap_direction, obey_snap);
4681 show_verbose_time_cursor((nframes_t) (rv->region()->position()/speed), 10);
4684 show_verbose_time_cursor((nframes_t) (rv->region()->last_frame()/speed), 10);
4687 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4691 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4692 drag_info.first_move = false;
4696 Editor::single_contents_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool swap_direction, bool obey_snap)
4698 boost::shared_ptr<Region> region (rv.region());
4700 if (region->locked()) {
4704 nframes_t new_bound;
4707 TimeAxisView* tvp = clicked_axisview;
4708 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4710 if (tv && tv->is_track()) {
4711 speed = tv->get_diskstream()->speed();
4714 if (left_direction) {
4715 if (swap_direction) {
4716 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4718 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4721 if (swap_direction) {
4722 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4724 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4729 snap_to (new_bound);
4731 region->trim_start ((nframes_t) (new_bound * speed), this);
4732 rv.region_changed (StartChanged);
4736 Editor::single_start_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool obey_snap)
4738 boost::shared_ptr<Region> region (rv.region());
4740 if (region->locked()) {
4744 nframes_t new_bound;
4747 TimeAxisView* tvp = clicked_axisview;
4748 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4750 if (tv && tv->is_track()) {
4751 speed = tv->get_diskstream()->speed();
4754 if (left_direction) {
4755 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4757 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4761 snap_to (new_bound, (left_direction ? 0 : 1));
4764 region->trim_front ((nframes_t) (new_bound * speed), this);
4766 rv.region_changed (Change (LengthChanged|PositionChanged|StartChanged));
4770 Editor::single_end_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool obey_snap)
4772 boost::shared_ptr<Region> region (rv.region());
4774 if (region->locked()) {
4778 nframes_t new_bound;
4781 TimeAxisView* tvp = clicked_axisview;
4782 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4784 if (tv && tv->is_track()) {
4785 speed = tv->get_diskstream()->speed();
4788 if (left_direction) {
4789 new_bound = (nframes_t) ((region->last_frame() + 1)/speed) - frame_delta;
4791 new_bound = (nframes_t) ((region->last_frame() + 1)/speed) + frame_delta;
4795 snap_to (new_bound);
4797 region->trim_end ((nframes_t) (new_bound * speed), this);
4798 rv.region_changed (LengthChanged);
4802 Editor::trim_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
4804 if (!drag_info.first_move) {
4805 trim_motion_callback (item, event);
4807 if (!selection->selected (clicked_regionview)) {
4808 thaw_region_after_trim (*clicked_regionview);
4811 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4812 i != selection->regions.by_layer().end(); ++i)
4814 thaw_region_after_trim (**i);
4815 (*i)->fake_set_opaque (true);
4819 for (set<boost::shared_ptr<Playlist> >::iterator p = motion_frozen_playlists.begin(); p != motion_frozen_playlists.end(); ++p) {
4821 session->add_command (new MementoCommand<Playlist>(*(*p).get(), 0, &(*p)->get_state()));
4824 motion_frozen_playlists.clear ();
4826 commit_reversible_command();
4828 /* no mouse movement */
4834 Editor::point_trim (GdkEvent* event)
4836 RegionView* rv = clicked_regionview;
4837 nframes_t new_bound = drag_info.current_pointer_frame;
4839 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4840 snap_to (new_bound);
4843 /* Choose action dependant on which button was pressed */
4844 switch (event->button.button) {
4846 trim_op = StartTrim;
4847 begin_reversible_command (_("Start point trim"));
4849 if (selection->selected (rv)) {
4851 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4852 i != selection->regions.by_layer().end(); ++i)
4854 if (!(*i)->region()->locked()) {
4855 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4856 XMLNode &before = pl->get_state();
4857 (*i)->region()->trim_front (new_bound, this);
4858 XMLNode &after = pl->get_state();
4859 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4865 if (!rv->region()->locked()) {
4866 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
4867 XMLNode &before = pl->get_state();
4868 rv->region()->trim_front (new_bound, this);
4869 XMLNode &after = pl->get_state();
4870 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4874 commit_reversible_command();
4879 begin_reversible_command (_("End point trim"));
4881 if (selection->selected (rv)) {
4883 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
4885 if (!(*i)->region()->locked()) {
4886 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4887 XMLNode &before = pl->get_state();
4888 (*i)->region()->trim_end (new_bound, this);
4889 XMLNode &after = pl->get_state();
4890 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4896 if (!rv->region()->locked()) {
4897 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
4898 XMLNode &before = pl->get_state();
4899 rv->region()->trim_end (new_bound, this);
4900 XMLNode &after = pl->get_state();
4901 session->add_command (new MementoCommand<Playlist>(*pl.get(), &before, &after));
4905 commit_reversible_command();
4914 Editor::thaw_region_after_trim (RegionView& rv)
4916 boost::shared_ptr<Region> region (rv.region());
4918 if (region->locked()) {
4922 region->thaw (_("trimmed region"));
4923 XMLNode &after = region->playlist()->get_state();
4924 session->add_command (new MementoCommand<Playlist>(*(region->playlist()), 0, &after));
4926 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(&rv);
4928 arv->unhide_envelope ();
4932 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* event)
4937 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
4938 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
4942 Location* location = find_location_from_marker (marker, is_start);
4943 location->set_hidden (true, this);
4948 Editor::start_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event, RangeMarkerOp op)
4954 drag_info.item = item;
4955 drag_info.motion_callback = &Editor::drag_range_markerbar_op;
4956 drag_info.finished_callback = &Editor::end_range_markerbar_op;
4958 range_marker_op = op;
4960 if (!temp_location) {
4961 temp_location = new Location;
4965 case CreateRangeMarker:
4966 case CreateTransportMarker:
4967 case CreateCDMarker:
4969 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
4970 drag_info.copy = true;
4972 drag_info.copy = false;
4974 start_grab (event, selector_cursor);
4978 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4983 Editor::drag_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4985 nframes_t start = 0;
4987 ArdourCanvas::SimpleRect *crect;
4989 switch (range_marker_op) {
4990 case CreateRangeMarker:
4991 crect = range_bar_drag_rect;
4993 case CreateTransportMarker:
4994 crect = transport_bar_drag_rect;
4996 case CreateCDMarker:
4997 crect = cd_marker_bar_drag_rect;
5000 cerr << "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()" << endl;
5005 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
5006 snap_to (drag_info.current_pointer_frame);
5009 /* only alter selection if the current frame is
5010 different from the last frame position.
5013 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
5015 switch (range_marker_op) {
5016 case CreateRangeMarker:
5017 case CreateTransportMarker:
5018 case CreateCDMarker:
5019 if (drag_info.first_move) {
5020 snap_to (drag_info.grab_frame);
5023 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
5024 start = drag_info.current_pointer_frame;
5025 end = drag_info.grab_frame;
5027 end = drag_info.current_pointer_frame;
5028 start = drag_info.grab_frame;
5031 /* first drag: Either add to the selection
5032 or create a new selection.
5035 if (drag_info.first_move) {
5037 temp_location->set (start, end);
5041 update_marker_drag_item (temp_location);
5042 range_marker_drag_rect->show();
5043 range_marker_drag_rect->raise_to_top();
5049 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
5050 start_canvas_autoscroll (1);
5054 temp_location->set (start, end);
5056 double x1 = frame_to_pixel (start);
5057 double x2 = frame_to_pixel (end);
5058 crect->property_x1() = x1;
5059 crect->property_x2() = x2;
5061 update_marker_drag_item (temp_location);
5064 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
5065 drag_info.first_move = false;
5067 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
5072 Editor::end_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
5074 Location * newloc = 0;
5078 if (!drag_info.first_move) {
5079 drag_range_markerbar_op (item, event);
5081 switch (range_marker_op) {
5082 case CreateRangeMarker:
5083 case CreateCDMarker:
5085 begin_reversible_command (_("new range marker"));
5086 XMLNode &before = session->locations()->get_state();
5087 session->locations()->next_available_name(rangename,"unnamed");
5088 if (range_marker_op == CreateCDMarker) {
5089 flags = Location::IsRangeMarker|Location::IsCDMarker;
5090 cd_marker_bar_drag_rect->hide();
5093 flags = Location::IsRangeMarker;
5094 range_bar_drag_rect->hide();
5096 newloc = new Location(temp_location->start(), temp_location->end(), rangename, (Location::Flags) flags);
5097 session->locations()->add (newloc, true);
5098 XMLNode &after = session->locations()->get_state();
5099 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
5100 commit_reversible_command ();
5102 range_marker_drag_rect->hide();
5106 case CreateTransportMarker:
5107 // popup menu to pick loop or punch
5108 new_transport_marker_context_menu (&event->button, item);
5113 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
5115 if (Keyboard::no_modifier_keys_pressed (&event->button) && range_marker_op != CreateCDMarker) {
5120 start = session->locations()->first_mark_before (drag_info.grab_frame);
5121 end = session->locations()->first_mark_after (drag_info.grab_frame);
5123 if (end == max_frames) {
5124 end = session->current_end_frame ();
5128 start = session->current_start_frame ();
5131 switch (mouse_mode) {
5133 /* find the two markers on either side and then make the selection from it */
5134 select_all_within (start, end, 0.0f, FLT_MAX, track_views, Selection::Set);
5138 /* find the two markers on either side of the click and make the range out of it */
5139 selection->set (0, start, end);
5148 stop_canvas_autoscroll ();
5154 Editor::start_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
5156 drag_info.item = item;
5157 drag_info.motion_callback = &Editor::drag_mouse_zoom;
5158 drag_info.finished_callback = &Editor::end_mouse_zoom;
5160 start_grab (event, zoom_cursor);
5162 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5166 Editor::drag_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
5171 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
5172 snap_to (drag_info.current_pointer_frame);
5174 if (drag_info.first_move) {
5175 snap_to (drag_info.grab_frame);
5179 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
5181 /* base start and end on initial click position */
5182 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
5183 start = drag_info.current_pointer_frame;
5184 end = drag_info.grab_frame;
5186 end = drag_info.current_pointer_frame;
5187 start = drag_info.grab_frame;
5192 if (drag_info.first_move) {
5194 zoom_rect->raise_to_top();
5197 reposition_zoom_rect(start, end);
5199 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
5200 drag_info.first_move = false;
5202 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5207 Editor::end_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
5209 if (!drag_info.first_move) {
5210 drag_mouse_zoom (item, event);
5212 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
5213 temporal_zoom_by_frame (drag_info.grab_frame, drag_info.last_pointer_frame, "mouse zoom");
5215 temporal_zoom_by_frame (drag_info.last_pointer_frame, drag_info.grab_frame, "mouse zoom");
5218 temporal_zoom_to_frame (false, drag_info.grab_frame);
5220 temporal_zoom_step (false);
5221 center_screen (drag_info.grab_frame);
5229 Editor::reposition_zoom_rect (nframes_t start, nframes_t end)
5231 double x1 = frame_to_pixel (start);
5232 double x2 = frame_to_pixel (end);
5233 double y2 = full_canvas_height - 1.0;
5235 zoom_rect->property_x1() = x1;
5236 zoom_rect->property_y1() = 1.0;
5237 zoom_rect->property_x2() = x2;
5238 zoom_rect->property_y2() = y2;
5242 Editor::start_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
5244 drag_info.item = item;
5245 drag_info.motion_callback = &Editor::drag_rubberband_select;
5246 drag_info.finished_callback = &Editor::end_rubberband_select;
5248 start_grab (event, cross_hair_cursor);
5250 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5254 Editor::drag_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
5261 /* use a bigger drag threshold than the default */
5263 if (abs ((int) (drag_info.current_pointer_frame - drag_info.grab_frame)) < 8) {
5267 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier()) && Config->get_rubberbanding_snaps_to_grid()) {
5268 if (drag_info.first_move) {
5269 snap_to (drag_info.grab_frame);
5271 snap_to (drag_info.current_pointer_frame);
5274 /* base start and end on initial click position */
5276 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
5277 start = drag_info.current_pointer_frame;
5278 end = drag_info.grab_frame;
5280 end = drag_info.current_pointer_frame;
5281 start = drag_info.grab_frame;
5284 if (drag_info.current_pointer_y < drag_info.grab_y) {
5285 y1 = drag_info.current_pointer_y;
5286 y2 = drag_info.grab_y;
5288 y2 = drag_info.current_pointer_y;
5289 y1 = drag_info.grab_y;
5293 if (start != end || y1 != y2) {
5295 double x1 = frame_to_pixel (start);
5296 double x2 = frame_to_pixel (end);
5298 rubberband_rect->property_x1() = x1;
5299 rubberband_rect->property_y1() = y1;
5300 rubberband_rect->property_x2() = x2;
5301 rubberband_rect->property_y2() = y2;
5303 rubberband_rect->show();
5304 rubberband_rect->raise_to_top();
5306 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
5307 drag_info.first_move = false;
5309 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5314 Editor::end_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
5316 if (!drag_info.first_move) {
5318 drag_rubberband_select (item, event);
5321 if (drag_info.current_pointer_y < drag_info.grab_y) {
5322 y1 = drag_info.current_pointer_y;
5323 y2 = drag_info.grab_y;
5326 y2 = drag_info.current_pointer_y;
5327 y1 = drag_info.grab_y;
5331 Selection::Operation op = Keyboard::selection_type (event->button.state);
5334 begin_reversible_command (_("rubberband selection"));
5336 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
5337 commit = select_all_within (drag_info.grab_frame, drag_info.last_pointer_frame, y1, y2, track_views, op);
5339 commit = select_all_within (drag_info.last_pointer_frame, drag_info.grab_frame, y1, y2, track_views, op);
5343 commit_reversible_command ();
5347 selection->clear_tracks();
5348 selection->clear_regions();
5349 selection->clear_points ();
5350 selection->clear_lines ();
5353 rubberband_rect->hide();
5358 Editor::mouse_rename_region (ArdourCanvas::Item* item, GdkEvent* event)
5360 using namespace Gtkmm2ext;
5362 ArdourPrompter prompter (false);
5364 prompter.set_prompt (_("Name for region:"));
5365 prompter.set_initial_text (clicked_regionview->region()->name());
5366 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
5367 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
5368 prompter.show_all ();
5369 switch (prompter.run ()) {
5370 case Gtk::RESPONSE_ACCEPT:
5372 prompter.get_result(str);
5374 clicked_regionview->region()->set_name (str);
5382 Editor::start_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
5384 drag_info.item = item;
5385 drag_info.motion_callback = &Editor::time_fx_motion;
5386 drag_info.finished_callback = &Editor::end_time_fx;
5390 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5394 Editor::time_fx_motion (ArdourCanvas::Item *item, GdkEvent* event)
5396 RegionView* rv = clicked_regionview;
5398 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
5399 snap_to (drag_info.current_pointer_frame);
5402 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
5406 if (drag_info.current_pointer_frame > rv->region()->position()) {
5407 rv->get_time_axis_view().show_timestretch (rv->region()->position(), drag_info.current_pointer_frame);
5410 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
5411 drag_info.first_move = false;
5413 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5417 Editor::end_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
5419 clicked_regionview->get_time_axis_view().hide_timestretch ();
5421 if (drag_info.first_move) {
5425 if (drag_info.last_pointer_frame < clicked_regionview->region()->position()) {
5426 /* backwards drag of the left edge - not usable */
5430 nframes_t newlen = drag_info.last_pointer_frame - clicked_regionview->region()->position();
5432 float percentage = (double) newlen / (double) clicked_regionview->region()->length();
5434 #ifndef USE_RUBBERBAND
5435 // Soundtouch uses percentage / 100 instead of normal (/ 1)
5436 if (clicked_regionview->region()->data_type() == DataType::AUDIO) {
5437 percentage = (float) ((double) newlen - (double) clicked_regionview->region()->length()) / ((double) newlen) * 100.0f;
5441 begin_reversible_command (_("timestretch"));
5443 // XXX how do timeFX on multiple regions ?
5446 rs.add (clicked_regionview);
5448 if (time_stretch (rs, percentage) == 0) {
5449 session->commit_reversible_command ();
5454 Editor::mouse_brush_insert_region (RegionView* rv, nframes_t pos)
5456 /* no brushing without a useful snap setting */
5459 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
5462 switch (snap_mode) {
5464 return; /* can't work because it allows region to be placed anywhere */
5469 switch (snap_type) {
5477 /* don't brush a copy over the original */
5479 if (pos == rv->region()->position()) {
5483 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&arv->get_time_axis_view());
5485 if (rtv == 0 || !rtv->is_track()) {
5489 boost::shared_ptr<Playlist> playlist = rtv->playlist();
5490 double speed = rtv->get_diskstream()->speed();
5492 XMLNode &before = playlist->get_state();
5493 playlist->add_region (boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (arv->audio_region())), (nframes_t) (pos * speed));
5494 XMLNode &after = playlist->get_state();
5495 session->add_command(new MementoCommand<Playlist>(*playlist.get(), &before, &after));
5497 // playlist is frozen, so we have to update manually
5499 playlist->Modified(); /* EMIT SIGNAL */
5503 Editor::track_height_step_timeout ()
5506 struct timeval delta;
5508 gettimeofday (&now, 0);
5509 timersub (&now, &last_track_height_step_timestamp, &delta);
5511 if (delta.tv_sec * 1000000 + delta.tv_usec > 250000) { /* milliseconds */
5512 current_stepping_trackview = 0;