3 Copyright (C) 2000-2001 Paul Davis
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
29 #include <pbd/error.h>
30 #include <gtkmm2ext/utils.h>
31 #include <pbd/memento_command.h>
32 #include <pbd/basename.h>
34 #include "ardour_ui.h"
36 #include "time_axis_view.h"
37 #include "audio_time_axis.h"
38 #include "audio_region_view.h"
39 #include "midi_region_view.h"
41 #include "streamview.h"
42 #include "region_gain_line.h"
43 #include "automation_time_axis.h"
44 #include "control_point.h"
47 #include "selection.h"
50 #include "rgb_macros.h"
52 #include <ardour/types.h>
53 #include <ardour/profile.h>
54 #include <ardour/route.h>
55 #include <ardour/audio_track.h>
56 #include <ardour/audio_diskstream.h>
57 #include <ardour/midi_diskstream.h>
58 #include <ardour/playlist.h>
59 #include <ardour/audioplaylist.h>
60 #include <ardour/audioregion.h>
61 #include <ardour/midi_region.h>
62 #include <ardour/dB.h>
63 #include <ardour/utils.h>
64 #include <ardour/region_factory.h>
65 #include <ardour/source_factory.h>
72 using namespace ARDOUR;
76 using namespace Editing;
78 const static double ZERO_GAIN_FRACTION = gain_to_slider_position(dB_to_coefficient(0.0));
81 Editor::mouse_frame (nframes64_t& where, bool& in_track_canvas) const
85 Gdk::ModifierType mask;
86 Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->track_canvas->get_window();
87 Glib::RefPtr<const Gdk::Window> pointer_window;
93 pointer_window = canvas_window->get_pointer (x, y, mask);
95 if (pointer_window == track_canvas->get_bin_window()) {
98 in_track_canvas = true;
101 in_track_canvas = false;
106 event.type = GDK_BUTTON_RELEASE;
110 where = event_frame (&event, 0, 0);
115 Editor::event_frame (GdkEvent* event, double* pcx, double* pcy) const
129 switch (event->type) {
130 case GDK_BUTTON_RELEASE:
131 case GDK_BUTTON_PRESS:
132 case GDK_2BUTTON_PRESS:
133 case GDK_3BUTTON_PRESS:
135 *pcx = event->button.x;
136 *pcy = event->button.y;
137 _trackview_group->w2i(*pcx, *pcy);
139 case GDK_MOTION_NOTIFY:
141 *pcx = event->motion.x;
142 *pcy = event->motion.y;
143 _trackview_group->w2i(*pcx, *pcy);
145 case GDK_ENTER_NOTIFY:
146 case GDK_LEAVE_NOTIFY:
147 track_canvas->w2c(event->crossing.x, event->crossing.y, *pcx, *pcy);
150 case GDK_KEY_RELEASE:
151 // track_canvas->w2c(event->key.x, event->key.y, *pcx, *pcy);
154 warning << string_compose (_("Editor::event_frame() used on unhandled event type %1"), event->type) << endmsg;
158 /* note that pixel_to_frame() never returns less than zero, so even if the pixel
159 position is negative (as can be the case with motion events in particular),
160 the frame location is always positive.
163 return pixel_to_frame (*pcx);
167 Editor::mouse_mode_toggled (MouseMode m)
169 if (ignore_mouse_mode_toggle) {
175 if (mouse_select_button.get_active()) {
181 if (mouse_move_button.get_active()) {
187 if (mouse_gain_button.get_active()) {
193 if (mouse_zoom_button.get_active()) {
199 if (mouse_timefx_button.get_active()) {
205 if (mouse_audition_button.get_active()) {
211 if (mouse_note_button.get_active()) {
222 Editor::which_grabber_cursor ()
224 switch (_edit_point) {
226 return grabber_edit_point_cursor;
231 return grabber_cursor;
235 Editor::set_canvas_cursor ()
237 switch (mouse_mode) {
239 current_canvas_cursor = selector_cursor;
243 current_canvas_cursor = which_grabber_cursor();
247 current_canvas_cursor = cross_hair_cursor;
251 current_canvas_cursor = zoom_cursor;
255 current_canvas_cursor = time_fx_cursor; // just use playhead
259 current_canvas_cursor = speaker_cursor;
263 set_midi_edit_cursor (current_midi_edit_mode());
268 track_canvas->get_window()->set_cursor(*current_canvas_cursor);
273 Editor::set_mouse_mode (MouseMode m, bool force)
275 if (drag_info.item) {
279 if (!force && m == mouse_mode) {
287 if (mouse_mode != MouseRange) {
289 /* in all modes except range, hide the range selection,
290 show the object (region) selection.
293 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
294 (*i)->set_should_show_selection (true);
296 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
297 (*i)->hide_selection ();
303 in range mode,show the range selection.
306 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
307 if ((*i)->get_selected()) {
308 (*i)->show_selection (selection->time);
313 /* XXX the hack of unsetting all other buttons should go
314 away once GTK2 allows us to use regular radio buttons drawn like
315 normal buttons, rather than my silly GroupedButton hack.
318 ignore_mouse_mode_toggle = true;
320 switch (mouse_mode) {
322 mouse_select_button.set_active (true);
326 mouse_move_button.set_active (true);
330 mouse_gain_button.set_active (true);
334 mouse_zoom_button.set_active (true);
338 mouse_timefx_button.set_active (true);
342 mouse_audition_button.set_active (true);
346 mouse_note_button.set_active (true);
347 set_midi_edit_cursor (current_midi_edit_mode());
351 if (mouse_mode == MouseNote)
352 midi_toolbar_frame.show();
354 midi_toolbar_frame.hide();
356 ignore_mouse_mode_toggle = false;
358 set_canvas_cursor ();
362 Editor::step_mouse_mode (bool next)
364 switch (current_mouse_mode()) {
366 if (next) set_mouse_mode (MouseRange);
367 else set_mouse_mode (MouseTimeFX);
371 if (next) set_mouse_mode (MouseZoom);
372 else set_mouse_mode (MouseObject);
376 if (next) set_mouse_mode (MouseGain);
377 else set_mouse_mode (MouseRange);
381 if (next) set_mouse_mode (MouseTimeFX);
382 else set_mouse_mode (MouseZoom);
386 if (next) set_mouse_mode (MouseAudition);
387 else set_mouse_mode (MouseGain);
391 if (next) set_mouse_mode (MouseObject);
392 else set_mouse_mode (MouseTimeFX);
396 if (next) set_mouse_mode (MouseObject);
397 else set_mouse_mode (MouseAudition);
403 Editor::midi_edit_mode_toggled (MidiEditMode m)
405 if (ignore_midi_edit_mode_toggle) {
411 if (midi_tool_pencil_button.get_active())
412 set_midi_edit_mode (m);
416 if (midi_tool_select_button.get_active())
417 set_midi_edit_mode (m);
421 if (midi_tool_resize_button.get_active())
422 set_midi_edit_mode (m);
426 if (midi_tool_erase_button.get_active())
427 set_midi_edit_mode (m);
434 set_midi_edit_cursor(m);
439 Editor::set_midi_edit_mode (MidiEditMode m, bool force)
441 if (drag_info.item) {
445 if (!force && m == midi_edit_mode) {
453 ignore_midi_edit_mode_toggle = true;
455 switch (midi_edit_mode) {
457 midi_tool_pencil_button.set_active (true);
461 midi_tool_select_button.set_active (true);
465 midi_tool_resize_button.set_active (true);
469 midi_tool_erase_button.set_active (true);
473 ignore_midi_edit_mode_toggle = false;
475 set_midi_edit_cursor (current_midi_edit_mode());
478 track_canvas->get_window()->set_cursor(*current_canvas_cursor);
483 Editor::set_midi_edit_cursor (MidiEditMode m)
485 switch (midi_edit_mode) {
487 current_canvas_cursor = midi_pencil_cursor;
491 current_canvas_cursor = midi_select_cursor;
495 current_canvas_cursor = midi_resize_cursor;
499 current_canvas_cursor = midi_erase_cursor;
505 Editor::button_selection (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
507 /* in object/audition/timefx mode, any button press sets
508 the selection if the object can be selected. this is a
509 bit of hack, because we want to avoid this if the
510 mouse operation is a region alignment.
512 note: not dbl-click or triple-click
515 if (((mouse_mode != MouseObject) &&
516 (mouse_mode != MouseAudition || item_type != RegionItem) &&
517 (mouse_mode != MouseTimeFX || item_type != RegionItem) &&
518 (mouse_mode != MouseRange)) ||
520 ((event->type != GDK_BUTTON_PRESS && event->type != GDK_BUTTON_RELEASE) || event->button.button > 3)) {
525 if (event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) {
527 if ((event->button.state & Keyboard::RelevantModifierKeyMask) && event->button.button != 1) {
529 /* almost no selection action on modified button-2 or button-3 events */
531 if (item_type != RegionItem && event->button.button != 2) {
537 Selection::Operation op = Keyboard::selection_type (event->button.state);
538 bool press = (event->type == GDK_BUTTON_PRESS);
540 // begin_reversible_command (_("select on click"));
544 if (mouse_mode != MouseRange) {
545 set_selected_regionview_from_click (press, op, true);
546 } else if (event->type == GDK_BUTTON_PRESS) {
547 set_selected_track_as_side_effect ();
551 case RegionViewNameHighlight:
553 if (mouse_mode != MouseRange) {
554 set_selected_regionview_from_click (press, op, true);
555 } else if (event->type == GDK_BUTTON_PRESS) {
556 set_selected_track_as_side_effect ();
561 case FadeInHandleItem:
563 case FadeOutHandleItem:
565 if (mouse_mode != MouseRange) {
566 set_selected_regionview_from_click (press, op, true);
567 } else if (event->type == GDK_BUTTON_PRESS) {
568 set_selected_track_as_side_effect ();
572 case ControlPointItem:
573 set_selected_track_as_side_effect ();
574 if (mouse_mode != MouseRange) {
575 set_selected_control_point_from_click (op, false);
580 /* for context click or range selection, select track */
581 if (event->button.button == 3) {
582 set_selected_track_as_side_effect ();
583 } else if (event->type == GDK_BUTTON_PRESS && mouse_mode == MouseRange) {
584 set_selected_track_as_side_effect ();
588 case AutomationTrackItem:
589 set_selected_track_as_side_effect (true);
598 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
600 Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->track_canvas->get_window();
603 Glib::RefPtr<const Gdk::Window> pointer_window;
606 Gdk::ModifierType mask;
608 pointer_window = canvas_window->get_pointer (x, y, mask);
610 if (pointer_window == track_canvas->get_bin_window()) {
611 track_canvas->window_to_world (x, y, wx, wy);
612 allow_vertical_scroll = true;
614 allow_vertical_scroll = false;
618 track_canvas->grab_focus();
620 if (session && session->actively_recording()) {
624 button_selection (item, event, item_type);
626 if (drag_info.item == 0 &&
627 (Keyboard::is_delete_event (&event->button) ||
628 Keyboard::is_context_menu_event (&event->button) ||
629 Keyboard::is_edit_event (&event->button))) {
631 /* handled by button release */
635 switch (event->button.button) {
638 if (event->type == GDK_BUTTON_PRESS) {
640 if (drag_info.item) {
641 drag_info.item->ungrab (event->button.time);
644 /* single mouse clicks on any of these item types operate
645 independent of mouse mode, mostly because they are
646 not on the main track canvas or because we want
651 case PlayheadCursorItem:
652 start_cursor_grab (item, event);
656 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
657 hide_marker (item, event);
659 start_marker_grab (item, event);
663 case TempoMarkerItem:
664 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
665 start_tempo_marker_copy_grab (item, event);
667 start_tempo_marker_grab (item, event);
671 case MeterMarkerItem:
672 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
673 start_meter_marker_copy_grab (item, event);
675 start_meter_marker_grab (item, event);
685 case RangeMarkerBarItem:
686 start_range_markerbar_op (item, event, CreateRangeMarker);
690 case CdMarkerBarItem:
691 start_range_markerbar_op (item, event, CreateCDMarker);
695 case TransportMarkerBarItem:
696 start_range_markerbar_op (item, event, CreateTransportMarker);
705 switch (mouse_mode) {
708 case StartSelectionTrimItem:
709 start_selection_op (item, event, SelectionStartTrim);
712 case EndSelectionTrimItem:
713 start_selection_op (item, event, SelectionEndTrim);
717 if (Keyboard::modifier_state_contains
718 (event->button.state, Keyboard::ModifierMask(Keyboard::SecondaryModifier))) {
719 // contains and not equals because I can't use alt as a modifier alone.
720 start_selection_grab (item, event);
721 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
722 /* grab selection for moving */
723 start_selection_op (item, event, SelectionMove);
725 /* this was debated, but decided the more common action was to
726 make a new selection */
727 start_selection_op (item, event, CreateSelection);
732 start_selection_op (item, event, CreateSelection);
738 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)) &&
739 event->type == GDK_BUTTON_PRESS) {
741 start_rubberband_select (item, event);
743 } else if (event->type == GDK_BUTTON_PRESS) {
746 case FadeInHandleItem:
747 start_fade_in_grab (item, event);
750 case FadeOutHandleItem:
751 start_fade_out_grab (item, event);
755 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
756 start_region_copy_grab (item, event);
757 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
758 start_region_brush_grab (item, event);
760 start_region_grab (item, event);
764 case RegionViewNameHighlight:
765 start_trim (item, event);
770 /* rename happens on edit clicks */
771 start_trim (clicked_regionview->get_name_highlight(), event);
775 case ControlPointItem:
776 start_control_point_grab (item, event);
780 case AutomationLineItem:
781 start_line_grab_from_line (item, event);
786 case AutomationTrackItem:
787 start_rubberband_select (item, event);
791 case ImageFrameHandleStartItem:
792 imageframe_start_handle_op(item, event) ;
795 case ImageFrameHandleEndItem:
796 imageframe_end_handle_op(item, event) ;
799 case MarkerViewHandleStartItem:
800 markerview_item_start_handle_op(item, event) ;
803 case MarkerViewHandleEndItem:
804 markerview_item_end_handle_op(item, event) ;
808 start_markerview_grab(item, event) ;
811 start_imageframe_grab(item, event) ;
829 // start_line_grab_from_regionview (item, event);
833 start_line_grab_from_line (item, event);
836 case ControlPointItem:
837 start_control_point_grab (item, event);
848 case ControlPointItem:
849 start_control_point_grab (item, event);
852 case AutomationLineItem:
853 start_line_grab_from_line (item, event);
857 // XXX need automation mode to identify which
859 // start_line_grab_from_regionview (item, event);
869 if (event->type == GDK_BUTTON_PRESS) {
870 start_mouse_zoom (item, event);
877 if (item_type == RegionItem) {
878 start_time_fx (item, event);
885 scrub_reverse_distance = 0;
886 last_scrub_x = event->button.x;
887 scrubbing_direction = 0;
888 track_canvas->get_window()->set_cursor (*transparent_cursor);
889 /* rest handled in motion & release */
893 start_create_region_grab (item, event);
902 switch (mouse_mode) {
904 if (event->type == GDK_BUTTON_PRESS) {
907 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
908 start_region_copy_grab (item, event);
910 start_region_grab (item, event);
914 case ControlPointItem:
915 start_control_point_grab (item, event);
926 case RegionViewNameHighlight:
927 start_trim (item, event);
932 start_trim (clicked_regionview->get_name_highlight(), event);
943 if (event->type == GDK_BUTTON_PRESS) {
944 /* relax till release */
951 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
952 temporal_zoom_session();
954 temporal_zoom_to_frame (true, event_frame(event));
977 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
979 nframes64_t where = event_frame (event, 0, 0);
980 AutomationTimeAxisView* atv = 0;
982 /* no action if we're recording */
984 if (session && session->actively_recording()) {
988 /* first, see if we're finishing a drag ... */
990 if (drag_info.item) {
991 if (end_grab (item, event)) {
992 /* grab dragged, so do nothing else */
997 button_selection (item, event, item_type);
999 /* edit events get handled here */
1001 if (drag_info.item == 0 && Keyboard::is_edit_event (&event->button)) {
1002 switch (item_type) {
1007 case TempoMarkerItem:
1008 edit_tempo_marker (item);
1011 case MeterMarkerItem:
1012 edit_meter_marker (item);
1015 case RegionViewName:
1016 if (clicked_regionview->name_active()) {
1017 return mouse_rename_region (item, event);
1027 /* context menu events get handled here */
1029 if (Keyboard::is_context_menu_event (&event->button)) {
1031 if (drag_info.item == 0) {
1033 /* no matter which button pops up the context menu, tell the menu
1034 widget to use button 1 to drive menu selection.
1037 switch (item_type) {
1039 case FadeInHandleItem:
1041 case FadeOutHandleItem:
1042 popup_fade_context_menu (1, event->button.time, item, item_type);
1046 popup_track_context_menu (1, event->button.time, item_type, false, where);
1050 case RegionViewNameHighlight:
1051 case RegionViewName:
1052 popup_track_context_menu (1, event->button.time, item_type, false, where);
1056 popup_track_context_menu (1, event->button.time, item_type, true, where);
1059 case AutomationTrackItem:
1060 popup_track_context_menu (1, event->button.time, item_type, false, where);
1064 case RangeMarkerBarItem:
1065 case TransportMarkerBarItem:
1066 case CdMarkerBarItem:
1069 popup_ruler_menu (pixel_to_frame(event->button.x), item_type);
1073 marker_context_menu (&event->button, item);
1076 case TempoMarkerItem:
1077 tm_marker_context_menu (&event->button, item);
1080 case MeterMarkerItem:
1081 tm_marker_context_menu (&event->button, item);
1084 case CrossfadeViewItem:
1085 popup_track_context_menu (1, event->button.time, item_type, false, where);
1089 case ImageFrameItem:
1090 popup_imageframe_edit_menu(1, event->button.time, item, true) ;
1092 case ImageFrameTimeAxisItem:
1093 popup_imageframe_edit_menu(1, event->button.time, item, false) ;
1095 case MarkerViewItem:
1096 popup_marker_time_axis_edit_menu(1, event->button.time, item, true) ;
1098 case MarkerTimeAxisItem:
1099 popup_marker_time_axis_edit_menu(1, event->button.time, item, false) ;
1111 /* delete events get handled here */
1113 if (drag_info.item == 0 && Keyboard::is_delete_event (&event->button)) {
1115 switch (item_type) {
1116 case TempoMarkerItem:
1117 remove_tempo_marker (item);
1120 case MeterMarkerItem:
1121 remove_meter_marker (item);
1125 remove_marker (*item, event);
1129 if (mouse_mode == MouseObject) {
1130 remove_clicked_region ();
1134 case ControlPointItem:
1135 if (mouse_mode == MouseGain) {
1136 remove_gain_control_point (item, event);
1138 remove_control_point (item, event);
1148 switch (event->button.button) {
1151 switch (item_type) {
1152 /* see comments in button_press_handler */
1153 case PlayheadCursorItem:
1156 case AutomationLineItem:
1157 case StartSelectionTrimItem:
1158 case EndSelectionTrimItem:
1162 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1163 snap_to (where, 0, true);
1165 mouse_add_new_marker (where);
1168 case CdMarkerBarItem:
1169 // if we get here then a dragged range wasn't done
1170 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1171 snap_to (where, 0, true);
1173 mouse_add_new_marker (where, true);
1177 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1180 mouse_add_new_tempo_event (where);
1184 mouse_add_new_meter_event (pixel_to_frame (event->button.x));
1192 switch (mouse_mode) {
1194 switch (item_type) {
1195 case AutomationTrackItem:
1196 atv = dynamic_cast<AutomationTimeAxisView*>(clicked_routeview);
1198 atv->add_automation_event (item, event, where, event->button.y);
1210 // Gain only makes sense for audio regions
1212 if (!dynamic_cast<AudioRegionView*>(clicked_regionview)) {
1216 switch (item_type) {
1218 dynamic_cast<AudioRegionView*>(clicked_regionview)->add_gain_point_event (item, event);
1222 case AutomationTrackItem:
1223 dynamic_cast<AutomationTimeAxisView*>(clicked_axisview)->
1224 add_automation_event (item, event, where, event->button.y);
1234 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1235 if (scrubbing_direction == 0) {
1236 /* no drag, just a click */
1237 switch (item_type) {
1239 play_selected_region ();
1245 /* make sure we stop */
1246 session->request_transport_speed (0.0);
1260 switch (mouse_mode) {
1263 switch (item_type) {
1265 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1267 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier|Keyboard::SecondaryModifier))) {
1270 // Button2 click is unused
1283 // x_style_paste (where, 1.0);
1303 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1309 if (last_item_entered != item) {
1310 last_item_entered = item;
1311 last_item_entered_n = 0;
1314 switch (item_type) {
1315 case ControlPointItem:
1316 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1317 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1318 cp->set_visible (true);
1322 at_y = cp->get_y ();
1323 cp->item()->i2w (at_x, at_y);
1327 fraction = 1.0 - (cp->get_y() / cp->line().height());
1329 if (is_drawable() && !_scrubbing) {
1330 track_canvas->get_window()->set_cursor (*fader_cursor);
1333 last_item_entered_n++;
1334 set_verbose_canvas_cursor (cp->line().get_verbose_cursor_string (fraction), at_x, at_y);
1335 if (last_item_entered_n < 10) {
1336 show_verbose_canvas_cursor ();
1342 if (mouse_mode == MouseGain) {
1343 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1345 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredGainLine.get();
1346 if (is_drawable()) {
1347 track_canvas->get_window()->set_cursor (*fader_cursor);
1352 case AutomationLineItem:
1353 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1355 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1357 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredAutomationLine.get();
1359 if (is_drawable()) {
1360 track_canvas->get_window()->set_cursor (*fader_cursor);
1365 case RegionViewNameHighlight:
1366 if (is_drawable() && mouse_mode == MouseObject) {
1367 track_canvas->get_window()->set_cursor (*trimmer_cursor);
1371 case StartSelectionTrimItem:
1372 case EndSelectionTrimItem:
1375 case ImageFrameHandleStartItem:
1376 case ImageFrameHandleEndItem:
1377 case MarkerViewHandleStartItem:
1378 case MarkerViewHandleEndItem:
1381 if (is_drawable()) {
1382 track_canvas->get_window()->set_cursor (*trimmer_cursor);
1386 case PlayheadCursorItem:
1387 if (is_drawable()) {
1388 switch (_edit_point) {
1390 track_canvas->get_window()->set_cursor (*grabber_edit_point_cursor);
1393 track_canvas->get_window()->set_cursor (*grabber_cursor);
1399 case RegionViewName:
1401 /* when the name is not an active item, the entire name highlight is for trimming */
1403 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1404 if (mouse_mode == MouseObject && is_drawable()) {
1405 track_canvas->get_window()->set_cursor (*trimmer_cursor);
1411 case AutomationTrackItem:
1412 if (is_drawable()) {
1413 Gdk::Cursor *cursor;
1414 switch (mouse_mode) {
1416 cursor = selector_cursor;
1419 cursor = zoom_cursor;
1422 cursor = cross_hair_cursor;
1426 track_canvas->get_window()->set_cursor (*cursor);
1428 AutomationTimeAxisView* atv;
1429 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1430 clear_entered_track = false;
1431 set_entered_track (atv);
1437 case RangeMarkerBarItem:
1438 case TransportMarkerBarItem:
1439 case CdMarkerBarItem:
1442 if (is_drawable()) {
1443 track_canvas->get_window()->set_cursor (*timebar_cursor);
1448 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1451 entered_marker = marker;
1452 marker->set_color_rgba (ARDOUR_UI::config()->canvasvar_EnteredMarker.get());
1454 case MeterMarkerItem:
1455 case TempoMarkerItem:
1456 if (is_drawable()) {
1457 track_canvas->get_window()->set_cursor (*timebar_cursor);
1460 case FadeInHandleItem:
1461 case FadeOutHandleItem:
1462 if (mouse_mode == MouseObject) {
1463 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1465 rect->property_fill_color_rgba() = 0;
1466 rect->property_outline_pixels() = 1;
1475 /* second pass to handle entered track status in a comprehensible way.
1478 switch (item_type) {
1480 case AutomationLineItem:
1481 case ControlPointItem:
1482 /* these do not affect the current entered track state */
1483 clear_entered_track = false;
1486 case AutomationTrackItem:
1487 /* handled above already */
1491 set_entered_track (0);
1499 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1508 switch (item_type) {
1509 case ControlPointItem:
1510 cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
1511 if (cp->line().the_list()->interpolation() != AutomationList::Discrete) {
1512 if (cp->line().npoints() > 1 && !cp->selected()) {
1513 cp->set_visible (false);
1517 if (is_drawable()) {
1518 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1521 hide_verbose_canvas_cursor ();
1524 case RegionViewNameHighlight:
1525 case StartSelectionTrimItem:
1526 case EndSelectionTrimItem:
1527 case PlayheadCursorItem:
1530 case ImageFrameHandleStartItem:
1531 case ImageFrameHandleEndItem:
1532 case MarkerViewHandleStartItem:
1533 case MarkerViewHandleEndItem:
1536 if (is_drawable()) {
1537 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1542 case AutomationLineItem:
1543 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1545 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1547 line->property_fill_color_rgba() = al->get_line_color();
1549 if (is_drawable()) {
1550 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1554 case RegionViewName:
1555 /* see enter_handler() for notes */
1556 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1557 if (is_drawable() && mouse_mode == MouseObject) {
1558 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1563 case RangeMarkerBarItem:
1564 case TransportMarkerBarItem:
1565 case CdMarkerBarItem:
1569 if (is_drawable()) {
1570 track_canvas->get_window()->set_cursor (*timebar_cursor);
1575 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1579 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
1580 location_flags_changed (loc, this);
1583 case MeterMarkerItem:
1584 case TempoMarkerItem:
1586 if (is_drawable()) {
1587 track_canvas->get_window()->set_cursor (*timebar_cursor);
1592 case FadeInHandleItem:
1593 case FadeOutHandleItem:
1594 rv = static_cast<RegionView*>(item->get_data ("regionview"));
1596 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1598 rect->property_fill_color_rgba() = rv->get_fill_color();
1599 rect->property_outline_pixels() = 0;
1604 case AutomationTrackItem:
1605 if (is_drawable()) {
1606 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1607 clear_entered_track = true;
1608 Glib::signal_idle().connect (mem_fun(*this, &Editor::left_automation_track));
1620 Editor::left_automation_track ()
1622 if (clear_entered_track) {
1623 set_entered_track (0);
1624 clear_entered_track = false;
1634 if (scrubbing_direction == 0) {
1636 session->request_locate (drag_info.current_pointer_frame, false);
1637 session->request_transport_speed (0.1);
1638 scrubbing_direction = 1;
1642 if (last_scrub_x > drag_info.current_pointer_x) {
1644 /* pointer moved to the left */
1646 if (scrubbing_direction > 0) {
1648 /* we reversed direction to go backwards */
1651 scrub_reverse_distance += (int) (last_scrub_x - drag_info.current_pointer_x);
1655 /* still moving to the left (backwards) */
1657 scrub_reversals = 0;
1658 scrub_reverse_distance = 0;
1660 delta = 0.01 * (last_scrub_x - drag_info.current_pointer_x);
1661 session->request_transport_speed (session->transport_speed() - delta);
1665 /* pointer moved to the right */
1667 if (scrubbing_direction < 0) {
1668 /* we reversed direction to go forward */
1671 scrub_reverse_distance += (int) (drag_info.current_pointer_x - last_scrub_x);
1674 /* still moving to the right */
1676 scrub_reversals = 0;
1677 scrub_reverse_distance = 0;
1679 delta = 0.01 * (drag_info.current_pointer_x - last_scrub_x);
1680 session->request_transport_speed (session->transport_speed() + delta);
1684 /* if there have been more than 2 opposite motion moves detected, or one that moves
1685 back more than 10 pixels, reverse direction
1688 if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
1690 if (scrubbing_direction > 0) {
1691 /* was forwards, go backwards */
1692 session->request_transport_speed (-0.1);
1693 scrubbing_direction = -1;
1695 /* was backwards, go forwards */
1696 session->request_transport_speed (0.1);
1697 scrubbing_direction = 1;
1700 scrub_reverse_distance = 0;
1701 scrub_reversals = 0;
1705 last_scrub_x = drag_info.current_pointer_x;
1709 Editor::motion_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type, bool from_autoscroll)
1711 if (event->motion.is_hint) {
1714 /* We call this so that MOTION_NOTIFY events continue to be
1715 delivered to the canvas. We need to do this because we set
1716 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
1717 the density of the events, at the expense of a round-trip
1718 to the server. Given that this will mostly occur on cases
1719 where DISPLAY = :0.0, and given the cost of what the motion
1720 event might do, its a good tradeoff.
1723 track_canvas->get_pointer (x, y);
1726 if (current_stepping_trackview) {
1727 /* don't keep the persistent stepped trackview if the mouse moves */
1728 current_stepping_trackview = 0;
1729 step_timeout.disconnect ();
1732 if (session && session->actively_recording()) {
1733 /* Sorry. no dragging stuff around while we record */
1737 drag_info.item_type = item_type;
1738 drag_info.last_pointer_x = drag_info.current_pointer_x;
1739 drag_info.last_pointer_y = drag_info.current_pointer_y;
1740 drag_info.current_pointer_frame = event_frame (event, &drag_info.current_pointer_x,
1741 &drag_info.current_pointer_y);
1744 switch (mouse_mode) {
1756 if (!from_autoscroll && drag_info.item) {
1757 /* item != 0 is the best test i can think of for dragging.
1759 if (!drag_info.move_threshold_passed) {
1761 bool x_threshold_passed = (::llabs ((nframes64_t) (drag_info.current_pointer_x - drag_info.grab_x)) > 4LL);
1762 bool y_threshold_passed = (::llabs ((nframes64_t) (drag_info.current_pointer_y - drag_info.grab_y)) > 4LL);
1764 drag_info.move_threshold_passed = (x_threshold_passed || y_threshold_passed);
1766 // and change the initial grab loc/frame if this drag info wants us to
1768 if (drag_info.want_move_threshold && drag_info.move_threshold_passed) {
1769 drag_info.grab_frame = drag_info.current_pointer_frame;
1770 drag_info.grab_x = drag_info.current_pointer_x;
1771 drag_info.grab_y = drag_info.current_pointer_y;
1772 drag_info.last_pointer_frame = drag_info.grab_frame;
1773 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
1778 switch (item_type) {
1779 case PlayheadCursorItem:
1781 case ControlPointItem:
1782 case RangeMarkerBarItem:
1783 case TransportMarkerBarItem:
1784 case CdMarkerBarItem:
1785 case TempoMarkerItem:
1786 case MeterMarkerItem:
1787 case RegionViewNameHighlight:
1788 case StartSelectionTrimItem:
1789 case EndSelectionTrimItem:
1792 case AutomationLineItem:
1793 case FadeInHandleItem:
1794 case FadeOutHandleItem:
1797 case ImageFrameHandleStartItem:
1798 case ImageFrameHandleEndItem:
1799 case MarkerViewHandleStartItem:
1800 case MarkerViewHandleEndItem:
1803 if (drag_info.item && (event->motion.state & Gdk::BUTTON1_MASK ||
1804 (event->motion.state & Gdk::BUTTON2_MASK))) {
1805 if (!from_autoscroll) {
1806 maybe_autoscroll_horizontally (&event->motion);
1808 (this->*(drag_info.motion_callback)) (item, event);
1817 switch (mouse_mode) {
1823 if (drag_info.item && (event->motion.state & GDK_BUTTON1_MASK ||
1824 (event->motion.state & GDK_BUTTON2_MASK))) {
1825 if (!from_autoscroll) {
1826 maybe_autoscroll (&event->motion);
1828 (this->*(drag_info.motion_callback)) (item, event);
1839 track_canvas_motion (event);
1840 // drag_info.last_pointer_frame = drag_info.current_pointer_frame;
1848 Editor::break_drag ()
1850 stop_canvas_autoscroll ();
1851 hide_verbose_canvas_cursor ();
1853 if (drag_info.item) {
1854 drag_info.item->ungrab (0);
1856 /* put it back where it came from */
1861 drag_info.item->i2w (cxw, cyw);
1862 drag_info.item->move (drag_info.original_x - cxw, drag_info.original_y - cyw);
1869 Editor::finalize_drag ()
1872 drag_info.copy = false;
1873 drag_info.motion_callback = 0;
1874 drag_info.finished_callback = 0;
1875 drag_info.dest_trackview = 0;
1876 drag_info.source_trackview = 0;
1877 drag_info.last_frame_position = 0;
1878 drag_info.grab_frame = 0;
1879 drag_info.last_pointer_frame = 0;
1880 drag_info.current_pointer_frame = 0;
1881 drag_info.brushing = false;
1882 range_marker_drag_rect->hide();
1883 drag_info.clear_copied_locations ();
1887 Editor::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
1889 if (drag_info.item == 0) {
1890 fatal << _("programming error: start_grab called without drag item") << endmsg;
1896 cursor = which_grabber_cursor ();
1899 // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
1901 if (event->button.button == 2) {
1902 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
1903 drag_info.y_constrained = true;
1904 drag_info.x_constrained = false;
1906 drag_info.y_constrained = false;
1907 drag_info.x_constrained = true;
1910 drag_info.x_constrained = false;
1911 drag_info.y_constrained = false;
1914 drag_info.grab_frame = event_frame (event, &drag_info.grab_x, &drag_info.grab_y);
1915 drag_info.last_pointer_frame = drag_info.grab_frame;
1916 drag_info.current_pointer_frame = drag_info.grab_frame;
1917 drag_info.current_pointer_x = drag_info.grab_x;
1918 drag_info.current_pointer_y = drag_info.grab_y;
1919 drag_info.last_pointer_x = drag_info.current_pointer_x;
1920 drag_info.last_pointer_y = drag_info.current_pointer_y;
1921 drag_info.cumulative_x_drag = 0;
1922 drag_info.cumulative_y_drag = 0;
1923 drag_info.first_move = true;
1924 drag_info.move_threshold_passed = false;
1925 drag_info.want_move_threshold = false;
1926 drag_info.pointer_frame_offset = 0;
1927 drag_info.brushing = false;
1928 drag_info.clear_copied_locations ();
1930 drag_info.original_x = 0;
1931 drag_info.original_y = 0;
1932 drag_info.item->i2w (drag_info.original_x, drag_info.original_y);
1934 drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK,
1936 event->button.time);
1938 if (session && session->transport_rolling()) {
1939 drag_info.was_rolling = true;
1941 drag_info.was_rolling = false;
1944 switch (snap_type) {
1945 case SnapToRegionStart:
1946 case SnapToRegionEnd:
1947 case SnapToRegionSync:
1948 case SnapToRegionBoundary:
1949 build_region_boundary_cache ();
1957 Editor::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t time)
1959 drag_info.item->ungrab (0);
1960 drag_info.item = new_item;
1963 cursor = which_grabber_cursor ();
1966 drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK, *cursor, time);
1970 Editor::end_grab (ArdourCanvas::Item* item, GdkEvent* event)
1972 bool did_drag = false;
1974 stop_canvas_autoscroll ();
1976 if (drag_info.item == 0) {
1980 drag_info.item->ungrab (event->button.time);
1982 if (drag_info.finished_callback) {
1983 drag_info.last_pointer_x = drag_info.current_pointer_x;
1984 drag_info.last_pointer_y = drag_info.current_pointer_y;
1985 (this->*(drag_info.finished_callback)) (item, event);
1988 did_drag = !drag_info.first_move;
1990 hide_verbose_canvas_cursor();
1998 Editor::start_fade_in_grab (ArdourCanvas::Item* item, GdkEvent* event)
2000 drag_info.item = item;
2001 drag_info.motion_callback = &Editor::fade_in_drag_motion_callback;
2002 drag_info.finished_callback = &Editor::fade_in_drag_finished_callback;
2006 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
2007 fatal << _("programming error: fade in canvas item has no regionview data pointer!") << endmsg;
2011 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
2014 drag_info.pointer_frame_offset = drag_info.grab_frame - ((nframes64_t) arv->audio_region()->fade_in()->back()->when + arv->region()->position());
2018 Editor::fade_in_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2020 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
2022 nframes64_t fade_length;
2024 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2025 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2031 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2035 if (pos < (arv->region()->position() + 64)) {
2036 fade_length = 64; // this should be a minimum defined somewhere
2037 } else if (pos > arv->region()->last_frame()) {
2038 fade_length = arv->region()->length();
2040 fade_length = pos - arv->region()->position();
2042 /* mapover the region selection */
2044 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2046 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2052 tmp->reset_fade_in_shape_width (fade_length);
2055 show_verbose_duration_cursor (arv->region()->position(), arv->region()->position() + fade_length, 10);
2057 drag_info.first_move = false;
2061 Editor::fade_in_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2063 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
2065 nframes64_t fade_length;
2067 if (drag_info.first_move) return;
2069 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2070 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2075 if (pos < (arv->region()->position() + 64)) {
2076 fade_length = 64; // this should be a minimum defined somewhere
2077 } else if (pos > arv->region()->last_frame()) {
2078 fade_length = arv->region()->length();
2080 fade_length = pos - arv->region()->position();
2083 begin_reversible_command (_("change fade in length"));
2085 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2087 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2093 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
2094 XMLNode &before = alist->get_state();
2096 tmp->audio_region()->set_fade_in_length (fade_length);
2097 tmp->audio_region()->set_fade_in_active (true);
2099 XMLNode &after = alist->get_state();
2100 session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2103 commit_reversible_command ();
2107 Editor::start_fade_out_grab (ArdourCanvas::Item* item, GdkEvent* event)
2109 drag_info.item = item;
2110 drag_info.motion_callback = &Editor::fade_out_drag_motion_callback;
2111 drag_info.finished_callback = &Editor::fade_out_drag_finished_callback;
2115 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
2116 fatal << _("programming error: fade out canvas item has no regionview data pointer!") << endmsg;
2120 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
2122 drag_info.pointer_frame_offset = drag_info.grab_frame - (arv->region()->length() - (nframes64_t) arv->audio_region()->fade_out()->back()->when + arv->region()->position());
2126 Editor::fade_out_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2128 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
2130 nframes64_t fade_length;
2132 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2133 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2138 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2142 if (pos > (arv->region()->last_frame() - 64)) {
2143 fade_length = 64; // this should really be a minimum fade defined somewhere
2145 else if (pos < arv->region()->position()) {
2146 fade_length = arv->region()->length();
2149 fade_length = arv->region()->last_frame() - pos;
2152 /* mapover the region selection */
2154 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2156 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2162 tmp->reset_fade_out_shape_width (fade_length);
2165 show_verbose_duration_cursor (arv->region()->last_frame() - fade_length, arv->region()->last_frame(), 10);
2167 drag_info.first_move = false;
2171 Editor::fade_out_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2173 if (drag_info.first_move) return;
2175 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
2177 nframes64_t fade_length;
2179 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2180 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2186 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2190 if (pos > (arv->region()->last_frame() - 64)) {
2191 fade_length = 64; // this should really be a minimum fade defined somewhere
2193 else if (pos < arv->region()->position()) {
2194 fade_length = arv->region()->length();
2197 fade_length = arv->region()->last_frame() - pos;
2200 begin_reversible_command (_("change fade out length"));
2202 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2204 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2210 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
2211 XMLNode &before = alist->get_state();
2213 tmp->audio_region()->set_fade_out_length (fade_length);
2214 tmp->audio_region()->set_fade_out_active (true);
2216 XMLNode &after = alist->get_state();
2217 session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2220 commit_reversible_command ();
2224 Editor::start_cursor_grab (ArdourCanvas::Item* item, GdkEvent* event)
2226 drag_info.item = item;
2227 drag_info.motion_callback = &Editor::cursor_drag_motion_callback;
2228 drag_info.finished_callback = &Editor::cursor_drag_finished_callback;
2232 if ((drag_info.data = (item->get_data ("cursor"))) == 0) {
2233 fatal << _("programming error: cursor canvas item has no cursor data pointer!") << endmsg;
2237 Cursor* cursor = (Cursor *) drag_info.data;
2239 if (cursor == playhead_cursor) {
2240 _dragging_playhead = true;
2242 if (session && drag_info.was_rolling) {
2243 session->request_stop ();
2246 if (session && session->is_auditioning()) {
2247 session->cancel_audition ();
2251 drag_info.pointer_frame_offset = drag_info.grab_frame - cursor->current_frame;
2253 show_verbose_time_cursor (cursor->current_frame, 10);
2257 Editor::cursor_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2259 Cursor* cursor = (Cursor *) drag_info.data;
2260 nframes64_t adjusted_frame;
2262 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2263 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2269 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2270 if (cursor == playhead_cursor) {
2271 snap_to (adjusted_frame);
2275 if (adjusted_frame == drag_info.last_pointer_frame) return;
2277 cursor->set_position (adjusted_frame);
2279 show_verbose_time_cursor (cursor->current_frame, 10);
2282 track_canvas->update_now ();
2284 UpdateAllTransportClocks (cursor->current_frame);
2286 drag_info.last_pointer_frame = adjusted_frame;
2287 drag_info.first_move = false;
2291 Editor::cursor_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2293 if (drag_info.first_move) return;
2295 cursor_drag_motion_callback (item, event);
2297 _dragging_playhead = false;
2299 if (item == &playhead_cursor->canvas_item) {
2301 session->request_locate (playhead_cursor->current_frame, drag_info.was_rolling);
2307 Editor::update_marker_drag_item (Location *location)
2309 double x1 = frame_to_pixel (location->start());
2310 double x2 = frame_to_pixel (location->end());
2312 if (location->is_mark()) {
2313 marker_drag_line_points.front().set_x(x1);
2314 marker_drag_line_points.back().set_x(x1);
2315 marker_drag_line->property_points() = marker_drag_line_points;
2317 range_marker_drag_rect->property_x1() = x1;
2318 range_marker_drag_rect->property_x2() = x2;
2324 Editor::start_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2328 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2329 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2335 Location *location = find_location_from_marker (marker, is_start);
2337 drag_info.item = item;
2338 drag_info.data = marker;
2339 drag_info.motion_callback = &Editor::marker_drag_motion_callback;
2340 drag_info.finished_callback = &Editor::marker_drag_finished_callback;
2344 _dragging_edit_point = true;
2346 drag_info.pointer_frame_offset = drag_info.grab_frame - (is_start ? location->start() : location->end());
2348 update_marker_drag_item (location);
2350 if (location->is_mark()) {
2351 // marker_drag_line->show();
2352 // marker_drag_line->raise_to_top();
2354 range_marker_drag_rect->show();
2355 //range_marker_drag_rect->raise_to_top();
2359 show_verbose_time_cursor (location->start(), 10);
2361 show_verbose_time_cursor (location->end(), 10);
2364 Selection::Operation op = Keyboard::selection_type (event->button.state);
2367 case Selection::Toggle:
2368 selection->toggle (marker);
2370 case Selection::Set:
2371 if (!selection->selected (marker)) {
2372 selection->set (marker);
2375 case Selection::Extend:
2377 Locations::LocationList ll;
2378 list<Marker*> to_add;
2380 selection->markers.range (s, e);
2381 s = min (marker->position(), s);
2382 e = max (marker->position(), e);
2385 if (e < max_frames) {
2388 session->locations()->find_all_between (s, e, ll, Location::Flags (0));
2389 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
2390 LocationMarkers* lm = find_location_markers (*i);
2393 to_add.push_back (lm->start);
2396 to_add.push_back (lm->end);
2400 if (!to_add.empty()) {
2401 selection->add (to_add);
2405 case Selection::Add:
2406 selection->add (marker);
2410 /* set up copies for us to manipulate during the drag */
2412 drag_info.clear_copied_locations ();
2414 for (MarkerSelection::iterator i = selection->markers.begin(); i != selection->markers.end(); ++i) {
2415 Location *l = find_location_from_marker (*i, is_start);
2416 drag_info.copied_locations.push_back (new Location (*l));
2421 Editor::marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2423 nframes64_t f_delta = 0;
2424 nframes64_t newframe;
2426 bool move_both = false;
2427 Marker* dragged_marker = (Marker*) drag_info.data;
2429 Location *real_location;
2430 Location *copy_location;
2432 if (drag_info.pointer_frame_offset <= drag_info.current_pointer_frame) {
2433 newframe = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2438 nframes64_t next = newframe;
2440 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2441 snap_to (newframe, 0, true);
2444 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
2448 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2452 MarkerSelection::iterator i;
2453 list<Location*>::iterator x;
2455 /* find the marker we're dragging, and compute the delta */
2457 for (i = selection->markers.begin(), x = drag_info.copied_locations.begin();
2458 x != drag_info.copied_locations.end() && i != selection->markers.end();
2464 if (marker == dragged_marker) {
2466 if ((real_location = find_location_from_marker (marker, is_start)) == 0) {
2471 if (real_location->is_mark()) {
2472 f_delta = newframe - copy_location->start();
2476 switch (marker->type()) {
2478 case Marker::LoopStart:
2479 case Marker::PunchIn:
2480 f_delta = newframe - copy_location->start();
2484 case Marker::LoopEnd:
2485 case Marker::PunchOut:
2486 f_delta = newframe - copy_location->end();
2489 /* what kind of marker is this ? */
2497 if (i == selection->markers.end()) {
2498 /* hmm, impossible - we didn't find the dragged marker */
2502 /* now move them all */
2504 for (i = selection->markers.begin(), x = drag_info.copied_locations.begin();
2505 x != drag_info.copied_locations.end() && i != selection->markers.end();
2511 /* call this to find out if its the start or end */
2513 if ((real_location = find_location_from_marker (marker, is_start)) == 0) {
2517 if (real_location->locked()) {
2521 if (copy_location->is_mark()) {
2525 copy_location->set_start (copy_location->start() + f_delta);
2529 nframes64_t new_start = copy_location->start() + f_delta;
2530 nframes64_t new_end = copy_location->end() + f_delta;
2532 if (is_start) { // start-of-range marker
2535 copy_location->set_start (new_start);
2536 copy_location->set_end (new_end);
2537 } else if (new_start < copy_location->end()) {
2538 copy_location->set_start (new_start);
2540 snap_to (next, 1, true);
2541 copy_location->set_end (next);
2542 copy_location->set_start (newframe);
2545 } else { // end marker
2548 copy_location->set_end (new_end);
2549 copy_location->set_start (new_start);
2550 } else if (new_end > copy_location->start()) {
2551 copy_location->set_end (new_end);
2552 } else if (newframe > 0) {
2553 snap_to (next, -1, true);
2554 copy_location->set_start (next);
2555 copy_location->set_end (newframe);
2559 update_marker_drag_item (copy_location);
2561 LocationMarkers* lm = find_location_markers (real_location);
2564 lm->set_position (copy_location->start(), copy_location->end());
2568 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
2569 drag_info.first_move = false;
2571 if (drag_info.copied_locations.empty()) {
2575 edit_point_clock.set (drag_info.copied_locations.front()->start());
2576 show_verbose_time_cursor (newframe, 10);
2579 track_canvas->update_now ();
2581 edit_point_clock.set (copy_location->start());
2585 Editor::marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2587 if (drag_info.first_move) {
2589 /* just a click, do nothing but finish
2590 off the selection process
2593 Selection::Operation op = Keyboard::selection_type (event->button.state);
2594 Marker* marker = (Marker *) drag_info.data;
2597 case Selection::Set:
2598 if (selection->selected (marker) && selection->markers.size() > 1) {
2599 selection->set (marker);
2603 case Selection::Toggle:
2604 case Selection::Extend:
2605 case Selection::Add:
2612 _dragging_edit_point = false;
2615 begin_reversible_command ( _("move marker") );
2616 XMLNode &before = session->locations()->get_state();
2618 MarkerSelection::iterator i;
2619 list<Location*>::iterator x;
2622 for (i = selection->markers.begin(), x = drag_info.copied_locations.begin();
2623 x != drag_info.copied_locations.end() && i != selection->markers.end();
2626 Location * location = find_location_from_marker ((*i), is_start);
2630 if (location->locked()) {
2634 if (location->is_mark()) {
2635 location->set_start ((*x)->start());
2637 location->set ((*x)->start(), (*x)->end());
2642 XMLNode &after = session->locations()->get_state();
2643 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
2644 commit_reversible_command ();
2646 marker_drag_line->hide();
2647 range_marker_drag_rect->hide();
2651 Editor::start_meter_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2654 MeterMarker* meter_marker;
2656 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2657 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2661 meter_marker = dynamic_cast<MeterMarker*> (marker);
2663 MetricSection& section (meter_marker->meter());
2665 if (!section.movable()) {
2669 drag_info.item = item;
2670 drag_info.copy = false;
2671 drag_info.data = marker;
2672 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2673 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2677 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2679 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2683 Editor::start_meter_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2686 MeterMarker* meter_marker;
2688 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2689 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2693 meter_marker = dynamic_cast<MeterMarker*> (marker);
2695 // create a dummy marker for visual representation of moving the copy.
2696 // The actual copying is not done before we reach the finish callback.
2698 snprintf (name, sizeof(name), "%g/%g", meter_marker->meter().beats_per_bar(), meter_marker->meter().note_divisor ());
2699 MeterMarker* new_marker = new MeterMarker(*this, *meter_group, ARDOUR_UI::config()->canvasvar_MeterMarker.get(), name,
2700 *new MeterSection(meter_marker->meter()));
2702 drag_info.item = &new_marker->the_item();
2703 drag_info.copy = true;
2704 drag_info.data = new_marker;
2705 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2706 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2710 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2712 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2716 Editor::meter_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2718 MeterMarker* marker = (MeterMarker *) drag_info.data;
2719 nframes64_t adjusted_frame;
2721 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2722 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2728 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2729 snap_to (adjusted_frame);
2732 if (adjusted_frame == drag_info.last_pointer_frame) return;
2734 marker->set_position (adjusted_frame);
2737 drag_info.last_pointer_frame = adjusted_frame;
2738 drag_info.first_move = false;
2740 show_verbose_time_cursor (adjusted_frame, 10);
2744 Editor::meter_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2746 if (drag_info.first_move) return;
2748 meter_marker_drag_motion_callback (drag_info.item, event);
2750 MeterMarker* marker = (MeterMarker *) drag_info.data;
2753 TempoMap& map (session->tempo_map());
2754 map.bbt_time (drag_info.last_pointer_frame, when);
2756 if (drag_info.copy == true) {
2757 begin_reversible_command (_("copy meter mark"));
2758 XMLNode &before = map.get_state();
2759 map.add_meter (marker->meter(), when);
2760 XMLNode &after = map.get_state();
2761 session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2762 commit_reversible_command ();
2764 // delete the dummy marker we used for visual representation of copying.
2765 // a new visual marker will show up automatically.
2768 begin_reversible_command (_("move meter mark"));
2769 XMLNode &before = map.get_state();
2770 map.move_meter (marker->meter(), when);
2771 XMLNode &after = map.get_state();
2772 session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2773 commit_reversible_command ();
2778 Editor::start_tempo_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2781 TempoMarker* tempo_marker;
2783 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2784 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2788 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2789 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2793 MetricSection& section (tempo_marker->tempo());
2795 if (!section.movable()) {
2799 drag_info.item = item;
2800 drag_info.copy = false;
2801 drag_info.data = marker;
2802 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2803 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2807 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2808 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2812 Editor::start_tempo_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2815 TempoMarker* tempo_marker;
2817 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2818 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2822 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2823 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2827 // create a dummy marker for visual representation of moving the copy.
2828 // The actual copying is not done before we reach the finish callback.
2830 snprintf (name, sizeof (name), "%.2f", tempo_marker->tempo().beats_per_minute());
2831 TempoMarker* new_marker = new TempoMarker(*this, *tempo_group, ARDOUR_UI::config()->canvasvar_TempoMarker.get(), name,
2832 *new TempoSection(tempo_marker->tempo()));
2834 drag_info.item = &new_marker->the_item();
2835 drag_info.copy = true;
2836 drag_info.data = new_marker;
2837 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2838 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2842 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2844 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2848 Editor::tempo_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2850 TempoMarker* marker = (TempoMarker *) drag_info.data;
2851 nframes64_t adjusted_frame;
2853 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2854 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2860 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2861 snap_to (adjusted_frame);
2864 if (adjusted_frame == drag_info.last_pointer_frame) return;
2866 /* OK, we've moved far enough to make it worth actually move the thing. */
2868 marker->set_position (adjusted_frame);
2870 show_verbose_time_cursor (adjusted_frame, 10);
2872 drag_info.last_pointer_frame = adjusted_frame;
2873 drag_info.first_move = false;
2877 Editor::tempo_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2879 if (drag_info.first_move) return;
2881 tempo_marker_drag_motion_callback (drag_info.item, event);
2883 TempoMarker* marker = (TempoMarker *) drag_info.data;
2886 TempoMap& map (session->tempo_map());
2887 map.bbt_time (drag_info.last_pointer_frame, when);
2889 if (drag_info.copy == true) {
2890 begin_reversible_command (_("copy tempo mark"));
2891 XMLNode &before = map.get_state();
2892 map.add_tempo (marker->tempo(), when);
2893 XMLNode &after = map.get_state();
2894 session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2895 commit_reversible_command ();
2897 // delete the dummy marker we used for visual representation of copying.
2898 // a new visual marker will show up automatically.
2901 begin_reversible_command (_("move tempo mark"));
2902 XMLNode &before = map.get_state();
2903 map.move_tempo (marker->tempo(), when);
2904 XMLNode &after = map.get_state();
2905 session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2906 commit_reversible_command ();
2911 Editor::remove_gain_control_point (ArdourCanvas::Item*item, GdkEvent* event)
2913 ControlPoint* control_point;
2915 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2916 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2920 // We shouldn't remove the first or last gain point
2921 if (control_point->line().is_last_point(*control_point) ||
2922 control_point->line().is_first_point(*control_point)) {
2926 control_point->line().remove_point (*control_point);
2930 Editor::remove_control_point (ArdourCanvas::Item* item, GdkEvent* event)
2932 ControlPoint* control_point;
2934 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2935 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2939 control_point->line().remove_point (*control_point);
2943 Editor::start_control_point_grab (ArdourCanvas::Item* item, GdkEvent* event)
2945 ControlPoint* control_point;
2947 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2948 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2952 drag_info.item = item;
2953 drag_info.data = control_point;
2954 drag_info.motion_callback = &Editor::control_point_drag_motion_callback;
2955 drag_info.finished_callback = &Editor::control_point_drag_finished_callback;
2957 start_grab (event, fader_cursor);
2959 // start the grab at the center of the control point so
2960 // the point doesn't 'jump' to the mouse after the first drag
2961 drag_info.grab_x = control_point->get_x();
2962 drag_info.grab_y = control_point->get_y();
2964 control_point->line().parent_group().i2w(drag_info.grab_x, drag_info.grab_y);
2965 track_canvas->w2c(drag_info.grab_x, drag_info.grab_y, drag_info.grab_x, drag_info.grab_y);
2967 drag_info.grab_frame = pixel_to_frame(drag_info.grab_x);
2969 control_point->line().start_drag (control_point, drag_info.grab_frame, 0);
2971 float fraction = 1.0 - (control_point->get_y() / control_point->line().height());
2972 set_verbose_canvas_cursor (control_point->line().get_verbose_cursor_string (fraction),
2973 drag_info.current_pointer_x + 10, drag_info.current_pointer_y + 10);
2975 show_verbose_canvas_cursor ();
2979 Editor::control_point_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2981 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2983 double dx = drag_info.current_pointer_x - drag_info.last_pointer_x;
2984 double dy = drag_info.current_pointer_y - drag_info.last_pointer_y;
2986 if (event->button.state & Keyboard::SecondaryModifier) {
2991 double cx = drag_info.grab_x + drag_info.cumulative_x_drag + dx;
2992 double cy = drag_info.grab_y + drag_info.cumulative_y_drag + dy;
2994 // calculate zero crossing point. back off by .01 to stay on the
2995 // positive side of zero
2997 double zero_gain_y = (1.0 - ZERO_GAIN_FRACTION) * cp->line().height() - .01;
2998 cp->line().parent_group().i2w(_unused, zero_gain_y);
3000 // make sure we hit zero when passing through
3001 if ((cy < zero_gain_y and (cy - dy) > zero_gain_y)
3002 or (cy > zero_gain_y and (cy - dy) < zero_gain_y)) {
3006 if (drag_info.x_constrained) {
3007 cx = drag_info.grab_x;
3009 if (drag_info.y_constrained) {
3010 cy = drag_info.grab_y;
3013 drag_info.cumulative_x_drag = cx - drag_info.grab_x;
3014 drag_info.cumulative_y_drag = cy - drag_info.grab_y;
3016 cp->line().parent_group().w2i (cx, cy);
3020 cy = min ((double) cp->line().height(), cy);
3022 //translate cx to frames
3023 nframes64_t cx_frames = unit_to_frame (cx);
3025 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier()) && !drag_info.x_constrained) {
3026 snap_to (cx_frames);
3029 float fraction = 1.0 - (cy / cp->line().height());
3033 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) {
3039 cp->line().point_drag (*cp, cx_frames , fraction, push);
3041 set_verbose_canvas_cursor_text (cp->line().get_verbose_cursor_string (fraction));
3043 drag_info.first_move = false;
3047 Editor::control_point_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3049 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
3051 if (drag_info.first_move) {
3055 if ((event->type == GDK_BUTTON_RELEASE) && (event->button.button == 1) && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3056 reset_point_selection ();
3060 control_point_drag_motion_callback (item, event);
3062 cp->line().end_drag (cp);
3066 Editor::start_line_grab_from_regionview (ArdourCanvas::Item* item, GdkEvent* event)
3068 switch (mouse_mode) {
3070 assert(dynamic_cast<AudioRegionView*>(clicked_regionview));
3071 start_line_grab (dynamic_cast<AudioRegionView*>(clicked_regionview)->get_gain_line(), event);
3079 Editor::start_line_grab_from_line (ArdourCanvas::Item* item, GdkEvent* event)
3083 if ((al = reinterpret_cast<AutomationLine*> (item->get_data ("line"))) == 0) {
3084 fatal << _("programming error: line canvas item has no line pointer!") << endmsg;
3088 start_line_grab (al, event);
3092 Editor::start_line_grab (AutomationLine* line, GdkEvent* event)
3096 nframes64_t frame_within_region;
3098 /* need to get x coordinate in terms of parent (TimeAxisItemView)
3102 cx = event->button.x;
3103 cy = event->button.y;
3104 line->parent_group().w2i (cx, cy);
3105 frame_within_region = (nframes64_t) floor (cx * frames_per_unit);
3107 if (!line->control_points_adjacent (frame_within_region, current_line_drag_info.before,
3108 current_line_drag_info.after)) {
3109 /* no adjacent points */
3113 drag_info.item = &line->grab_item();
3114 drag_info.data = line;
3115 drag_info.motion_callback = &Editor::line_drag_motion_callback;
3116 drag_info.finished_callback = &Editor::line_drag_finished_callback;
3118 start_grab (event, fader_cursor);
3120 double fraction = 1.0 - (cy / line->height());
3122 line->start_drag (0, drag_info.grab_frame, fraction);
3124 set_verbose_canvas_cursor (line->get_verbose_cursor_string (fraction),
3125 drag_info.current_pointer_x + 10, drag_info.current_pointer_y + 10);
3126 show_verbose_canvas_cursor ();
3130 Editor::line_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
3132 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
3134 double dy = drag_info.current_pointer_y - drag_info.last_pointer_y;
3136 if (event->button.state & Keyboard::SecondaryModifier) {
3140 double cx = drag_info.current_pointer_x;
3141 double cy = drag_info.grab_y + drag_info.cumulative_y_drag + dy;
3143 // calculate zero crossing point. back off by .01 to stay on the
3144 // positive side of zero
3146 double zero_gain_y = (1.0 - ZERO_GAIN_FRACTION) * line->height() - .01;
3147 line->parent_group().i2w(_unused, zero_gain_y);
3149 // make sure we hit zero when passing through
3150 if ((cy < zero_gain_y and (cy - dy) > zero_gain_y)
3151 or (cy > zero_gain_y and (cy - dy) < zero_gain_y)) {
3155 drag_info.cumulative_y_drag = cy - drag_info.grab_y;
3157 line->parent_group().w2i (cx, cy);
3160 cy = min ((double) line->height(), cy);
3162 double fraction = 1.0 - (cy / line->height());
3166 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) {
3172 line->line_drag (current_line_drag_info.before, current_line_drag_info.after, fraction, push);
3174 set_verbose_canvas_cursor_text (line->get_verbose_cursor_string (fraction));
3178 Editor::line_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3180 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
3181 line_drag_motion_callback (item, event);
3186 Editor::start_region_grab (ArdourCanvas::Item* item, GdkEvent* event)
3188 if (selection->regions.empty() || clicked_regionview == 0) {
3191 _region_motion_group->raise_to_top ();
3192 drag_info.copy = false;
3193 drag_info.item = item;
3194 drag_info.data = clicked_regionview;
3196 if (Config->get_edit_mode() == Splice) {
3197 drag_info.motion_callback = &Editor::region_drag_splice_motion_callback;
3198 drag_info.finished_callback = &Editor::region_drag_splice_finished_callback;
3200 drag_info.motion_callback = &Editor::region_drag_motion_callback;
3201 drag_info.finished_callback = &Editor::region_drag_finished_callback;
3207 TimeAxisView* tvp = clicked_axisview;
3208 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
3210 if (tv && tv->is_track()) {
3211 speed = tv->get_diskstream()->speed();
3214 drag_info.last_frame_position = (nframes64_t) (clicked_regionview->region()->position() / speed);
3215 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
3216 drag_info.source_trackview = &clicked_regionview->get_time_axis_view();
3217 drag_info.dest_trackview = drag_info.source_trackview;
3218 // we want a move threshold
3219 drag_info.want_move_threshold = true;
3220 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3222 begin_reversible_command (_("move region(s)"));
3224 /* sync the canvas to what we think is its current state */
3225 track_canvas->update_now();
3229 Editor::start_create_region_grab (ArdourCanvas::Item* item, GdkEvent* event)
3231 drag_info.copy = false;
3232 drag_info.item = item;
3233 drag_info.data = clicked_axisview;
3234 drag_info.source_trackview = clicked_axisview;
3235 drag_info.dest_trackview = drag_info.source_trackview;
3236 drag_info.motion_callback = &Editor::create_region_drag_motion_callback;
3237 drag_info.finished_callback = &Editor::create_region_drag_finished_callback;
3243 Editor::start_region_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
3245 if (selection->regions.empty() || clicked_regionview == 0) {
3248 _region_motion_group->raise_to_top ();
3249 drag_info.copy = true;
3250 drag_info.item = item;
3251 drag_info.data = clicked_regionview;
3255 TimeAxisView* tv = &clicked_regionview->get_time_axis_view();
3256 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
3259 if (rtv && rtv->is_track()) {
3260 speed = rtv->get_diskstream()->speed();
3263 drag_info.source_trackview = &clicked_regionview->get_time_axis_view();
3264 drag_info.dest_trackview = drag_info.source_trackview;
3265 drag_info.last_frame_position = (nframes64_t) (clicked_regionview->region()->position() / speed);
3266 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
3267 // we want a move threshold
3268 drag_info.want_move_threshold = true;
3269 drag_info.motion_callback = &Editor::region_drag_motion_callback;
3270 drag_info.finished_callback = &Editor::region_drag_finished_callback;
3271 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3275 Editor::start_region_brush_grab (ArdourCanvas::Item* item, GdkEvent* event)
3277 if (selection->regions.empty() || clicked_regionview == 0 || Config->get_edit_mode() == Splice) {
3281 drag_info.copy = false;
3282 drag_info.item = item;
3283 drag_info.data = clicked_regionview;
3284 drag_info.motion_callback = &Editor::region_drag_motion_callback;
3285 drag_info.finished_callback = &Editor::region_drag_finished_callback;
3290 TimeAxisView* tvp = clicked_axisview;
3291 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
3293 if (tv && tv->is_track()) {
3294 speed = tv->get_diskstream()->speed();
3297 drag_info.last_frame_position = (nframes64_t) (clicked_regionview->region()->position() / speed);
3298 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
3299 drag_info.source_trackview = &clicked_regionview->get_time_axis_view();
3300 drag_info.dest_trackview = drag_info.source_trackview;
3301 // we want a move threshold
3302 drag_info.want_move_threshold = true;
3303 drag_info.brushing = true;
3305 begin_reversible_command (_("Drag region brush"));
3309 Editor::possibly_copy_regions_during_grab (GdkEvent* event)
3311 if (drag_info.copy && drag_info.move_threshold_passed && drag_info.want_move_threshold) {
3313 drag_info.want_move_threshold = false; // don't copy again
3315 /* duplicate the regionview(s) and region(s) */
3317 vector<RegionView*> new_regionviews;
3319 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3324 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
3325 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
3327 const boost::shared_ptr<const Region> original = rv->region();
3328 boost::shared_ptr<Region> region_copy = RegionFactory::create (original);
3331 boost::shared_ptr<AudioRegion> audioregion_copy
3332 = boost::dynamic_pointer_cast<AudioRegion>(region_copy);
3333 nrv = new AudioRegionView (*arv, audioregion_copy);
3335 boost::shared_ptr<MidiRegion> midiregion_copy
3336 = boost::dynamic_pointer_cast<MidiRegion>(region_copy);
3337 nrv = new MidiRegionView (*mrv, midiregion_copy);
3342 nrv->get_canvas_group()->show ();
3343 new_regionviews.push_back (nrv);
3346 if (new_regionviews.empty()) {
3350 /* reset selection to new regionviews. This will not set selection visual status for
3351 these regionviews since they don't belong to a track, so do that by hand too.
3354 selection->set (new_regionviews);
3356 for (vector<RegionView*>::iterator i = new_regionviews.begin(); i != new_regionviews.end(); ++i) {
3357 (*i)->set_selected (true);
3360 /* reset drag_info data to reflect the fact that we are dragging the copies */
3362 drag_info.data = new_regionviews.front();
3364 swap_grab (new_regionviews.front()->get_canvas_group (), 0, event->motion.time);
3366 sync the canvas to what we think is its current state
3367 without it, the canvas seems to
3368 "forget" to update properly after the upcoming reparent()
3369 ..only if the mouse is in rapid motion at the time of the grab.
3370 something to do with regionview creation raking so long?
3372 track_canvas->update_now();
3377 Editor::check_region_drag_possible (RouteTimeAxisView** tv)
3379 /* Which trackview is this ? */
3381 TimeAxisView* tvp = trackview_by_y_position (drag_info.current_pointer_y);
3382 (*tv) = dynamic_cast<RouteTimeAxisView*>(tvp);
3384 /* The region motion is only processed if the pointer is over
3388 if (!(*tv) || !(*tv)->is_track()) {
3389 /* To make sure we hide the verbose canvas cursor when the mouse is
3390 not held over and audiotrack.
3392 hide_verbose_canvas_cursor ();
3399 struct RegionSelectionByPosition {
3400 bool operator() (RegionView*a, RegionView* b) {
3401 return a->region()->position () < b->region()->position();
3406 Editor::region_drag_splice_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
3408 RouteTimeAxisView* tv;
3410 if (!check_region_drag_possible (&tv)) {
3414 if (!drag_info.move_threshold_passed) {
3420 if (drag_info.current_pointer_x - drag_info.grab_x > 0) {
3426 RegionSelection copy (selection->regions);
3428 RegionSelectionByPosition cmp;
3431 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
3433 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
3439 boost::shared_ptr<Playlist> playlist;
3441 if ((playlist = atv->playlist()) == 0) {
3445 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
3450 if (drag_info.current_pointer_frame < (*i)->region()->last_frame() + 1) {
3454 if (drag_info.current_pointer_frame > (*i)->region()->first_frame()) {
3460 playlist->shuffle ((*i)->region(), dir);
3462 drag_info.grab_x = drag_info.current_pointer_x;
3467 Editor::region_drag_splice_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3472 Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
3476 RegionView* rv = reinterpret_cast<RegionView*> (drag_info.data);
3477 nframes64_t pending_region_position = 0;
3478 int32_t pointer_y_span = 0, canvas_pointer_y_span = 0, original_pointer_order;
3479 int32_t visible_y_high = 0, visible_y_low = 512; //high meaning higher numbered.. not the height on the screen
3480 bool clamp_y_axis = false;
3481 vector<int32_t> height_list(512) ;
3482 vector<int32_t>::iterator j;
3483 RouteTimeAxisView* tv;
3485 possibly_copy_regions_during_grab (event);
3487 if (!check_region_drag_possible (&tv)) {
3491 original_pointer_order = drag_info.dest_trackview->order;
3493 /************************************************************
3495 ************************************************************/
3497 if (drag_info.brushing) {
3498 clamp_y_axis = true;
3503 if ((pointer_y_span = (drag_info.dest_trackview->order - tv->order)) != 0) {
3505 int32_t children = 0, numtracks = 0;
3506 // XXX hard coding track limit, oh my, so very very bad
3507 bitset <1024> tracks (0x00);
3508 /* get a bitmask representing the visible tracks */
3510 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
3511 TimeAxisView *tracklist_timeview;
3512 tracklist_timeview = (*i);
3513 RouteTimeAxisView* rtv2 = dynamic_cast<RouteTimeAxisView*>(tracklist_timeview);
3514 TimeAxisView::Children children_list;
3516 /* zeroes are audio tracks. ones are other types. */
3518 if (!rtv2->hidden()) {
3520 if (visible_y_high < rtv2->order) {
3521 visible_y_high = rtv2->order;
3523 if (visible_y_low > rtv2->order) {
3524 visible_y_low = rtv2->order;
3527 if (!rtv2->is_track()) {
3528 tracks = tracks |= (0x01 << rtv2->order);
3531 height_list[rtv2->order] = (*i)->current_height();
3534 if ((children_list = rtv2->get_child_list()).size() > 0) {
3535 for (TimeAxisView::Children::iterator j = children_list.begin(); j != children_list.end(); ++j) {
3536 tracks = tracks |= (0x01 << (rtv2->order + children));
3537 height_list[rtv2->order + children] = (*j)->current_height();
3545 /* find the actual span according to the canvas */
3547 canvas_pointer_y_span = pointer_y_span;
3548 if (drag_info.dest_trackview->order >= tv->order) {
3550 for (y = tv->order; y < drag_info.dest_trackview->order; y++) {
3551 if (height_list[y] == 0 ) {
3552 canvas_pointer_y_span--;
3557 for (y = drag_info.dest_trackview->order;y <= tv->order; y++) {
3558 if ( height_list[y] == 0 ) {
3559 canvas_pointer_y_span++;
3564 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3565 RegionView* rv2 = (*i);
3566 double ix1, ix2, iy1, iy2;
3569 if (rv2->region()->locked()) {
3573 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3574 rv2->get_canvas_frame()->i2w (ix1, iy1);
3575 iy1 += vertical_adjustment.get_value() - canvas_timebars_vsize;
3577 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3578 RouteTimeAxisView* rtv2 = dynamic_cast<RouteTimeAxisView*>(tvp2);
3580 if (rtv2->order != original_pointer_order) {
3581 /* this isn't the pointer track */
3583 if (canvas_pointer_y_span > 0) {
3585 /* moving up the canvas */
3586 if ((rtv2->order - canvas_pointer_y_span) >= visible_y_low) {
3588 int32_t visible_tracks = 0;
3589 while (visible_tracks < canvas_pointer_y_span ) {
3592 while (height_list[rtv2->order - (visible_tracks - n)] == 0) {
3593 /* we're passing through a hidden track */
3598 if (tracks[rtv2->order - (canvas_pointer_y_span - n)] != 0x00) {
3599 clamp_y_axis = true;
3603 clamp_y_axis = true;
3606 } else if (canvas_pointer_y_span < 0) {
3608 /*moving down the canvas*/
3610 if ((rtv2->order - (canvas_pointer_y_span - n)) <= visible_y_high) { // we will overflow
3613 int32_t visible_tracks = 0;
3615 while (visible_tracks > canvas_pointer_y_span ) {
3618 while (height_list[rtv2->order - (visible_tracks - n)] == 0) {
3622 if ( tracks[rtv2->order - ( canvas_pointer_y_span - n)] != 0x00) {
3623 clamp_y_axis = true;
3628 clamp_y_axis = true;
3634 /* this is the pointer's track */
3635 if ((rtv2->order - pointer_y_span) > visible_y_high) { // we will overflow
3636 clamp_y_axis = true;
3637 } else if ((rtv2->order - pointer_y_span) < visible_y_low) { // we will underflow
3638 clamp_y_axis = true;
3646 } else if (drag_info.dest_trackview == tv) {
3647 clamp_y_axis = true;
3651 if (!clamp_y_axis) {
3652 drag_info.dest_trackview = tv;
3655 /************************************************************
3657 ************************************************************/
3659 /* compute the amount of pointer motion in frames, and where
3660 the region would be if we moved it by that much.
3662 if ( drag_info.move_threshold_passed ) {
3664 if (drag_info.current_pointer_frame >= drag_info.pointer_frame_offset) {
3666 nframes64_t sync_frame;
3667 nframes64_t sync_offset;
3670 pending_region_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
3672 sync_offset = rv->region()->sync_offset (sync_dir);
3674 /* we don't handle a sync point that lies before zero.
3676 if (sync_dir >= 0 || (sync_dir < 0 && pending_region_position >= sync_offset)) {
3677 sync_frame = pending_region_position + (sync_dir*sync_offset);
3679 /* we snap if the snap modifier is not enabled.
3682 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3683 snap_to (sync_frame);
3686 pending_region_position = rv->region()->adjust_to_sync (sync_frame);
3689 pending_region_position = drag_info.last_frame_position;
3693 pending_region_position = 0;
3696 if (pending_region_position > max_frames - rv->region()->length()) {
3697 pending_region_position = drag_info.last_frame_position;
3700 // printf ("3: pending_region_position= %lu %lu\n", pending_region_position, drag_info.last_frame_position );
3702 bool x_move_allowed;
3704 if (Config->get_edit_mode() == Lock) {
3705 if (drag_info.copy) {
3706 x_move_allowed = !drag_info.x_constrained;
3708 /* in locked edit mode, reverse the usual meaning of x_constrained */
3709 x_move_allowed = drag_info.x_constrained;
3712 x_move_allowed = !drag_info.x_constrained;
3715 if (( pending_region_position != drag_info.last_frame_position) && x_move_allowed ) {
3717 /* now compute the canvas unit distance we need to move the regionview
3718 to make it appear at the new location.
3721 if (pending_region_position > drag_info.last_frame_position) {
3722 x_delta = ((double) (pending_region_position - drag_info.last_frame_position) / frames_per_unit);
3724 x_delta = -((double) (drag_info.last_frame_position - pending_region_position) / frames_per_unit);
3725 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3727 RegionView* rv2 = (*i);
3729 // If any regionview is at zero, we need to know so we can stop further leftward motion.
3731 double ix1, ix2, iy1, iy2;
3732 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3733 rv2->get_canvas_frame()->i2w (ix1, iy1);
3735 if (-x_delta > ix1 + horizontal_adjustment.get_value()) {
3738 pending_region_position = drag_info.last_frame_position;
3745 drag_info.last_frame_position = pending_region_position;
3752 /* threshold not passed */
3757 /*************************************************************
3759 ************************************************************/
3761 if (x_delta == 0 && (pointer_y_span == 0)) {
3762 /* haven't reached next snap point, and we're not switching
3763 trackviews. nothing to do.
3768 /*************************************************************
3770 ************************************************************/
3771 bool do_move = true;
3772 if (drag_info.first_move) {
3773 if (!drag_info.move_threshold_passed) {
3780 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
3781 const list<RegionView*>& layered_regions = selection->regions.by_layer();
3783 for (list<RegionView*>::const_iterator i = layered_regions.begin(); i != layered_regions.end(); ++i) {
3785 RegionView* rv = (*i);
3786 double ix1, ix2, iy1, iy2;
3787 int32_t temp_pointer_y_span = pointer_y_span;
3789 if (rv->region()->locked()) {
3793 /* get item BBox, which will be relative to parent. so we have
3794 to query on a child, then convert to world coordinates using
3798 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3799 rv->get_canvas_frame()->i2w (ix1, iy1);
3801 cerr << "adjust y from " << iy1 << " using "
3802 << vertical_adjustment.get_value() << " - "
3803 << canvas_timebars_vsize
3806 iy1 += get_trackview_group_vertical_offset ();;
3808 if (drag_info.first_move) {
3810 // hide any dependent views
3812 rv->get_time_axis_view().hide_dependent_views (*rv);
3815 reparent to a non scrolling group so that we can keep the
3816 region selection above all time axis views.
3817 reparenting means we have to move the rv as the two
3818 parent groups have different coordinates.
3821 rv->get_canvas_group()->property_y() = iy1 - 1;
3822 rv->get_canvas_group()->reparent(*_region_motion_group);
3824 rv->fake_set_opaque (true);
3827 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3828 RouteTimeAxisView* canvas_rtv = dynamic_cast<RouteTimeAxisView*>(tvp2);
3829 RouteTimeAxisView* temp_rtv;
3831 if ((pointer_y_span != 0) && !clamp_y_axis) {
3834 for (j = height_list.begin(); j!= height_list.end(); j++) {
3835 if (x == canvas_rtv->order) {
3836 /* we found the track the region is on */
3837 if (x != original_pointer_order) {
3838 /*this isn't from the same track we're dragging from */
3839 temp_pointer_y_span = canvas_pointer_y_span;
3841 while (temp_pointer_y_span > 0) {
3842 /* we're moving up canvas-wise,
3843 so we need to find the next track height
3845 if (j != height_list.begin()) {
3848 if (x != original_pointer_order) {
3849 /* we're not from the dragged track, so ignore hidden tracks. */
3851 temp_pointer_y_span++;
3855 temp_pointer_y_span--;
3858 while (temp_pointer_y_span < 0) {
3860 if (x != original_pointer_order) {
3862 temp_pointer_y_span--;
3866 if (j != height_list.end()) {
3869 temp_pointer_y_span++;
3871 /* find out where we'll be when we move and set height accordingly */
3873 tvp2 = trackview_by_y_position (iy1 + y_delta);
3874 temp_rtv = dynamic_cast<RouteTimeAxisView*>(tvp2);
3875 rv->set_height (temp_rtv->current_height());
3877 /* if you un-comment the following, the region colours will follow the track colours whilst dragging,
3878 personally, i think this can confuse things, but never mind.
3881 //const GdkColor& col (temp_rtv->view->get_region_color());
3882 //rv->set_color (const_cast<GdkColor&>(col));
3889 /* prevent the regionview from being moved to before
3890 the zero position on the canvas.
3895 if (-x_delta > ix1) {
3898 } else if ((x_delta > 0) && (rv->region()->last_frame() > max_frames - x_delta)) {
3899 x_delta = max_frames - rv->region()->last_frame();
3902 if (drag_info.brushing) {
3903 mouse_brush_insert_region (rv, pending_region_position);
3905 rv->move (x_delta, y_delta);
3908 } /* foreach region */
3912 if (drag_info.first_move && drag_info.move_threshold_passed) {
3913 cursor_group->raise_to_top();
3914 drag_info.first_move = false;
3917 if (x_delta != 0 && !drag_info.brushing) {
3918 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3923 Editor::region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3925 bool nocommit = true;
3926 vector<RegionView*> copies;
3927 RouteTimeAxisView* source_tv;
3928 boost::shared_ptr<Diskstream> ds;
3929 boost::shared_ptr<Playlist> from_playlist;
3930 vector<RegionView*> new_selection;
3931 typedef set<boost::shared_ptr<Playlist> > PlaylistSet;
3932 PlaylistSet modified_playlists;
3933 PlaylistSet frozen_playlists;
3934 list <sigc::connection> modified_playlist_connections;
3935 pair<PlaylistSet::iterator,bool> insert_result, frozen_insert_result;
3937 /* first_move is set to false if the regionview has been moved in the
3941 if (drag_info.first_move) {
3948 if (Config->get_edit_mode() == Splice && !pre_drag_region_selection.empty()) {
3949 selection->set (pre_drag_region_selection);
3950 pre_drag_region_selection.clear ();
3953 if (drag_info.brushing) {
3954 /* all changes were made during motion event handlers */
3956 if (drag_info.copy) {
3957 for (list<RegionView*>::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
3958 copies.push_back (*i);
3967 /* reverse this here so that we have the correct logic to finalize
3971 if (Config->get_edit_mode() == Lock && !drag_info.copy) {
3972 drag_info.x_constrained = !drag_info.x_constrained;
3975 if (drag_info.copy) {
3976 if (drag_info.x_constrained) {
3977 op_string = _("fixed time region copy");
3979 op_string = _("region copy");
3982 if (drag_info.x_constrained) {
3983 op_string = _("fixed time region drag");
3985 op_string = _("region drag");
3989 begin_reversible_command (op_string);
3991 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ) {
3993 RegionView* rv = (*i);
3994 double ix1, ix2, iy1, iy2;
3995 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3996 rv->get_canvas_frame()->i2w (ix1, iy1);
3997 iy1 += vertical_adjustment.get_value() - canvas_timebars_vsize;
3999 TimeAxisView* dest_tv = trackview_by_y_position (iy1);
4000 RouteTimeAxisView* dest_rtv = dynamic_cast<RouteTimeAxisView*>(dest_tv);
4002 bool changed_tracks, changed_position;
4005 if (rv->region()->locked()) {
4010 /* adjust for track speed */
4014 if (dest_rtv && dest_rtv->get_diskstream()) {
4015 speed = dest_rtv->get_diskstream()->speed();
4018 changed_position = (drag_info.last_frame_position != (nframes64_t) (rv->region()->position()/speed));
4019 changed_tracks = (dest_tv != &rv->get_time_axis_view());
4021 if (changed_position && !drag_info.x_constrained) {
4022 _master_group->w2i(ix1, iy1);
4023 where = (nframes64_t) (unit_to_frame (ix1) * speed);
4025 where = rv->region()->position();
4028 boost::shared_ptr<Region> new_region;
4031 if (drag_info.copy) {
4032 /* we already made a copy */
4033 new_region = rv->region();
4035 /* undo the previous hide_dependent_views so that xfades don't
4036 disappear on copying regions
4039 //rv->get_time_axis_view().reveal_dependent_views (*rv);
4041 } else if (changed_tracks && dest_rtv->playlist()) {
4042 new_region = RegionFactory::create (rv->region());
4045 if (changed_tracks || drag_info.copy) {
4047 boost::shared_ptr<Playlist> to_playlist = dest_rtv->playlist();
4053 latest_regionviews.clear ();
4055 sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
4057 insert_result = modified_playlists.insert (to_playlist);
4058 if (insert_result.second) {
4059 session->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
4062 to_playlist->add_region (new_region, where);
4066 if (!latest_regionviews.empty()) {
4067 // XXX why just the first one ? we only expect one
4068 // commented out in nick_m's canvas reworking. is that intended?
4069 //dest_atv->reveal_dependent_views (*latest_regionviews.front());
4070 new_selection.push_back (latest_regionviews.front());
4075 motion on the same track. plonk the previously reparented region
4076 back to its original canvas group (its streamview).
4077 No need to do anything for copies as they are fake regions which will be deleted.
4080 rv->get_canvas_group()->reparent (*dest_rtv->view()->canvas_item());
4081 rv->get_canvas_group()->property_y() = 0;
4083 /* just change the model */
4085 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
4087 insert_result = modified_playlists.insert (playlist);
4088 if (insert_result.second) {
4089 session->add_command (new MementoCommand<Playlist>(*playlist, &playlist->get_state(), 0));
4091 /* freeze to avoid lots of relayering in the case of a multi-region drag */
4092 frozen_insert_result = frozen_playlists.insert(playlist);
4093 if (frozen_insert_result.second) {
4097 rv->region()->set_position (where, (void*) this);
4100 if (changed_tracks && !drag_info.copy) {
4102 /* get the playlist where this drag started. we can't use rv->region()->playlist()
4103 because we may have copied the region and it has not been attached to a playlist.
4106 assert ((source_tv = dynamic_cast<RouteTimeAxisView*> (&rv->get_time_axis_view())));
4107 assert ((ds = source_tv->get_diskstream()));
4108 assert ((from_playlist = ds->playlist()));
4110 /* moved to a different audio track, without copying */
4112 /* the region that used to be in the old playlist is not
4113 moved to the new one - we use a copy of it. as a result,
4114 any existing editor for the region should no longer be
4118 rv->hide_region_editor();
4119 rv->fake_set_opaque (false);
4121 /* remove the region from the old playlist */
4123 insert_result = modified_playlists.insert (from_playlist);
4124 if (insert_result.second) {
4125 session->add_command (new MementoCommand<Playlist>(*from_playlist, &from_playlist->get_state(), 0));
4128 from_playlist->remove_region ((rv->region()));
4130 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
4131 was selected in all of them, then removing it from a playlist will have removed all
4132 trace of it from the selection (i.e. there were N regions selected, we removed 1,
4133 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
4134 corresponding regionview, and the selection is now empty).
4136 this could have invalidated any and all iterators into the region selection.
4138 the heuristic we use here is: if the region selection is empty, break out of the loop
4139 here. if the region selection is not empty, then restart the loop because we know that
4140 we must have removed at least the region(view) we've just been working on as well as any
4141 that we processed on previous iterations.
4143 EXCEPT .... if we are doing a copy drag, then the selection hasn't been modified and
4144 we can just iterate.
4147 if (selection->regions.empty()) {
4150 i = selection->regions.by_layer().begin();
4157 if (drag_info.copy) {
4158 copies.push_back (rv);
4162 if (new_selection.empty()) {
4163 if (drag_info.copy) {
4164 /* the region(view)s that are selected and being dragged around
4165 are copies and do not belong to any track. remove them
4166 from the selection right here.
4168 selection->clear_regions();
4171 /* this will clear any existing selection that would have been
4172 cleared in the other clause above
4174 selection->set (new_selection);
4177 for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
4183 for (set<boost::shared_ptr<Playlist> >::iterator p = modified_playlists.begin(); p != modified_playlists.end(); ++p) {
4184 session->add_command (new MementoCommand<Playlist>(*(*p), 0, &(*p)->get_state()));
4186 commit_reversible_command ();
4189 for (vector<RegionView*>::iterator x = copies.begin(); x != copies.end(); ++x) {
4196 Editor::create_region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
4198 if (drag_info.move_threshold_passed) {
4199 if (drag_info.first_move) {
4200 // TODO: create region-create-drag region view here
4201 drag_info.first_move = false;
4204 // TODO: resize region-create-drag region view here
4209 Editor::create_region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
4211 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (drag_info.dest_trackview);
4215 const boost::shared_ptr<MidiDiskstream> diskstream =
4216 boost::dynamic_pointer_cast<MidiDiskstream>(mtv->view()->trackview().track()->diskstream());
4219 warning << "Cannot create non-MIDI region" << endl;
4223 if (drag_info.first_move) {
4224 begin_reversible_command (_("create region"));
4225 XMLNode &before = mtv->playlist()->get_state();
4227 nframes64_t start = drag_info.grab_frame;
4228 snap_to (start, -1);
4229 const Meter& m = session->tempo_map().meter_at(start);
4230 const Tempo& t = session->tempo_map().tempo_at(start);
4231 double length = floor (m.frames_per_bar(t, session->frame_rate()));
4233 boost::shared_ptr<Source> src = session->create_midi_source_for_session(*diskstream.get());
4235 mtv->playlist()->add_region (boost::dynamic_pointer_cast<MidiRegion>
4236 (RegionFactory::create(src, 0, (nframes_t) length,
4237 PBD::basename_nosuffix(src->name()))), start);
4238 XMLNode &after = mtv->playlist()->get_state();
4239 session->add_command(new MementoCommand<Playlist>(*mtv->playlist().get(), &before, &after));
4240 commit_reversible_command();
4243 create_region_drag_motion_callback (item, event);
4244 // TODO: create region-create-drag region here
4249 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
4251 /* Either add to or set the set the region selection, unless
4252 this is an alignment click (control used)
4255 if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
4256 TimeAxisView* tv = &rv.get_time_axis_view();
4257 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
4259 if (rtv && rtv->is_track()) {
4260 speed = rtv->get_diskstream()->speed();
4263 nframes64_t where = get_preferred_edit_position();
4267 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
4269 align_region (rv.region(), SyncPoint, (nframes64_t) (where * speed));
4271 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
4273 align_region (rv.region(), End, (nframes64_t) (where * speed));
4277 align_region (rv.region(), Start, (nframes64_t) (where * speed));
4284 Editor::show_verbose_time_cursor (nframes64_t frame, double offset, double xpos, double ypos)
4290 nframes64_t frame_rate;
4299 if (Profile->get_sae() || Profile->get_small_screen()) {
4300 m = ARDOUR_UI::instance()->primary_clock.mode();
4302 m = ARDOUR_UI::instance()->secondary_clock.mode();
4306 case AudioClock::BBT:
4307 session->bbt_time (frame, bbt);
4308 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, bbt.bars, bbt.beats, bbt.ticks);
4311 case AudioClock::SMPTE:
4312 session->smpte_time (frame, smpte);
4313 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
4316 case AudioClock::MinSec:
4317 /* XXX this is copied from show_verbose_duration_cursor() */
4318 frame_rate = session->frame_rate();
4319 hours = frame / (frame_rate * 3600);
4320 frame = frame % (frame_rate * 3600);
4321 mins = frame / (frame_rate * 60);
4322 frame = frame % (frame_rate * 60);
4323 secs = (float) frame / (float) frame_rate;
4324 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
4328 snprintf (buf, sizeof(buf), "%" PRIi64, frame);
4332 if (xpos >= 0 && ypos >=0) {
4333 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
4336 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset - horizontal_adjustment.get_value(), drag_info.current_pointer_y + offset - vertical_adjustment.get_value() + canvas_timebars_vsize);
4338 show_verbose_canvas_cursor ();
4342 Editor::show_verbose_duration_cursor (nframes64_t start, nframes64_t end, double offset, double xpos, double ypos)
4349 nframes64_t distance, frame_rate;
4351 Meter meter_at_start(session->tempo_map().meter_at(start));
4359 if (Profile->get_sae() || Profile->get_small_screen()) {
4360 m = ARDOUR_UI::instance()->primary_clock.mode ();
4362 m = ARDOUR_UI::instance()->secondary_clock.mode ();
4366 case AudioClock::BBT:
4367 session->bbt_time (start, sbbt);
4368 session->bbt_time (end, ebbt);
4371 /* XXX this computation won't work well if the
4372 user makes a selection that spans any meter changes.
4375 ebbt.bars -= sbbt.bars;
4376 if (ebbt.beats >= sbbt.beats) {
4377 ebbt.beats -= sbbt.beats;
4380 ebbt.beats = int(meter_at_start.beats_per_bar()) + ebbt.beats - sbbt.beats;
4382 if (ebbt.ticks >= sbbt.ticks) {
4383 ebbt.ticks -= sbbt.ticks;
4386 ebbt.ticks = int(Meter::ticks_per_beat) + ebbt.ticks - sbbt.ticks;
4389 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, ebbt.bars, ebbt.beats, ebbt.ticks);
4392 case AudioClock::SMPTE:
4393 session->smpte_duration (end - start, smpte);
4394 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
4397 case AudioClock::MinSec:
4398 /* XXX this stuff should be elsewhere.. */
4399 distance = end - start;
4400 frame_rate = session->frame_rate();
4401 hours = distance / (frame_rate * 3600);
4402 distance = distance % (frame_rate * 3600);
4403 mins = distance / (frame_rate * 60);
4404 distance = distance % (frame_rate * 60);
4405 secs = (float) distance / (float) frame_rate;
4406 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
4410 snprintf (buf, sizeof(buf), "%" PRIi64, end - start);
4414 if (xpos >= 0 && ypos >=0) {
4415 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
4418 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
4421 show_verbose_canvas_cursor ();
4425 Editor::collect_new_region_view (RegionView* rv)
4427 latest_regionviews.push_back (rv);
4431 Editor::collect_and_select_new_region_view (RegionView* rv)
4434 latest_regionviews.push_back (rv);
4438 Editor::start_selection_grab (ArdourCanvas::Item* item, GdkEvent* event)
4440 if (clicked_regionview == 0) {
4444 /* lets try to create new Region for the selection */
4446 vector<boost::shared_ptr<Region> > new_regions;
4447 create_region_from_selection (new_regions);
4449 if (new_regions.empty()) {
4453 /* XXX fix me one day to use all new regions */
4455 boost::shared_ptr<Region> region (new_regions.front());
4457 /* add it to the current stream/playlist.
4459 tricky: the streamview for the track will add a new regionview. we will
4460 catch the signal it sends when it creates the regionview to
4461 set the regionview we want to then drag.
4464 latest_regionviews.clear();
4465 sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
4467 /* A selection grab currently creates two undo/redo operations, one for
4468 creating the new region and another for moving it.
4471 begin_reversible_command (_("selection grab"));
4473 boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
4475 XMLNode *before = &(playlist->get_state());
4476 clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
4477 XMLNode *after = &(playlist->get_state());
4478 session->add_command(new MementoCommand<Playlist>(*playlist, before, after));
4480 commit_reversible_command ();
4484 if (latest_regionviews.empty()) {
4485 /* something went wrong */
4489 /* we need to deselect all other regionviews, and select this one
4490 i'm ignoring undo stuff, because the region creation will take care of it
4492 selection->set (latest_regionviews);
4494 drag_info.item = latest_regionviews.front()->get_canvas_group();
4495 drag_info.data = latest_regionviews.front();
4496 drag_info.motion_callback = &Editor::region_drag_motion_callback;
4497 drag_info.finished_callback = &Editor::region_drag_finished_callback;
4501 drag_info.source_trackview = clicked_routeview;
4502 drag_info.dest_trackview = drag_info.source_trackview;
4503 drag_info.last_frame_position = latest_regionviews.front()->region()->position();
4504 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
4506 show_verbose_time_cursor (drag_info.last_frame_position, 10);
4510 Editor::cancel_selection ()
4512 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4513 (*i)->hide_selection ();
4515 selection->clear ();
4516 clicked_selection = 0;
4520 Editor::start_selection_op (ArdourCanvas::Item* item, GdkEvent* event, SelectionOp op)
4522 nframes64_t start = 0;
4523 nframes64_t end = 0;
4529 drag_info.item = item;
4530 drag_info.motion_callback = &Editor::drag_selection;
4531 drag_info.finished_callback = &Editor::end_selection_op;
4536 case CreateSelection:
4537 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
4538 drag_info.copy = true;
4540 drag_info.copy = false;
4542 start_grab (event, selector_cursor);
4545 case SelectionStartTrim:
4546 if (clicked_axisview) {
4547 clicked_axisview->order_selection_trims (item, true);
4549 start_grab (event, trimmer_cursor);
4550 start = selection->time[clicked_selection].start;
4551 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
4554 case SelectionEndTrim:
4555 if (clicked_axisview) {
4556 clicked_axisview->order_selection_trims (item, false);
4558 start_grab (event, trimmer_cursor);
4559 end = selection->time[clicked_selection].end;
4560 drag_info.pointer_frame_offset = drag_info.grab_frame - end;
4564 start = selection->time[clicked_selection].start;
4566 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
4570 if (selection_op == SelectionMove) {
4571 show_verbose_time_cursor(start, 10);
4573 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4578 Editor::drag_selection (ArdourCanvas::Item* item, GdkEvent* event)
4580 nframes64_t start = 0;
4581 nframes64_t end = 0;
4583 nframes64_t pending_position;
4585 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
4586 pending_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
4588 pending_position = 0;
4591 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4592 snap_to (pending_position);
4595 /* only alter selection if the current frame is
4596 different from the last frame position (adjusted)
4599 if (pending_position == drag_info.last_pointer_frame) return;
4601 switch (selection_op) {
4602 case CreateSelection:
4604 if (drag_info.first_move) {
4605 snap_to (drag_info.grab_frame);
4608 if (pending_position < drag_info.grab_frame) {
4609 start = pending_position;
4610 end = drag_info.grab_frame;
4612 end = pending_position;
4613 start = drag_info.grab_frame;
4616 /* first drag: Either add to the selection
4617 or create a new selection->
4620 if (drag_info.first_move) {
4622 begin_reversible_command (_("range selection"));
4624 if (drag_info.copy) {
4625 /* adding to the selection */
4626 clicked_selection = selection->add (start, end);
4627 drag_info.copy = false;
4629 /* new selection-> */
4630 clicked_selection = selection->set (clicked_axisview, start, end);
4635 case SelectionStartTrim:
4637 if (drag_info.first_move) {
4638 begin_reversible_command (_("trim selection start"));
4641 start = selection->time[clicked_selection].start;
4642 end = selection->time[clicked_selection].end;
4644 if (pending_position > end) {
4647 start = pending_position;
4651 case SelectionEndTrim:
4653 if (drag_info.first_move) {
4654 begin_reversible_command (_("trim selection end"));
4657 start = selection->time[clicked_selection].start;
4658 end = selection->time[clicked_selection].end;
4660 if (pending_position < start) {
4663 end = pending_position;
4670 if (drag_info.first_move) {
4671 begin_reversible_command (_("move selection"));
4674 start = selection->time[clicked_selection].start;
4675 end = selection->time[clicked_selection].end;
4677 length = end - start;
4679 start = pending_position;
4682 end = start + length;
4687 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
4688 start_canvas_autoscroll (1, 0);
4692 selection->replace (clicked_selection, start, end);
4695 drag_info.last_pointer_frame = pending_position;
4696 drag_info.first_move = false;
4698 if (selection_op == SelectionMove) {
4699 show_verbose_time_cursor(start, 10);
4701 show_verbose_time_cursor(pending_position, 10);
4706 Editor::end_selection_op (ArdourCanvas::Item* item, GdkEvent* event)
4708 if (!drag_info.first_move) {
4709 drag_selection (item, event);
4710 /* XXX this is not object-oriented programming at all. ick */
4711 if (selection->time.consolidate()) {
4712 selection->TimeChanged ();
4714 commit_reversible_command ();
4716 /* just a click, no pointer movement.*/
4718 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
4720 selection->clear_time();
4725 /* XXX what happens if its a music selection? */
4726 session->set_audio_range (selection->time);
4727 stop_canvas_autoscroll ();
4731 Editor::start_trim (ArdourCanvas::Item* item, GdkEvent* event)
4734 TimeAxisView* tvp = clicked_axisview;
4735 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4737 if (tv && tv->is_track()) {
4738 speed = tv->get_diskstream()->speed();
4741 nframes64_t region_start = (nframes64_t) (clicked_regionview->region()->position() / speed);
4742 nframes64_t region_end = (nframes64_t) (clicked_regionview->region()->last_frame() / speed);
4743 nframes64_t region_length = (nframes64_t) (clicked_regionview->region()->length() / speed);
4745 //drag_info.item = clicked_regionview->get_name_highlight();
4746 drag_info.item = item;
4747 drag_info.motion_callback = &Editor::trim_motion_callback;
4748 drag_info.finished_callback = &Editor::trim_finished_callback;
4750 start_grab (event, trimmer_cursor);
4752 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
4753 trim_op = ContentsTrim;
4755 /* These will get overridden for a point trim.*/
4756 if (drag_info.current_pointer_frame < (region_start + region_length/2)) {
4757 /* closer to start */
4758 trim_op = StartTrim;
4759 } else if (drag_info.current_pointer_frame > (region_end - region_length/2)) {
4767 show_verbose_time_cursor(region_start, 10);
4770 show_verbose_time_cursor(region_end, 10);
4773 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4779 Editor::trim_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
4781 RegionView* rv = clicked_regionview;
4782 nframes64_t frame_delta = 0;
4783 bool left_direction;
4784 bool obey_snap = !Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier());
4786 /* snap modifier works differently here..
4787 its' current state has to be passed to the
4788 various trim functions in order to work properly
4792 TimeAxisView* tvp = clicked_axisview;
4793 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4794 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
4796 if (tv && tv->is_track()) {
4797 speed = tv->get_diskstream()->speed();
4800 if (drag_info.last_pointer_frame > drag_info.current_pointer_frame) {
4801 left_direction = true;
4803 left_direction = false;
4807 snap_to (drag_info.current_pointer_frame);
4810 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
4814 if (drag_info.first_move) {
4820 trim_type = "Region start trim";
4823 trim_type = "Region end trim";
4826 trim_type = "Region content trim";
4830 begin_reversible_command (trim_type);
4832 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4833 (*i)->fake_set_opaque(false);
4834 (*i)->region()->freeze ();
4836 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
4838 arv->temporarily_hide_envelope ();
4840 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4841 insert_result = motion_frozen_playlists.insert (pl);
4842 if (insert_result.second) {
4843 session->add_command(new MementoCommand<Playlist>(*pl, &pl->get_state(), 0));
4849 if (left_direction) {
4850 frame_delta = (drag_info.last_pointer_frame - drag_info.current_pointer_frame);
4852 frame_delta = (drag_info.current_pointer_frame - drag_info.last_pointer_frame);
4857 if ((left_direction == false) && (drag_info.current_pointer_frame <= rv->region()->first_frame()/speed)) {
4860 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4861 single_start_trim (**i, frame_delta, left_direction, obey_snap);
4867 if ((left_direction == true) && (drag_info.current_pointer_frame > (nframes64_t) (rv->region()->last_frame()/speed))) {
4870 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4871 single_end_trim (**i, frame_delta, left_direction, obey_snap);
4878 bool swap_direction = false;
4880 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
4881 swap_direction = true;
4884 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4885 i != selection->regions.by_layer().end(); ++i)
4887 single_contents_trim (**i, frame_delta, left_direction, swap_direction, obey_snap);
4895 show_verbose_time_cursor((nframes64_t) (rv->region()->position()/speed), 10);
4898 show_verbose_time_cursor((nframes64_t) (rv->region()->last_frame()/speed), 10);
4901 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4905 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4906 drag_info.first_move = false;
4910 Editor::single_contents_trim (RegionView& rv, nframes64_t frame_delta, bool left_direction, bool swap_direction, bool obey_snap)
4912 boost::shared_ptr<Region> region (rv.region());
4914 if (region->locked()) {
4918 nframes64_t new_bound;
4921 TimeAxisView* tvp = clicked_axisview;
4922 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4924 if (tv && tv->is_track()) {
4925 speed = tv->get_diskstream()->speed();
4928 if (left_direction) {
4929 if (swap_direction) {
4930 new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
4932 new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
4935 if (swap_direction) {
4936 new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
4938 new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
4943 snap_to (new_bound);
4945 region->trim_start ((nframes64_t) (new_bound * speed), this);
4946 rv.region_changed (StartChanged);
4950 Editor::single_start_trim (RegionView& rv, nframes64_t frame_delta, bool left_direction, bool obey_snap)
4952 boost::shared_ptr<Region> region (rv.region());
4954 if (region->locked()) {
4958 nframes64_t new_bound;
4961 TimeAxisView* tvp = clicked_axisview;
4962 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4964 if (tv && tv->is_track()) {
4965 speed = tv->get_diskstream()->speed();
4968 if (left_direction) {
4969 new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
4971 new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
4975 snap_to (new_bound, (left_direction ? 0 : 1));
4978 region->trim_front ((nframes64_t) (new_bound * speed), this);
4980 rv.region_changed (Change (LengthChanged|PositionChanged|StartChanged));
4984 Editor::single_end_trim (RegionView& rv, nframes64_t frame_delta, bool left_direction, bool obey_snap)
4986 boost::shared_ptr<Region> region (rv.region());
4988 if (region->locked()) {
4992 nframes64_t new_bound;
4995 TimeAxisView* tvp = clicked_axisview;
4996 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4998 if (tv && tv->is_track()) {
4999 speed = tv->get_diskstream()->speed();
5002 if (left_direction) {
5003 new_bound = (nframes64_t) ((region->last_frame() + 1)/speed) - frame_delta;
5005 new_bound = (nframes64_t) ((region->last_frame() + 1)/speed) + frame_delta;
5009 snap_to (new_bound);
5011 region->trim_end ((nframes64_t) (new_bound * speed), this);
5012 rv.region_changed (LengthChanged);
5016 Editor::trim_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
5018 if (!drag_info.first_move) {
5019 trim_motion_callback (item, event);
5021 if (!selection->selected (clicked_regionview)) {
5022 thaw_region_after_trim (*clicked_regionview);
5025 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
5026 i != selection->regions.by_layer().end(); ++i)
5028 thaw_region_after_trim (**i);
5029 (*i)->fake_set_opaque (true);
5033 for (set<boost::shared_ptr<Playlist> >::iterator p = motion_frozen_playlists.begin(); p != motion_frozen_playlists.end(); ++p) {
5035 session->add_command (new MementoCommand<Playlist>(*(*p).get(), 0, &(*p)->get_state()));
5038 motion_frozen_playlists.clear ();
5040 commit_reversible_command();
5042 /* no mouse movement */
5048 Editor::point_trim (GdkEvent* event)
5050 RegionView* rv = clicked_regionview;
5051 nframes64_t new_bound = drag_info.current_pointer_frame;
5053 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
5054 snap_to (new_bound);
5057 /* Choose action dependant on which button was pressed */
5058 switch (event->button.button) {
5060 trim_op = StartTrim;
5061 begin_reversible_command (_("Start point trim"));
5063 if (selection->selected (rv)) {
5065 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
5066 i != selection->regions.by_layer().end(); ++i)
5068 if (!(*i)->region()->locked()) {
5069 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
5070 XMLNode &before = pl->get_state();
5071 (*i)->region()->trim_front (new_bound, this);
5072 XMLNode &after = pl->get_state();
5073 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
5079 if (!rv->region()->locked()) {
5080 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
5081 XMLNode &before = pl->get_state();
5082 rv->region()->trim_front (new_bound, this);
5083 XMLNode &after = pl->get_state();
5084 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
5088 commit_reversible_command();
5093 begin_reversible_command (_("End point trim"));
5095 if (selection->selected (rv)) {
5097 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
5099 if (!(*i)->region()->locked()) {
5100 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
5101 XMLNode &before = pl->get_state();
5102 (*i)->region()->trim_end (new_bound, this);
5103 XMLNode &after = pl->get_state();
5104 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
5110 if (!rv->region()->locked()) {
5111 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
5112 XMLNode &before = pl->get_state();
5113 rv->region()->trim_end (new_bound, this);
5114 XMLNode &after = pl->get_state();
5115 session->add_command (new MementoCommand<Playlist>(*pl.get(), &before, &after));
5119 commit_reversible_command();
5128 Editor::thaw_region_after_trim (RegionView& rv)
5130 boost::shared_ptr<Region> region (rv.region());
5132 if (region->locked()) {
5136 region->thaw (_("trimmed region"));
5137 XMLNode &after = region->playlist()->get_state();
5138 session->add_command (new MementoCommand<Playlist>(*(region->playlist()), 0, &after));
5140 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(&rv);
5142 arv->unhide_envelope ();
5146 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* event)
5151 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
5152 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
5156 Location* location = find_location_from_marker (marker, is_start);
5157 location->set_hidden (true, this);
5162 Editor::start_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event, RangeMarkerOp op)
5168 drag_info.item = item;
5169 drag_info.motion_callback = &Editor::drag_range_markerbar_op;
5170 drag_info.finished_callback = &Editor::end_range_markerbar_op;
5172 range_marker_op = op;
5174 if (!temp_location) {
5175 temp_location = new Location;
5179 case CreateRangeMarker:
5180 case CreateTransportMarker:
5181 case CreateCDMarker:
5183 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
5184 drag_info.copy = true;
5186 drag_info.copy = false;
5188 start_grab (event, selector_cursor);
5192 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
5197 Editor::drag_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
5199 nframes64_t start = 0;
5200 nframes64_t end = 0;
5201 ArdourCanvas::SimpleRect *crect;
5203 switch (range_marker_op) {
5204 case CreateRangeMarker:
5205 crect = range_bar_drag_rect;
5207 case CreateTransportMarker:
5208 crect = transport_bar_drag_rect;
5210 case CreateCDMarker:
5211 crect = cd_marker_bar_drag_rect;
5214 cerr << "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()" << endl;
5219 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
5220 snap_to (drag_info.current_pointer_frame);
5223 /* only alter selection if the current frame is
5224 different from the last frame position.
5227 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
5229 switch (range_marker_op) {
5230 case CreateRangeMarker:
5231 case CreateTransportMarker:
5232 case CreateCDMarker:
5233 if (drag_info.first_move) {
5234 snap_to (drag_info.grab_frame);
5237 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
5238 start = drag_info.current_pointer_frame;
5239 end = drag_info.grab_frame;
5241 end = drag_info.current_pointer_frame;
5242 start = drag_info.grab_frame;
5245 /* first drag: Either add to the selection
5246 or create a new selection.
5249 if (drag_info.first_move) {
5251 temp_location->set (start, end);
5255 update_marker_drag_item (temp_location);
5256 range_marker_drag_rect->show();
5257 //range_marker_drag_rect->raise_to_top();
5263 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
5264 start_canvas_autoscroll (1, 0);
5268 temp_location->set (start, end);
5270 double x1 = frame_to_pixel (start);
5271 double x2 = frame_to_pixel (end);
5272 crect->property_x1() = x1;
5273 crect->property_x2() = x2;
5275 update_marker_drag_item (temp_location);
5278 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
5279 drag_info.first_move = false;
5281 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
5286 Editor::end_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
5288 Location * newloc = 0;
5292 if (!drag_info.first_move) {
5293 drag_range_markerbar_op (item, event);
5295 switch (range_marker_op) {
5296 case CreateRangeMarker:
5297 case CreateCDMarker:
5299 begin_reversible_command (_("new range marker"));
5300 XMLNode &before = session->locations()->get_state();
5301 session->locations()->next_available_name(rangename,"unnamed");
5302 if (range_marker_op == CreateCDMarker) {
5303 flags = Location::IsRangeMarker|Location::IsCDMarker;
5304 cd_marker_bar_drag_rect->hide();
5307 flags = Location::IsRangeMarker;
5308 range_bar_drag_rect->hide();
5310 newloc = new Location(temp_location->start(), temp_location->end(), rangename, (Location::Flags) flags);
5311 session->locations()->add (newloc, true);
5312 XMLNode &after = session->locations()->get_state();
5313 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
5314 commit_reversible_command ();
5316 range_marker_drag_rect->hide();
5320 case CreateTransportMarker:
5321 // popup menu to pick loop or punch
5322 new_transport_marker_context_menu (&event->button, item);
5327 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
5329 if (Keyboard::no_modifier_keys_pressed (&event->button) && range_marker_op != CreateCDMarker) {
5334 start = session->locations()->first_mark_before (drag_info.grab_frame);
5335 end = session->locations()->first_mark_after (drag_info.grab_frame);
5337 if (end == max_frames) {
5338 end = session->current_end_frame ();
5342 start = session->current_start_frame ();
5345 switch (mouse_mode) {
5347 /* find the two markers on either side and then make the selection from it */
5348 select_all_within (start, end, 0.0f, FLT_MAX, track_views, Selection::Set);
5352 /* find the two markers on either side of the click and make the range out of it */
5353 selection->set (0, start, end);
5362 stop_canvas_autoscroll ();
5368 Editor::start_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
5370 drag_info.item = item;
5371 drag_info.motion_callback = &Editor::drag_mouse_zoom;
5372 drag_info.finished_callback = &Editor::end_mouse_zoom;
5374 start_grab (event, zoom_cursor);
5376 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5380 Editor::drag_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
5385 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
5386 snap_to (drag_info.current_pointer_frame);
5388 if (drag_info.first_move) {
5389 snap_to (drag_info.grab_frame);
5393 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
5395 /* base start and end on initial click position */
5396 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
5397 start = drag_info.current_pointer_frame;
5398 end = drag_info.grab_frame;
5400 end = drag_info.current_pointer_frame;
5401 start = drag_info.grab_frame;
5406 if (drag_info.first_move) {
5408 zoom_rect->raise_to_top();
5411 reposition_zoom_rect(start, end);
5413 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
5414 drag_info.first_move = false;
5416 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5421 Editor::end_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
5423 if (!drag_info.first_move) {
5424 drag_mouse_zoom (item, event);
5426 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
5427 temporal_zoom_by_frame (drag_info.grab_frame, drag_info.last_pointer_frame, "mouse zoom");
5429 temporal_zoom_by_frame (drag_info.last_pointer_frame, drag_info.grab_frame, "mouse zoom");
5432 temporal_zoom_to_frame (false, drag_info.grab_frame);
5434 temporal_zoom_step (false);
5435 center_screen (drag_info.grab_frame);
5443 Editor::reposition_zoom_rect (nframes64_t start, nframes64_t end)
5445 double x1 = frame_to_pixel (start);
5446 double x2 = frame_to_pixel (end);
5447 double y2 = full_canvas_height - 1.0;
5449 zoom_rect->property_x1() = x1;
5450 zoom_rect->property_y1() = 1.0;
5451 zoom_rect->property_x2() = x2;
5452 zoom_rect->property_y2() = y2;
5456 Editor::start_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
5458 drag_info.item = item;
5459 drag_info.motion_callback = &Editor::drag_rubberband_select;
5460 drag_info.finished_callback = &Editor::end_rubberband_select;
5462 start_grab (event, cross_hair_cursor);
5464 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5468 Editor::drag_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
5475 /* use a bigger drag threshold than the default */
5477 if (abs ((int) (drag_info.current_pointer_frame - drag_info.grab_frame)) < 8) {
5481 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier()) && Config->get_rubberbanding_snaps_to_grid()) {
5482 if (drag_info.first_move) {
5483 snap_to (drag_info.grab_frame);
5485 snap_to (drag_info.current_pointer_frame);
5488 /* base start and end on initial click position */
5490 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
5491 start = drag_info.current_pointer_frame;
5492 end = drag_info.grab_frame;
5494 end = drag_info.current_pointer_frame;
5495 start = drag_info.grab_frame;
5498 if (drag_info.current_pointer_y < drag_info.grab_y) {
5499 y1 = drag_info.current_pointer_y;
5500 y2 = drag_info.grab_y;
5502 y2 = drag_info.current_pointer_y;
5503 y1 = drag_info.grab_y;
5507 if (start != end || y1 != y2) {
5509 double x1 = frame_to_pixel (start);
5510 double x2 = frame_to_pixel (end);
5512 rubberband_rect->property_x1() = x1;
5513 rubberband_rect->property_y1() = y1;
5514 rubberband_rect->property_x2() = x2;
5515 rubberband_rect->property_y2() = y2;
5517 rubberband_rect->show();
5518 rubberband_rect->raise_to_top();
5520 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
5521 drag_info.first_move = false;
5523 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5528 Editor::end_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
5530 if (!drag_info.first_move) {
5532 drag_rubberband_select (item, event);
5535 if (drag_info.current_pointer_y < drag_info.grab_y) {
5536 y1 = drag_info.current_pointer_y;
5537 y2 = drag_info.grab_y;
5539 y2 = drag_info.current_pointer_y;
5540 y1 = drag_info.grab_y;
5544 Selection::Operation op = Keyboard::selection_type (event->button.state);
5547 begin_reversible_command (_("rubberband selection"));
5549 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
5550 commit = select_all_within (drag_info.grab_frame, drag_info.last_pointer_frame, y1, y2, track_views, op);
5552 commit = select_all_within (drag_info.last_pointer_frame, drag_info.grab_frame, y1, y2, track_views, op);
5556 commit_reversible_command ();
5560 selection->clear_tracks();
5561 selection->clear_regions();
5562 selection->clear_points ();
5563 selection->clear_lines ();
5566 rubberband_rect->hide();
5571 Editor::mouse_rename_region (ArdourCanvas::Item* item, GdkEvent* event)
5573 using namespace Gtkmm2ext;
5575 ArdourPrompter prompter (false);
5577 prompter.set_prompt (_("Name for region:"));
5578 prompter.set_initial_text (clicked_regionview->region()->name());
5579 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
5580 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
5581 prompter.show_all ();
5582 switch (prompter.run ()) {
5583 case Gtk::RESPONSE_ACCEPT:
5585 prompter.get_result(str);
5587 clicked_regionview->region()->set_name (str);
5595 Editor::start_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
5597 drag_info.item = item;
5598 drag_info.motion_callback = &Editor::time_fx_motion;
5599 drag_info.finished_callback = &Editor::end_time_fx;
5603 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5607 Editor::time_fx_motion (ArdourCanvas::Item *item, GdkEvent* event)
5609 RegionView* rv = clicked_regionview;
5611 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
5612 snap_to (drag_info.current_pointer_frame);
5615 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
5619 if (drag_info.current_pointer_frame > rv->region()->position()) {
5620 rv->get_time_axis_view().show_timestretch (rv->region()->position(), drag_info.current_pointer_frame);
5623 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
5624 drag_info.first_move = false;
5626 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5630 Editor::end_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
5632 clicked_regionview->get_time_axis_view().hide_timestretch ();
5634 if (drag_info.first_move) {
5638 if (drag_info.last_pointer_frame < clicked_regionview->region()->position()) {
5639 /* backwards drag of the left edge - not usable */
5643 nframes64_t newlen = drag_info.last_pointer_frame - clicked_regionview->region()->position();
5645 float percentage = (double) newlen / (double) clicked_regionview->region()->length();
5647 #ifndef USE_RUBBERBAND
5648 // Soundtouch uses percentage / 100 instead of normal (/ 1)
5649 if (clicked_regionview->region()->data_type() == DataType::AUDIO) {
5650 percentage = (float) ((double) newlen - (double) clicked_regionview->region()->length()) / ((double) newlen) * 100.0f;
5654 begin_reversible_command (_("timestretch"));
5656 // XXX how do timeFX on multiple regions ?
5659 rs.add (clicked_regionview);
5661 if (time_stretch (rs, percentage) == 0) {
5662 session->commit_reversible_command ();
5667 Editor::mouse_brush_insert_region (RegionView* rv, nframes64_t pos)
5669 /* no brushing without a useful snap setting */
5671 switch (snap_mode) {
5673 return; /* can't work because it allows region to be placed anywhere */
5678 switch (snap_type) {
5686 /* don't brush a copy over the original */
5688 if (pos == rv->region()->position()) {
5692 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
5694 if (rtv == 0 || !rtv->is_track()) {
5698 boost::shared_ptr<Playlist> playlist = rtv->playlist();
5699 double speed = rtv->get_diskstream()->speed();
5701 XMLNode &before = playlist->get_state();
5702 playlist->add_region (RegionFactory::create (rv->region()), (nframes64_t) (pos * speed));
5703 XMLNode &after = playlist->get_state();
5704 session->add_command(new MementoCommand<Playlist>(*playlist.get(), &before, &after));
5706 // playlist is frozen, so we have to update manually
5708 playlist->Modified(); /* EMIT SIGNAL */
5712 Editor::track_height_step_timeout ()
5714 if (get_microseconds() - last_track_height_step_timestamp < 250000) {
5715 current_stepping_trackview = 0;