2 Copyright (C) 2000-2001 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
28 #include <pbd/error.h>
29 #include <gtkmm2ext/utils.h>
30 #include <pbd/memento_command.h>
31 #include <pbd/basename.h>
33 #include "ardour_ui.h"
35 #include "time_axis_view.h"
36 #include "audio_time_axis.h"
37 #include "audio_region_view.h"
38 #include "midi_region_view.h"
40 #include "streamview.h"
41 #include "region_gain_line.h"
42 #include "automation_time_axis.h"
43 #include "control_point.h"
46 #include "selection.h"
49 #include "rgb_macros.h"
51 #include <ardour/types.h>
52 #include <ardour/profile.h>
53 #include <ardour/route.h>
54 #include <ardour/audio_track.h>
55 #include <ardour/audio_diskstream.h>
56 #include <ardour/midi_diskstream.h>
57 #include <ardour/playlist.h>
58 #include <ardour/audioplaylist.h>
59 #include <ardour/audioregion.h>
60 #include <ardour/midi_region.h>
61 #include <ardour/dB.h>
62 #include <ardour/utils.h>
63 #include <ardour/region_factory.h>
64 #include <ardour/source_factory.h>
71 using namespace ARDOUR;
75 using namespace Editing;
77 const static double ZERO_GAIN_FRACTION = gain_to_slider_position(dB_to_coefficient(0.0));
80 Editor::mouse_frame (nframes64_t& where, bool& in_track_canvas) const
84 Gdk::ModifierType mask;
85 Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->track_canvas->get_window();
86 Glib::RefPtr<const Gdk::Window> pointer_window;
92 pointer_window = canvas_window->get_pointer (x, y, mask);
94 if (pointer_window == track_canvas->get_bin_window()) {
96 track_canvas->window_to_world (x, y, wx, wy);
97 in_track_canvas = true;
100 in_track_canvas = false;
102 if (pointer_window == time_canvas->get_bin_window()) {
103 time_canvas->window_to_world (x, y, wx, wy);
109 wx += horizontal_adjustment.get_value();
110 wy += vertical_adjustment.get_value();
113 event.type = GDK_BUTTON_RELEASE;
117 where = event_frame (&event, 0, 0);
122 Editor::event_frame (GdkEvent* event, double* pcx, double* pcy) const
136 switch (event->type) {
137 case GDK_BUTTON_RELEASE:
138 case GDK_BUTTON_PRESS:
139 case GDK_2BUTTON_PRESS:
140 case GDK_3BUTTON_PRESS:
141 track_canvas->w2c(event->button.x, event->button.y, *pcx, *pcy);
143 case GDK_MOTION_NOTIFY:
144 track_canvas->w2c(event->motion.x, event->motion.y, *pcx, *pcy);
146 case GDK_ENTER_NOTIFY:
147 case GDK_LEAVE_NOTIFY:
148 track_canvas->w2c(event->crossing.x, event->crossing.y, *pcx, *pcy);
151 case GDK_KEY_RELEASE:
152 // track_canvas->w2c(event->key.x, event->key.y, *pcx, *pcy);
155 warning << string_compose (_("Editor::event_frame() used on unhandled event type %1"), event->type) << endmsg;
159 /* note that pixel_to_frame() never returns less than zero, so even if the pixel
160 position is negative (as can be the case with motion events in particular),
161 the frame location is always positive.
164 return pixel_to_frame (*pcx);
168 Editor::mouse_mode_toggled (MouseMode m)
170 if (ignore_mouse_mode_toggle) {
176 if (mouse_select_button.get_active()) {
182 if (mouse_move_button.get_active()) {
188 if (mouse_gain_button.get_active()) {
194 if (mouse_zoom_button.get_active()) {
200 if (mouse_timefx_button.get_active()) {
206 if (mouse_audition_button.get_active()) {
212 if (mouse_note_button.get_active()) {
223 Editor::which_grabber_cursor ()
225 switch (_edit_point) {
227 return grabber_edit_point_cursor;
230 return grabber_cursor;
236 Editor::set_canvas_cursor ()
238 switch (mouse_mode) {
240 current_canvas_cursor = selector_cursor;
244 current_canvas_cursor = which_grabber_cursor();
248 current_canvas_cursor = cross_hair_cursor;
252 current_canvas_cursor = zoom_cursor;
256 current_canvas_cursor = time_fx_cursor; // just use playhead
260 current_canvas_cursor = speaker_cursor;
264 set_midi_edit_cursor (current_midi_edit_mode());
269 track_canvas->get_window()->set_cursor(*current_canvas_cursor);
274 Editor::set_mouse_mode (MouseMode m, bool force)
276 if (drag_info.item) {
280 if (!force && m == mouse_mode) {
288 if (mouse_mode != MouseRange) {
290 /* in all modes except range, hide the range selection,
291 show the object (region) selection.
294 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
295 (*i)->set_should_show_selection (true);
297 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
298 (*i)->hide_selection ();
304 in range mode,show the range selection.
307 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
308 if ((*i)->get_selected()) {
309 (*i)->show_selection (selection->time);
314 /* XXX the hack of unsetting all other buttons should go
315 away once GTK2 allows us to use regular radio buttons drawn like
316 normal buttons, rather than my silly GroupedButton hack.
319 ignore_mouse_mode_toggle = true;
321 switch (mouse_mode) {
323 mouse_select_button.set_active (true);
327 mouse_move_button.set_active (true);
331 mouse_gain_button.set_active (true);
335 mouse_zoom_button.set_active (true);
339 mouse_timefx_button.set_active (true);
343 mouse_audition_button.set_active (true);
347 mouse_note_button.set_active (true);
348 set_midi_edit_cursor (current_midi_edit_mode());
352 if (mouse_mode == MouseNote)
353 midi_toolbar_frame.show();
355 midi_toolbar_frame.hide();
357 ignore_mouse_mode_toggle = false;
359 set_canvas_cursor ();
363 Editor::step_mouse_mode (bool next)
365 switch (current_mouse_mode()) {
367 if (next) set_mouse_mode (MouseRange);
368 else set_mouse_mode (MouseTimeFX);
372 if (next) set_mouse_mode (MouseZoom);
373 else set_mouse_mode (MouseObject);
377 if (next) set_mouse_mode (MouseGain);
378 else set_mouse_mode (MouseRange);
382 if (next) set_mouse_mode (MouseTimeFX);
383 else set_mouse_mode (MouseZoom);
387 if (next) set_mouse_mode (MouseAudition);
388 else set_mouse_mode (MouseGain);
392 if (next) set_mouse_mode (MouseObject);
393 else set_mouse_mode (MouseTimeFX);
397 if (next) set_mouse_mode (MouseObject);
398 else set_mouse_mode (MouseAudition);
404 Editor::midi_edit_mode_toggled (MidiEditMode m)
406 if (ignore_midi_edit_mode_toggle) {
412 if (midi_tool_pencil_button.get_active())
413 set_midi_edit_mode (m);
417 if (midi_tool_select_button.get_active())
418 set_midi_edit_mode (m);
422 if (midi_tool_resize_button.get_active())
423 set_midi_edit_mode (m);
427 if (midi_tool_erase_button.get_active())
428 set_midi_edit_mode (m);
435 set_midi_edit_cursor(m);
440 Editor::set_midi_edit_mode (MidiEditMode m, bool force)
442 if (drag_info.item) {
446 if (!force && m == midi_edit_mode) {
454 ignore_midi_edit_mode_toggle = true;
456 switch (midi_edit_mode) {
458 midi_tool_pencil_button.set_active (true);
462 midi_tool_select_button.set_active (true);
466 midi_tool_resize_button.set_active (true);
470 midi_tool_erase_button.set_active (true);
474 ignore_midi_edit_mode_toggle = false;
476 set_midi_edit_cursor (current_midi_edit_mode());
479 track_canvas->get_window()->set_cursor(*current_canvas_cursor);
484 Editor::set_midi_edit_cursor (MidiEditMode m)
486 switch (midi_edit_mode) {
488 current_canvas_cursor = midi_pencil_cursor;
492 current_canvas_cursor = midi_select_cursor;
496 current_canvas_cursor = midi_resize_cursor;
500 current_canvas_cursor = midi_erase_cursor;
506 Editor::button_selection (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
508 /* in object/audition/timefx mode, any button press sets
509 the selection if the object can be selected. this is a
510 bit of hack, because we want to avoid this if the
511 mouse operation is a region alignment.
513 note: not dbl-click or triple-click
516 if (((mouse_mode != MouseObject) &&
517 (mouse_mode != MouseAudition || item_type != RegionItem) &&
518 (mouse_mode != MouseTimeFX || item_type != RegionItem) &&
519 (mouse_mode != MouseRange)) ||
521 (event->type != GDK_BUTTON_PRESS && event->type != GDK_BUTTON_RELEASE || event->button.button > 3)) {
526 if (event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) {
528 if ((event->button.state & Keyboard::RelevantModifierKeyMask) && event->button.button != 1) {
530 /* almost no selection action on modified button-2 or button-3 events */
532 if (item_type != RegionItem && event->button.button != 2) {
538 Selection::Operation op = Keyboard::selection_type (event->button.state);
539 bool press = (event->type == GDK_BUTTON_PRESS);
541 // begin_reversible_command (_("select on click"));
545 if (mouse_mode != MouseRange) {
546 set_selected_regionview_from_click (press, op, true);
547 } else if (event->type == GDK_BUTTON_PRESS) {
548 set_selected_track_as_side_effect ();
552 case RegionViewNameHighlight:
554 if (mouse_mode != MouseRange) {
555 set_selected_regionview_from_click (press, op, true);
556 } else if (event->type == GDK_BUTTON_PRESS) {
557 set_selected_track_as_side_effect ();
562 case FadeInHandleItem:
564 case FadeOutHandleItem:
566 if (mouse_mode != MouseRange) {
567 set_selected_regionview_from_click (press, op, true);
568 } else if (event->type == GDK_BUTTON_PRESS) {
569 set_selected_track_as_side_effect ();
573 case ControlPointItem:
574 set_selected_track_as_side_effect ();
575 if (mouse_mode != MouseRange) {
576 set_selected_control_point_from_click (op, false);
581 /* for context click or range selection, select track */
582 if (event->button.button == 3) {
583 set_selected_track_as_side_effect ();
584 } else if (event->type == GDK_BUTTON_PRESS && mouse_mode == MouseRange) {
585 set_selected_track_as_side_effect ();
589 case AutomationTrackItem:
590 set_selected_track_as_side_effect (true);
599 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
601 track_canvas->grab_focus();
603 if (session && session->actively_recording()) {
607 button_selection (item, event, item_type);
609 if (drag_info.item == 0 &&
610 (Keyboard::is_delete_event (&event->button) ||
611 Keyboard::is_context_menu_event (&event->button) ||
612 Keyboard::is_edit_event (&event->button))) {
614 /* handled by button release */
618 switch (event->button.button) {
621 if (event->type == GDK_BUTTON_PRESS) {
623 if (drag_info.item) {
624 drag_info.item->ungrab (event->button.time);
627 /* single mouse clicks on any of these item types operate
628 independent of mouse mode, mostly because they are
629 not on the main track canvas or because we want
634 case PlayheadCursorItem:
635 start_cursor_grab (item, event);
639 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
640 hide_marker (item, event);
642 start_marker_grab (item, event);
646 case TempoMarkerItem:
647 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
648 start_tempo_marker_copy_grab (item, event);
650 start_tempo_marker_grab (item, event);
654 case MeterMarkerItem:
655 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
656 start_meter_marker_copy_grab (item, event);
658 start_meter_marker_grab (item, event);
668 case RangeMarkerBarItem:
669 start_range_markerbar_op (item, event, CreateRangeMarker);
673 case CdMarkerBarItem:
674 start_range_markerbar_op (item, event, CreateCDMarker);
678 case TransportMarkerBarItem:
679 start_range_markerbar_op (item, event, CreateTransportMarker);
688 switch (mouse_mode) {
691 case StartSelectionTrimItem:
692 start_selection_op (item, event, SelectionStartTrim);
695 case EndSelectionTrimItem:
696 start_selection_op (item, event, SelectionEndTrim);
700 if (Keyboard::modifier_state_contains
701 (event->button.state, Keyboard::ModifierMask(Keyboard::SecondaryModifier))) {
702 // contains and not equals because I can't use alt as a modifier alone.
703 start_selection_grab (item, event);
704 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
705 /* grab selection for moving */
706 start_selection_op (item, event, SelectionMove);
708 /* this was debated, but decided the more common action was to
709 make a new selection */
710 start_selection_op (item, event, CreateSelection);
715 start_selection_op (item, event, CreateSelection);
721 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)) &&
722 event->type == GDK_BUTTON_PRESS) {
724 start_rubberband_select (item, event);
726 } else if (event->type == GDK_BUTTON_PRESS) {
729 case FadeInHandleItem:
730 start_fade_in_grab (item, event);
733 case FadeOutHandleItem:
734 start_fade_out_grab (item, event);
738 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
739 start_region_copy_grab (item, event);
740 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
741 start_region_brush_grab (item, event);
743 start_region_grab (item, event);
747 case RegionViewNameHighlight:
748 start_trim (item, event);
753 /* rename happens on edit clicks */
754 start_trim (clicked_regionview->get_name_highlight(), event);
758 case ControlPointItem:
759 start_control_point_grab (item, event);
763 case AutomationLineItem:
764 start_line_grab_from_line (item, event);
769 case AutomationTrackItem:
770 start_rubberband_select (item, event);
774 case ImageFrameHandleStartItem:
775 imageframe_start_handle_op(item, event) ;
778 case ImageFrameHandleEndItem:
779 imageframe_end_handle_op(item, event) ;
782 case MarkerViewHandleStartItem:
783 markerview_item_start_handle_op(item, event) ;
786 case MarkerViewHandleEndItem:
787 markerview_item_end_handle_op(item, event) ;
791 start_markerview_grab(item, event) ;
794 start_imageframe_grab(item, event) ;
812 // start_line_grab_from_regionview (item, event);
816 start_line_grab_from_line (item, event);
819 case ControlPointItem:
820 start_control_point_grab (item, event);
831 case ControlPointItem:
832 start_control_point_grab (item, event);
835 case AutomationLineItem:
836 start_line_grab_from_line (item, event);
840 // XXX need automation mode to identify which
842 // start_line_grab_from_regionview (item, event);
852 if (event->type == GDK_BUTTON_PRESS) {
853 start_mouse_zoom (item, event);
860 if (item_type == RegionItem) {
861 start_time_fx (item, event);
868 scrub_reverse_distance = 0;
869 last_scrub_x = event->button.x;
870 scrubbing_direction = 0;
871 track_canvas->get_window()->set_cursor (*transparent_cursor);
872 /* rest handled in motion & release */
876 start_create_region_grab (item, event);
885 switch (mouse_mode) {
887 if (event->type == GDK_BUTTON_PRESS) {
890 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
891 start_region_copy_grab (item, event);
893 start_region_grab (item, event);
897 case ControlPointItem:
898 start_control_point_grab (item, event);
909 case RegionViewNameHighlight:
910 start_trim (item, event);
915 start_trim (clicked_regionview->get_name_highlight(), event);
926 if (event->type == GDK_BUTTON_PRESS) {
927 /* relax till release */
934 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
935 temporal_zoom_session();
937 temporal_zoom_to_frame (true, event_frame(event));
960 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
962 nframes_t where = event_frame (event, 0, 0);
963 AutomationTimeAxisView* atv = 0;
965 /* no action if we're recording */
967 if (session && session->actively_recording()) {
971 /* first, see if we're finishing a drag ... */
973 if (drag_info.item) {
974 if (end_grab (item, event)) {
975 /* grab dragged, so do nothing else */
980 button_selection (item, event, item_type);
982 /* edit events get handled here */
984 if (drag_info.item == 0 && Keyboard::is_edit_event (&event->button)) {
990 case TempoMarkerItem:
991 edit_tempo_marker (item);
994 case MeterMarkerItem:
995 edit_meter_marker (item);
999 if (clicked_regionview->name_active()) {
1000 return mouse_rename_region (item, event);
1010 /* context menu events get handled here */
1012 if (Keyboard::is_context_menu_event (&event->button)) {
1014 if (drag_info.item == 0) {
1016 /* no matter which button pops up the context menu, tell the menu
1017 widget to use button 1 to drive menu selection.
1020 switch (item_type) {
1022 case FadeInHandleItem:
1024 case FadeOutHandleItem:
1025 popup_fade_context_menu (1, event->button.time, item, item_type);
1029 popup_track_context_menu (1, event->button.time, item_type, false, where);
1033 case RegionViewNameHighlight:
1034 case RegionViewName:
1035 popup_track_context_menu (1, event->button.time, item_type, false, where);
1039 popup_track_context_menu (1, event->button.time, item_type, true, where);
1042 case AutomationTrackItem:
1043 popup_track_context_menu (1, event->button.time, item_type, false, where);
1047 case RangeMarkerBarItem:
1048 case TransportMarkerBarItem:
1049 case CdMarkerBarItem:
1052 popup_ruler_menu (pixel_to_frame(event->button.x), item_type);
1056 marker_context_menu (&event->button, item);
1059 case TempoMarkerItem:
1060 tm_marker_context_menu (&event->button, item);
1063 case MeterMarkerItem:
1064 tm_marker_context_menu (&event->button, item);
1067 case CrossfadeViewItem:
1068 popup_track_context_menu (1, event->button.time, item_type, false, where);
1072 case ImageFrameItem:
1073 popup_imageframe_edit_menu(1, event->button.time, item, true) ;
1075 case ImageFrameTimeAxisItem:
1076 popup_imageframe_edit_menu(1, event->button.time, item, false) ;
1078 case MarkerViewItem:
1079 popup_marker_time_axis_edit_menu(1, event->button.time, item, true) ;
1081 case MarkerTimeAxisItem:
1082 popup_marker_time_axis_edit_menu(1, event->button.time, item, false) ;
1094 /* delete events get handled here */
1096 if (drag_info.item == 0 && Keyboard::is_delete_event (&event->button)) {
1098 switch (item_type) {
1099 case TempoMarkerItem:
1100 remove_tempo_marker (item);
1103 case MeterMarkerItem:
1104 remove_meter_marker (item);
1108 remove_marker (*item, event);
1112 if (mouse_mode == MouseObject) {
1113 remove_clicked_region ();
1117 case ControlPointItem:
1118 if (mouse_mode == MouseGain) {
1119 remove_gain_control_point (item, event);
1121 remove_control_point (item, event);
1131 switch (event->button.button) {
1134 switch (item_type) {
1135 /* see comments in button_press_handler */
1136 case PlayheadCursorItem:
1139 case AutomationLineItem:
1140 case StartSelectionTrimItem:
1141 case EndSelectionTrimItem:
1145 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1146 snap_to (where, 0, true);
1148 mouse_add_new_marker (where);
1151 case CdMarkerBarItem:
1152 // if we get here then a dragged range wasn't done
1153 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1154 snap_to (where, 0, true);
1156 mouse_add_new_marker (where, true);
1160 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1163 mouse_add_new_tempo_event (where);
1167 mouse_add_new_meter_event (pixel_to_frame (event->button.x));
1175 switch (mouse_mode) {
1177 switch (item_type) {
1178 case AutomationTrackItem:
1179 atv = dynamic_cast<AutomationTimeAxisView*>(clicked_routeview);
1181 atv->add_automation_event (item, event, where, event->button.y);
1193 // Gain only makes sense for audio regions
1195 if (!dynamic_cast<AudioRegionView*>(clicked_regionview)) {
1199 switch (item_type) {
1201 dynamic_cast<AudioRegionView*>(clicked_regionview)->add_gain_point_event (item, event);
1205 case AutomationTrackItem:
1206 dynamic_cast<AutomationTimeAxisView*>(clicked_axisview)->
1207 add_automation_event (item, event, where, event->button.y);
1217 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1218 if (scrubbing_direction == 0) {
1219 /* no drag, just a click */
1220 switch (item_type) {
1222 play_selected_region ();
1228 /* make sure we stop */
1229 session->request_transport_speed (0.0);
1243 switch (mouse_mode) {
1246 switch (item_type) {
1248 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1250 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier|Keyboard::SecondaryModifier))) {
1253 // Button2 click is unused
1266 // x_style_paste (where, 1.0);
1286 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1292 if (last_item_entered != item) {
1293 last_item_entered = item;
1294 last_item_entered_n = 0;
1297 switch (item_type) {
1298 case ControlPointItem:
1299 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1300 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1301 cp->set_visible (true);
1305 at_y = cp->get_y ();
1306 cp->item()->i2w (at_x, at_y);
1310 fraction = 1.0 - (cp->get_y() / cp->line().height());
1312 if (is_drawable() && !_scrubbing) {
1313 track_canvas->get_window()->set_cursor (*fader_cursor);
1316 last_item_entered_n++;
1317 set_verbose_canvas_cursor (cp->line().get_verbose_cursor_string (fraction), at_x, at_y);
1318 if (last_item_entered_n < 10) {
1319 show_verbose_canvas_cursor ();
1325 if (mouse_mode == MouseGain) {
1326 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1328 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredGainLine.get();
1329 if (is_drawable()) {
1330 track_canvas->get_window()->set_cursor (*fader_cursor);
1335 case AutomationLineItem:
1336 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1338 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1340 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredAutomationLine.get();
1342 if (is_drawable()) {
1343 track_canvas->get_window()->set_cursor (*fader_cursor);
1348 case RegionViewNameHighlight:
1349 if (is_drawable() && mouse_mode == MouseObject) {
1350 track_canvas->get_window()->set_cursor (*trimmer_cursor);
1354 case StartSelectionTrimItem:
1355 case EndSelectionTrimItem:
1358 case ImageFrameHandleStartItem:
1359 case ImageFrameHandleEndItem:
1360 case MarkerViewHandleStartItem:
1361 case MarkerViewHandleEndItem:
1364 if (is_drawable()) {
1365 track_canvas->get_window()->set_cursor (*trimmer_cursor);
1369 case PlayheadCursorItem:
1370 if (is_drawable()) {
1371 switch (_edit_point) {
1373 track_canvas->get_window()->set_cursor (*grabber_edit_point_cursor);
1376 track_canvas->get_window()->set_cursor (*grabber_cursor);
1382 case RegionViewName:
1384 /* when the name is not an active item, the entire name highlight is for trimming */
1386 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1387 if (mouse_mode == MouseObject && is_drawable()) {
1388 track_canvas->get_window()->set_cursor (*trimmer_cursor);
1394 case AutomationTrackItem:
1395 if (is_drawable()) {
1396 Gdk::Cursor *cursor;
1397 switch (mouse_mode) {
1399 cursor = selector_cursor;
1402 cursor = zoom_cursor;
1405 cursor = cross_hair_cursor;
1409 track_canvas->get_window()->set_cursor (*cursor);
1411 AutomationTimeAxisView* atv;
1412 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1413 clear_entered_track = false;
1414 set_entered_track (atv);
1420 case RangeMarkerBarItem:
1421 case TransportMarkerBarItem:
1422 case CdMarkerBarItem:
1425 if (is_drawable()) {
1426 time_canvas->get_window()->set_cursor (*timebar_cursor);
1431 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1434 entered_marker = marker;
1435 marker->set_color_rgba (ARDOUR_UI::config()->canvasvar_EnteredMarker.get());
1437 case MeterMarkerItem:
1438 case TempoMarkerItem:
1439 if (is_drawable()) {
1440 time_canvas->get_window()->set_cursor (*timebar_cursor);
1443 case FadeInHandleItem:
1444 case FadeOutHandleItem:
1445 if (mouse_mode == MouseObject) {
1446 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1448 rect->property_fill_color_rgba() = 0;
1449 rect->property_outline_pixels() = 1;
1458 /* second pass to handle entered track status in a comprehensible way.
1461 switch (item_type) {
1463 case AutomationLineItem:
1464 case ControlPointItem:
1465 /* these do not affect the current entered track state */
1466 clear_entered_track = false;
1469 case AutomationTrackItem:
1470 /* handled above already */
1474 set_entered_track (0);
1482 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1491 switch (item_type) {
1492 case ControlPointItem:
1493 cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
1494 if (cp->line().the_list()->interpolation() != AutomationList::Discrete) {
1495 if (cp->line().npoints() > 1 && !cp->selected()) {
1496 cp->set_visible (false);
1500 if (is_drawable()) {
1501 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1504 hide_verbose_canvas_cursor ();
1507 case RegionViewNameHighlight:
1508 case StartSelectionTrimItem:
1509 case EndSelectionTrimItem:
1510 case PlayheadCursorItem:
1513 case ImageFrameHandleStartItem:
1514 case ImageFrameHandleEndItem:
1515 case MarkerViewHandleStartItem:
1516 case MarkerViewHandleEndItem:
1519 if (is_drawable()) {
1520 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1525 case AutomationLineItem:
1526 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1528 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1530 line->property_fill_color_rgba() = al->get_line_color();
1532 if (is_drawable()) {
1533 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1537 case RegionViewName:
1538 /* see enter_handler() for notes */
1539 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1540 if (is_drawable() && mouse_mode == MouseObject) {
1541 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1546 case RangeMarkerBarItem:
1547 case TransportMarkerBarItem:
1548 case CdMarkerBarItem:
1552 if (is_drawable()) {
1553 time_canvas->get_window()->set_cursor (*timebar_cursor);
1558 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1562 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
1563 location_flags_changed (loc, this);
1566 case MeterMarkerItem:
1567 case TempoMarkerItem:
1569 if (is_drawable()) {
1570 time_canvas->get_window()->set_cursor (*timebar_cursor);
1575 case FadeInHandleItem:
1576 case FadeOutHandleItem:
1577 rv = static_cast<RegionView*>(item->get_data ("regionview"));
1579 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1581 rect->property_fill_color_rgba() = rv->get_fill_color();
1582 rect->property_outline_pixels() = 0;
1587 case AutomationTrackItem:
1588 if (is_drawable()) {
1589 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1590 clear_entered_track = true;
1591 Glib::signal_idle().connect (mem_fun(*this, &Editor::left_automation_track));
1603 Editor::left_automation_track ()
1605 if (clear_entered_track) {
1606 set_entered_track (0);
1607 clear_entered_track = false;
1617 if (scrubbing_direction == 0) {
1619 session->request_locate (drag_info.current_pointer_frame, false);
1620 session->request_transport_speed (0.1);
1621 scrubbing_direction = 1;
1625 if (last_scrub_x > drag_info.current_pointer_x) {
1627 /* pointer moved to the left */
1629 if (scrubbing_direction > 0) {
1631 /* we reversed direction to go backwards */
1634 scrub_reverse_distance += (int) (last_scrub_x - drag_info.current_pointer_x);
1638 /* still moving to the left (backwards) */
1640 scrub_reversals = 0;
1641 scrub_reverse_distance = 0;
1643 delta = 0.01 * (last_scrub_x - drag_info.current_pointer_x);
1644 session->request_transport_speed (session->transport_speed() - delta);
1648 /* pointer moved to the right */
1650 if (scrubbing_direction < 0) {
1651 /* we reversed direction to go forward */
1654 scrub_reverse_distance += (int) (drag_info.current_pointer_x - last_scrub_x);
1657 /* still moving to the right */
1659 scrub_reversals = 0;
1660 scrub_reverse_distance = 0;
1662 delta = 0.01 * (drag_info.current_pointer_x - last_scrub_x);
1663 session->request_transport_speed (session->transport_speed() + delta);
1667 /* if there have been more than 2 opposite motion moves detected, or one that moves
1668 back more than 10 pixels, reverse direction
1671 if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
1673 if (scrubbing_direction > 0) {
1674 /* was forwards, go backwards */
1675 session->request_transport_speed (-0.1);
1676 scrubbing_direction = -1;
1678 /* was backwards, go forwards */
1679 session->request_transport_speed (0.1);
1680 scrubbing_direction = 1;
1683 scrub_reverse_distance = 0;
1684 scrub_reversals = 0;
1688 last_scrub_x = drag_info.current_pointer_x;
1692 Editor::motion_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type, bool from_autoscroll)
1694 if (event->motion.is_hint) {
1697 /* We call this so that MOTION_NOTIFY events continue to be
1698 delivered to the canvas. We need to do this because we set
1699 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
1700 the density of the events, at the expense of a round-trip
1701 to the server. Given that this will mostly occur on cases
1702 where DISPLAY = :0.0, and given the cost of what the motion
1703 event might do, its a good tradeoff.
1706 track_canvas->get_pointer (x, y);
1709 if (current_stepping_trackview) {
1710 /* don't keep the persistent stepped trackview if the mouse moves */
1711 current_stepping_trackview = 0;
1712 step_timeout.disconnect ();
1715 if (session && session->actively_recording()) {
1716 /* Sorry. no dragging stuff around while we record */
1720 drag_info.item_type = item_type;
1721 drag_info.last_pointer_x = drag_info.current_pointer_x;
1722 drag_info.last_pointer_y = drag_info.current_pointer_y;
1723 drag_info.current_pointer_frame = event_frame (event, &drag_info.current_pointer_x,
1724 &drag_info.current_pointer_y);
1727 switch (mouse_mode) {
1739 if (!from_autoscroll && drag_info.item) {
1740 /* item != 0 is the best test i can think of for dragging.
1742 if (!drag_info.move_threshold_passed) {
1744 bool x_threshold_passed = (::llabs ((nframes64_t) (drag_info.current_pointer_x - drag_info.grab_x)) > 4LL);
1745 bool y_threshold_passed = (::llabs ((nframes64_t) (drag_info.current_pointer_y - drag_info.grab_y)) > 4LL);
1747 drag_info.move_threshold_passed = (x_threshold_passed || y_threshold_passed);
1749 // and change the initial grab loc/frame if this drag info wants us to
1751 if (drag_info.want_move_threshold && drag_info.move_threshold_passed) {
1752 drag_info.grab_frame = drag_info.current_pointer_frame;
1753 drag_info.grab_x = drag_info.current_pointer_x;
1754 drag_info.grab_y = drag_info.current_pointer_y;
1755 drag_info.last_pointer_frame = drag_info.grab_frame;
1756 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
1761 switch (item_type) {
1762 case PlayheadCursorItem:
1764 case ControlPointItem:
1765 case TempoMarkerItem:
1766 case MeterMarkerItem:
1767 case RegionViewNameHighlight:
1768 case StartSelectionTrimItem:
1769 case EndSelectionTrimItem:
1772 case AutomationLineItem:
1773 case FadeInHandleItem:
1774 case FadeOutHandleItem:
1777 case ImageFrameHandleStartItem:
1778 case ImageFrameHandleEndItem:
1779 case MarkerViewHandleStartItem:
1780 case MarkerViewHandleEndItem:
1783 if (drag_info.item && (event->motion.state & Gdk::BUTTON1_MASK ||
1784 (event->motion.state & Gdk::BUTTON2_MASK))) {
1785 if (!from_autoscroll) {
1786 maybe_autoscroll (&event->motion);
1788 (this->*(drag_info.motion_callback)) (item, event);
1797 switch (mouse_mode) {
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 (&event->motion);
1808 (this->*(drag_info.motion_callback)) (item, event);
1819 track_canvas_motion (event);
1820 // drag_info.last_pointer_frame = drag_info.current_pointer_frame;
1828 Editor::break_drag ()
1830 stop_canvas_autoscroll ();
1831 hide_verbose_canvas_cursor ();
1833 if (drag_info.item) {
1834 drag_info.item->ungrab (0);
1836 /* put it back where it came from */
1841 drag_info.item->i2w (cxw, cyw);
1842 drag_info.item->move (drag_info.original_x - cxw, drag_info.original_y - cyw);
1849 Editor::finalize_drag ()
1852 drag_info.copy = false;
1853 drag_info.motion_callback = 0;
1854 drag_info.finished_callback = 0;
1855 drag_info.dest_trackview = 0;
1856 drag_info.source_trackview = 0;
1857 drag_info.last_frame_position = 0;
1858 drag_info.grab_frame = 0;
1859 drag_info.last_pointer_frame = 0;
1860 drag_info.current_pointer_frame = 0;
1861 drag_info.brushing = false;
1863 if (drag_info.copied_location) {
1864 delete drag_info.copied_location;
1865 drag_info.copied_location = 0;
1870 Editor::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
1872 if (drag_info.item == 0) {
1873 fatal << _("programming error: start_grab called without drag item") << endmsg;
1879 cursor = which_grabber_cursor ();
1882 // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
1884 if (event->button.button == 2) {
1885 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
1886 drag_info.y_constrained = true;
1887 drag_info.x_constrained = false;
1889 drag_info.y_constrained = false;
1890 drag_info.x_constrained = true;
1893 drag_info.x_constrained = false;
1894 drag_info.y_constrained = false;
1897 drag_info.grab_frame = event_frame (event, &drag_info.grab_x, &drag_info.grab_y);
1898 drag_info.last_pointer_frame = drag_info.grab_frame;
1899 drag_info.current_pointer_frame = drag_info.grab_frame;
1900 drag_info.current_pointer_x = drag_info.grab_x;
1901 drag_info.current_pointer_y = drag_info.grab_y;
1902 drag_info.last_pointer_x = drag_info.current_pointer_x;
1903 drag_info.last_pointer_y = drag_info.current_pointer_y;
1904 drag_info.cumulative_x_drag = 0;
1905 drag_info.cumulative_y_drag = 0;
1906 drag_info.first_move = true;
1907 drag_info.move_threshold_passed = false;
1908 drag_info.want_move_threshold = false;
1909 drag_info.pointer_frame_offset = 0;
1910 drag_info.brushing = false;
1911 drag_info.copied_location = 0;
1913 drag_info.original_x = 0;
1914 drag_info.original_y = 0;
1915 drag_info.item->i2w (drag_info.original_x, drag_info.original_y);
1917 drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK,
1919 event->button.time);
1921 if (session && session->transport_rolling()) {
1922 drag_info.was_rolling = true;
1924 drag_info.was_rolling = false;
1927 switch (snap_type) {
1928 case SnapToRegionStart:
1929 case SnapToRegionEnd:
1930 case SnapToRegionSync:
1931 case SnapToRegionBoundary:
1932 build_region_boundary_cache ();
1940 Editor::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t time)
1942 drag_info.item->ungrab (0);
1943 drag_info.item = new_item;
1946 cursor = which_grabber_cursor ();
1949 drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK, *cursor, time);
1953 Editor::end_grab (ArdourCanvas::Item* item, GdkEvent* event)
1955 bool did_drag = false;
1957 stop_canvas_autoscroll ();
1959 if (drag_info.item == 0) {
1963 drag_info.item->ungrab (event->button.time);
1965 if (drag_info.finished_callback) {
1966 drag_info.last_pointer_x = drag_info.current_pointer_x;
1967 drag_info.last_pointer_y = drag_info.current_pointer_y;
1968 (this->*(drag_info.finished_callback)) (item, event);
1971 did_drag = !drag_info.first_move;
1973 hide_verbose_canvas_cursor();
1981 Editor::start_fade_in_grab (ArdourCanvas::Item* item, GdkEvent* event)
1983 drag_info.item = item;
1984 drag_info.motion_callback = &Editor::fade_in_drag_motion_callback;
1985 drag_info.finished_callback = &Editor::fade_in_drag_finished_callback;
1989 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
1990 fatal << _("programming error: fade in canvas item has no regionview data pointer!") << endmsg;
1994 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1996 drag_info.pointer_frame_offset = drag_info.grab_frame - ((nframes_t) arv->audio_region()->fade_in()->back()->when + arv->region()->position());
2000 Editor::fade_in_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2002 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
2004 nframes_t fade_length;
2006 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2007 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2013 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2017 if (pos < (arv->region()->position() + 64)) {
2018 fade_length = 64; // this should be a minimum defined somewhere
2019 } else if (pos > arv->region()->last_frame()) {
2020 fade_length = arv->region()->length();
2022 fade_length = pos - arv->region()->position();
2024 /* mapover the region selection */
2026 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2028 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2034 tmp->reset_fade_in_shape_width (fade_length);
2037 show_verbose_duration_cursor (arv->region()->position(), arv->region()->position() + fade_length, 10);
2039 drag_info.first_move = false;
2043 Editor::fade_in_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2045 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
2047 nframes_t fade_length;
2049 if (drag_info.first_move) return;
2051 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2052 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2057 if (pos < (arv->region()->position() + 64)) {
2058 fade_length = 64; // this should be a minimum defined somewhere
2059 } else if (pos > arv->region()->last_frame()) {
2060 fade_length = arv->region()->length();
2062 fade_length = pos - arv->region()->position();
2065 begin_reversible_command (_("change fade in length"));
2067 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2069 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2075 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
2076 XMLNode &before = alist->get_state();
2078 tmp->audio_region()->set_fade_in_length (fade_length);
2079 tmp->audio_region()->set_fade_in_active (true);
2081 XMLNode &after = alist->get_state();
2082 session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2085 commit_reversible_command ();
2089 Editor::start_fade_out_grab (ArdourCanvas::Item* item, GdkEvent* event)
2091 drag_info.item = item;
2092 drag_info.motion_callback = &Editor::fade_out_drag_motion_callback;
2093 drag_info.finished_callback = &Editor::fade_out_drag_finished_callback;
2097 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
2098 fatal << _("programming error: fade out canvas item has no regionview data pointer!") << endmsg;
2102 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
2104 drag_info.pointer_frame_offset = drag_info.grab_frame - (arv->region()->length() - (nframes_t) arv->audio_region()->fade_out()->back()->when + arv->region()->position());
2108 Editor::fade_out_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2110 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
2112 nframes_t fade_length;
2114 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2115 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2120 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2124 if (pos > (arv->region()->last_frame() - 64)) {
2125 fade_length = 64; // this should really be a minimum fade defined somewhere
2127 else if (pos < arv->region()->position()) {
2128 fade_length = arv->region()->length();
2131 fade_length = arv->region()->last_frame() - pos;
2134 /* mapover the region selection */
2136 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2138 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2144 tmp->reset_fade_out_shape_width (fade_length);
2147 show_verbose_duration_cursor (arv->region()->last_frame() - fade_length, arv->region()->last_frame(), 10);
2149 drag_info.first_move = false;
2153 Editor::fade_out_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2155 if (drag_info.first_move) return;
2157 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
2159 nframes_t fade_length;
2161 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2162 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2168 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2172 if (pos > (arv->region()->last_frame() - 64)) {
2173 fade_length = 64; // this should really be a minimum fade defined somewhere
2175 else if (pos < arv->region()->position()) {
2176 fade_length = arv->region()->length();
2179 fade_length = arv->region()->last_frame() - pos;
2182 begin_reversible_command (_("change fade out length"));
2184 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2186 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2192 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
2193 XMLNode &before = alist->get_state();
2195 tmp->audio_region()->set_fade_out_length (fade_length);
2196 tmp->audio_region()->set_fade_out_active (true);
2198 XMLNode &after = alist->get_state();
2199 session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2202 commit_reversible_command ();
2206 Editor::start_cursor_grab (ArdourCanvas::Item* item, GdkEvent* event)
2208 drag_info.item = item;
2209 drag_info.motion_callback = &Editor::cursor_drag_motion_callback;
2210 drag_info.finished_callback = &Editor::cursor_drag_finished_callback;
2214 if ((drag_info.data = (item->get_data ("cursor"))) == 0) {
2215 fatal << _("programming error: cursor canvas item has no cursor data pointer!") << endmsg;
2219 Cursor* cursor = (Cursor *) drag_info.data;
2221 if (cursor == playhead_cursor) {
2222 _dragging_playhead = true;
2224 if (session && drag_info.was_rolling) {
2225 session->request_stop ();
2228 if (session && session->is_auditioning()) {
2229 session->cancel_audition ();
2233 drag_info.pointer_frame_offset = drag_info.grab_frame - cursor->current_frame;
2235 show_verbose_time_cursor (cursor->current_frame, 10);
2239 Editor::cursor_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2241 Cursor* cursor = (Cursor *) drag_info.data;
2242 nframes_t adjusted_frame;
2244 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2245 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2251 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2252 if (cursor == playhead_cursor) {
2253 snap_to (adjusted_frame);
2257 if (adjusted_frame == drag_info.last_pointer_frame) return;
2259 cursor->set_position (adjusted_frame);
2261 UpdateAllTransportClocks (cursor->current_frame);
2263 show_verbose_time_cursor (cursor->current_frame, 10);
2265 drag_info.last_pointer_frame = adjusted_frame;
2266 drag_info.first_move = false;
2270 Editor::cursor_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2272 if (drag_info.first_move) return;
2274 cursor_drag_motion_callback (item, event);
2276 _dragging_playhead = false;
2278 if (item == &playhead_cursor->canvas_item) {
2280 session->request_locate (playhead_cursor->current_frame, drag_info.was_rolling);
2286 Editor::update_marker_drag_item (Location *location)
2288 double x1 = frame_to_pixel (location->start());
2289 double x2 = frame_to_pixel (location->end());
2291 if (location->is_mark()) {
2292 marker_drag_line_points.front().set_x(x1);
2293 marker_drag_line_points.back().set_x(x1);
2294 marker_drag_line->property_points() = marker_drag_line_points;
2297 range_marker_drag_rect->property_x1() = x1;
2298 range_marker_drag_rect->property_x2() = x2;
2303 Editor::start_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2307 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2308 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2314 Location *location = find_location_from_marker (marker, is_start);
2316 drag_info.item = item;
2317 drag_info.data = marker;
2318 drag_info.motion_callback = &Editor::marker_drag_motion_callback;
2319 drag_info.finished_callback = &Editor::marker_drag_finished_callback;
2323 _dragging_edit_point = true;
2325 drag_info.copied_location = new Location (*location);
2326 drag_info.pointer_frame_offset = drag_info.grab_frame - (is_start ? location->start() : location->end());
2328 update_marker_drag_item (location);
2330 if (location->is_mark()) {
2331 // marker_drag_line->show();
2332 // marker_drag_line->raise_to_top();
2334 range_marker_drag_rect->show();
2335 range_marker_drag_rect->raise_to_top();
2339 show_verbose_time_cursor (location->start(), 10);
2341 show_verbose_time_cursor (location->end(), 10);
2344 Selection::Operation op = Keyboard::selection_type (event->button.state);
2347 case Selection::Toggle:
2348 selection->toggle (marker);
2350 case Selection::Set:
2351 selection->set (marker);
2353 case Selection::Extend:
2354 selection->add (marker);
2356 case Selection::Add:
2357 selection->add (marker);
2363 Editor::marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2366 Marker* marker = (Marker *) drag_info.data;
2367 Location *real_location;
2368 Location *copy_location;
2370 bool move_both = false;
2373 if (drag_info.pointer_frame_offset <= drag_info.current_pointer_frame) {
2374 newframe = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2379 nframes_t next = newframe;
2381 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2382 snap_to (newframe, 0, true);
2385 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
2389 /* call this to find out if its the start or end */
2391 if ((real_location = find_location_from_marker (marker, is_start)) == 0) {
2395 if (real_location->locked()) {
2399 /* use the copy that we're "dragging" around */
2401 copy_location = drag_info.copied_location;
2403 f_delta = copy_location->end() - copy_location->start();
2405 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2409 if (copy_location->is_mark()) {
2412 copy_location->set_start (newframe);
2416 if (is_start) { // start-of-range marker
2419 copy_location->set_start (newframe);
2420 copy_location->set_end (newframe + f_delta);
2421 } else if (newframe < copy_location->end()) {
2422 copy_location->set_start (newframe);
2424 snap_to (next, 1, true);
2425 copy_location->set_end (next);
2426 copy_location->set_start (newframe);
2429 } else { // end marker
2432 copy_location->set_end (newframe);
2433 copy_location->set_start (newframe - f_delta);
2434 } else if (newframe > copy_location->start()) {
2435 copy_location->set_end (newframe);
2437 } else if (newframe > 0) {
2438 snap_to (next, -1, true);
2439 copy_location->set_start (next);
2440 copy_location->set_end (newframe);
2445 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
2446 drag_info.first_move = false;
2448 update_marker_drag_item (copy_location);
2450 LocationMarkers* lm = find_location_markers (real_location);
2451 lm->set_position (copy_location->start(), copy_location->end());
2452 edit_point_clock.set (copy_location->start());
2454 show_verbose_time_cursor (newframe, 10);
2458 Editor::marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2460 if (drag_info.first_move) {
2461 /* just a click, do nothing but whatever selection occured */
2465 _dragging_edit_point = false;
2467 Marker* marker = (Marker *) drag_info.data;
2470 begin_reversible_command ( _("move marker") );
2471 XMLNode &before = session->locations()->get_state();
2473 Location * location = find_location_from_marker (marker, is_start);
2477 if (location->locked()) {
2481 if (location->is_mark()) {
2482 location->set_start (drag_info.copied_location->start());
2484 location->set (drag_info.copied_location->start(), drag_info.copied_location->end());
2488 XMLNode &after = session->locations()->get_state();
2489 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
2490 commit_reversible_command ();
2492 marker_drag_line->hide();
2493 range_marker_drag_rect->hide();
2497 Editor::start_meter_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2500 MeterMarker* meter_marker;
2502 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2503 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2507 meter_marker = dynamic_cast<MeterMarker*> (marker);
2509 MetricSection& section (meter_marker->meter());
2511 if (!section.movable()) {
2515 drag_info.item = item;
2516 drag_info.copy = false;
2517 drag_info.data = marker;
2518 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2519 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2523 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2525 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2529 Editor::start_meter_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2532 MeterMarker* meter_marker;
2534 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2535 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2539 meter_marker = dynamic_cast<MeterMarker*> (marker);
2541 // create a dummy marker for visual representation of moving the copy.
2542 // The actual copying is not done before we reach the finish callback.
2544 snprintf (name, sizeof(name), "%g/%g", meter_marker->meter().beats_per_bar(), meter_marker->meter().note_divisor ());
2545 MeterMarker* new_marker = new MeterMarker(*this, *meter_group, ARDOUR_UI::config()->canvasvar_MeterMarker.get(), name,
2546 *new MeterSection(meter_marker->meter()));
2548 drag_info.item = &new_marker->the_item();
2549 drag_info.copy = true;
2550 drag_info.data = new_marker;
2551 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2552 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2556 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2558 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2562 Editor::meter_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2564 MeterMarker* marker = (MeterMarker *) drag_info.data;
2565 nframes_t adjusted_frame;
2567 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2568 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2574 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2575 snap_to (adjusted_frame);
2578 if (adjusted_frame == drag_info.last_pointer_frame) return;
2580 marker->set_position (adjusted_frame);
2583 drag_info.last_pointer_frame = adjusted_frame;
2584 drag_info.first_move = false;
2586 show_verbose_time_cursor (adjusted_frame, 10);
2590 Editor::meter_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2592 if (drag_info.first_move) return;
2594 meter_marker_drag_motion_callback (drag_info.item, event);
2596 MeterMarker* marker = (MeterMarker *) drag_info.data;
2599 TempoMap& map (session->tempo_map());
2600 map.bbt_time (drag_info.last_pointer_frame, when);
2602 if (drag_info.copy == true) {
2603 begin_reversible_command (_("copy meter mark"));
2604 XMLNode &before = map.get_state();
2605 map.add_meter (marker->meter(), when);
2606 XMLNode &after = map.get_state();
2607 session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2608 commit_reversible_command ();
2610 // delete the dummy marker we used for visual representation of copying.
2611 // a new visual marker will show up automatically.
2614 begin_reversible_command (_("move meter mark"));
2615 XMLNode &before = map.get_state();
2616 map.move_meter (marker->meter(), when);
2617 XMLNode &after = map.get_state();
2618 session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2619 commit_reversible_command ();
2624 Editor::start_tempo_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2627 TempoMarker* tempo_marker;
2629 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2630 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2634 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2635 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2639 MetricSection& section (tempo_marker->tempo());
2641 if (!section.movable()) {
2645 drag_info.item = item;
2646 drag_info.copy = false;
2647 drag_info.data = marker;
2648 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2649 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2653 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2654 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2658 Editor::start_tempo_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2661 TempoMarker* tempo_marker;
2663 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2664 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2668 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2669 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2673 // create a dummy marker for visual representation of moving the copy.
2674 // The actual copying is not done before we reach the finish callback.
2676 snprintf (name, sizeof (name), "%.2f", tempo_marker->tempo().beats_per_minute());
2677 TempoMarker* new_marker = new TempoMarker(*this, *tempo_group, ARDOUR_UI::config()->canvasvar_TempoMarker.get(), name,
2678 *new TempoSection(tempo_marker->tempo()));
2680 drag_info.item = &new_marker->the_item();
2681 drag_info.copy = true;
2682 drag_info.data = new_marker;
2683 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2684 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2688 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2690 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2694 Editor::tempo_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2696 TempoMarker* marker = (TempoMarker *) drag_info.data;
2697 nframes_t adjusted_frame;
2699 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2700 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2706 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2707 snap_to (adjusted_frame);
2710 if (adjusted_frame == drag_info.last_pointer_frame) return;
2712 /* OK, we've moved far enough to make it worth actually move the thing. */
2714 marker->set_position (adjusted_frame);
2716 show_verbose_time_cursor (adjusted_frame, 10);
2718 drag_info.last_pointer_frame = adjusted_frame;
2719 drag_info.first_move = false;
2723 Editor::tempo_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2725 if (drag_info.first_move) return;
2727 tempo_marker_drag_motion_callback (drag_info.item, event);
2729 TempoMarker* marker = (TempoMarker *) drag_info.data;
2732 TempoMap& map (session->tempo_map());
2733 map.bbt_time (drag_info.last_pointer_frame, when);
2735 if (drag_info.copy == true) {
2736 begin_reversible_command (_("copy tempo mark"));
2737 XMLNode &before = map.get_state();
2738 map.add_tempo (marker->tempo(), when);
2739 XMLNode &after = map.get_state();
2740 session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2741 commit_reversible_command ();
2743 // delete the dummy marker we used for visual representation of copying.
2744 // a new visual marker will show up automatically.
2747 begin_reversible_command (_("move tempo mark"));
2748 XMLNode &before = map.get_state();
2749 map.move_tempo (marker->tempo(), when);
2750 XMLNode &after = map.get_state();
2751 session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2752 commit_reversible_command ();
2757 Editor::remove_gain_control_point (ArdourCanvas::Item*item, GdkEvent* event)
2759 ControlPoint* control_point;
2761 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2762 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2766 // We shouldn't remove the first or last gain point
2767 if (control_point->line().is_last_point(*control_point) ||
2768 control_point->line().is_first_point(*control_point)) {
2772 control_point->line().remove_point (*control_point);
2776 Editor::remove_control_point (ArdourCanvas::Item* item, GdkEvent* event)
2778 ControlPoint* control_point;
2780 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2781 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2785 control_point->line().remove_point (*control_point);
2789 Editor::start_control_point_grab (ArdourCanvas::Item* item, GdkEvent* event)
2791 ControlPoint* control_point;
2793 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2794 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2798 drag_info.item = item;
2799 drag_info.data = control_point;
2800 drag_info.motion_callback = &Editor::control_point_drag_motion_callback;
2801 drag_info.finished_callback = &Editor::control_point_drag_finished_callback;
2803 start_grab (event, fader_cursor);
2805 // start the grab at the center of the control point so
2806 // the point doesn't 'jump' to the mouse after the first drag
2807 drag_info.grab_x = control_point->get_x();
2808 drag_info.grab_y = control_point->get_y();
2810 control_point->line().parent_group().i2w(drag_info.grab_x, drag_info.grab_y);
2811 track_canvas->w2c(drag_info.grab_x, drag_info.grab_y, drag_info.grab_x, drag_info.grab_y);
2813 drag_info.grab_frame = pixel_to_frame(drag_info.grab_x);
2815 control_point->line().start_drag (control_point, drag_info.grab_frame, 0);
2817 double fraction = 1.0 - ((control_point->get_y() - control_point->line().y_position()) / (double)control_point->line().height());
2818 set_verbose_canvas_cursor (control_point->line().get_verbose_cursor_string (fraction),
2819 drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
2821 show_verbose_canvas_cursor ();
2825 Editor::control_point_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2827 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2829 double dx = drag_info.current_pointer_x - drag_info.last_pointer_x;
2830 double dy = drag_info.current_pointer_y - drag_info.last_pointer_y;
2832 if (event->button.state & Keyboard::SecondaryModifier) {
2837 double cx = drag_info.grab_x + drag_info.cumulative_x_drag + dx;
2838 double cy = drag_info.grab_y + drag_info.cumulative_y_drag + dy;
2840 // calculate zero crossing point. back off by .01 to stay on the
2841 // positive side of zero
2843 double zero_gain_y = (1.0 - ZERO_GAIN_FRACTION) * cp->line().height() - .01;
2844 cp->line().parent_group().i2w(_unused, zero_gain_y);
2846 // make sure we hit zero when passing through
2847 if ((cy < zero_gain_y and (cy - dy) > zero_gain_y)
2848 or (cy > zero_gain_y and (cy - dy) < zero_gain_y)) {
2852 if (drag_info.x_constrained) {
2853 cx = drag_info.grab_x;
2855 if (drag_info.y_constrained) {
2856 cy = drag_info.grab_y;
2859 drag_info.cumulative_x_drag = cx - drag_info.grab_x;
2860 drag_info.cumulative_y_drag = cy - drag_info.grab_y;
2862 cp->line().parent_group().w2i (cx, cy);
2866 cy = min ((double) (cp->line().y_position() + cp->line().height()), cy);
2868 //translate cx to frames
2869 nframes_t cx_frames = unit_to_frame (cx);
2871 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier()) && !drag_info.x_constrained) {
2872 snap_to (cx_frames);
2875 const double fraction = 1.0 - ((cy - cp->line().y_position()) / (double)cp->line().height());
2879 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) {
2885 cp->line().point_drag (*cp, cx_frames , fraction, push);
2887 set_verbose_canvas_cursor_text (cp->line().get_verbose_cursor_string (fraction));
2889 drag_info.first_move = false;
2893 Editor::control_point_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2895 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2897 if (drag_info.first_move) {
2901 if ((event->type == GDK_BUTTON_RELEASE) && (event->button.button == 1) && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2902 reset_point_selection ();
2906 control_point_drag_motion_callback (item, event);
2908 cp->line().end_drag (cp);
2912 Editor::start_line_grab_from_regionview (ArdourCanvas::Item* item, GdkEvent* event)
2914 switch (mouse_mode) {
2916 assert(dynamic_cast<AudioRegionView*>(clicked_regionview));
2917 start_line_grab (dynamic_cast<AudioRegionView*>(clicked_regionview)->get_gain_line(), event);
2925 Editor::start_line_grab_from_line (ArdourCanvas::Item* item, GdkEvent* event)
2929 if ((al = reinterpret_cast<AutomationLine*> (item->get_data ("line"))) == 0) {
2930 fatal << _("programming error: line canvas item has no line pointer!") << endmsg;
2934 start_line_grab (al, event);
2938 Editor::start_line_grab (AutomationLine* line, GdkEvent* event)
2942 nframes_t frame_within_region;
2944 /* need to get x coordinate in terms of parent (TimeAxisItemView)
2948 cx = event->button.x;
2949 cy = event->button.y;
2950 line->parent_group().w2i (cx, cy);
2951 frame_within_region = (nframes_t) floor (cx * frames_per_unit);
2953 if (!line->control_points_adjacent (frame_within_region, current_line_drag_info.before,
2954 current_line_drag_info.after)) {
2955 /* no adjacent points */
2959 drag_info.item = &line->grab_item();
2960 drag_info.data = line;
2961 drag_info.motion_callback = &Editor::line_drag_motion_callback;
2962 drag_info.finished_callback = &Editor::line_drag_finished_callback;
2964 start_grab (event, fader_cursor);
2966 const double fraction = 1.0 - ((cy - line->y_position()) / (double)line->height());
2968 line->start_drag (0, drag_info.grab_frame, fraction);
2970 set_verbose_canvas_cursor (line->get_verbose_cursor_string (fraction),
2971 drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
2972 show_verbose_canvas_cursor ();
2976 Editor::line_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2978 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
2980 double dy = drag_info.current_pointer_y - drag_info.last_pointer_y;
2982 if (event->button.state & Keyboard::SecondaryModifier) {
2986 double cx = drag_info.current_pointer_x;
2987 double cy = drag_info.grab_y + drag_info.cumulative_y_drag + dy;
2989 // calculate zero crossing point. back off by .01 to stay on the
2990 // positive side of zero
2992 double zero_gain_y = (1.0 - ZERO_GAIN_FRACTION) * line->height() - .01;
2993 line->parent_group().i2w(_unused, zero_gain_y);
2995 // make sure we hit zero when passing through
2996 if ((cy < zero_gain_y and (cy - dy) > zero_gain_y)
2997 or (cy > zero_gain_y and (cy - dy) < zero_gain_y)) {
3001 drag_info.cumulative_y_drag = cy - drag_info.grab_y;
3003 line->parent_group().w2i (cx, cy);
3006 cy = min ((double) line->height(), cy);
3008 const double fraction = 1.0 - ((cy - line->y_position()) / (double)line->height());
3012 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) {
3018 line->line_drag (current_line_drag_info.before, current_line_drag_info.after, fraction, push);
3020 set_verbose_canvas_cursor_text (line->get_verbose_cursor_string (fraction));
3024 Editor::line_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3026 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
3027 line_drag_motion_callback (item, event);
3032 Editor::start_region_grab (ArdourCanvas::Item* item, GdkEvent* event)
3034 if (selection->regions.empty() || clicked_regionview == 0) {
3038 drag_info.copy = false;
3039 drag_info.item = item;
3040 drag_info.data = clicked_regionview;
3042 if (Config->get_edit_mode() == Splice) {
3043 drag_info.motion_callback = &Editor::region_drag_splice_motion_callback;
3044 drag_info.finished_callback = &Editor::region_drag_splice_finished_callback;
3046 drag_info.motion_callback = &Editor::region_drag_motion_callback;
3047 drag_info.finished_callback = &Editor::region_drag_finished_callback;
3053 TimeAxisView* tvp = clicked_axisview;
3054 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
3056 if (tv && tv->is_track()) {
3057 speed = tv->get_diskstream()->speed();
3060 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
3061 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
3062 drag_info.source_trackview = &clicked_regionview->get_time_axis_view();
3063 drag_info.dest_trackview = drag_info.source_trackview;
3064 // we want a move threshold
3065 drag_info.want_move_threshold = true;
3067 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3069 begin_reversible_command (_("move region(s)"));
3073 Editor::start_create_region_grab (ArdourCanvas::Item* item, GdkEvent* event)
3075 drag_info.copy = false;
3076 drag_info.item = item;
3077 drag_info.data = clicked_axisview;
3078 drag_info.source_trackview = clicked_axisview;
3079 drag_info.dest_trackview = drag_info.source_trackview;
3080 drag_info.motion_callback = &Editor::create_region_drag_motion_callback;
3081 drag_info.finished_callback = &Editor::create_region_drag_finished_callback;
3087 Editor::start_region_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
3089 if (selection->regions.empty() || clicked_regionview == 0) {
3093 drag_info.copy = true;
3094 drag_info.item = item;
3095 drag_info.data = clicked_regionview;
3099 TimeAxisView* tv = &clicked_regionview->get_time_axis_view();
3100 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
3103 if (rtv && rtv->is_track()) {
3104 speed = rtv->get_diskstream()->speed();
3107 drag_info.source_trackview = &clicked_regionview->get_time_axis_view();
3108 drag_info.dest_trackview = drag_info.source_trackview;
3109 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
3110 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
3111 // we want a move threshold
3112 drag_info.want_move_threshold = true;
3113 drag_info.motion_callback = &Editor::region_drag_motion_callback;
3114 drag_info.finished_callback = &Editor::region_drag_finished_callback;
3115 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3119 Editor::start_region_brush_grab (ArdourCanvas::Item* item, GdkEvent* event)
3121 if (selection->regions.empty() || clicked_regionview == 0 || Config->get_edit_mode() == Splice) {
3125 drag_info.copy = false;
3126 drag_info.item = item;
3127 drag_info.data = clicked_regionview;
3128 drag_info.motion_callback = &Editor::region_drag_motion_callback;
3129 drag_info.finished_callback = &Editor::region_drag_finished_callback;
3134 TimeAxisView* tvp = clicked_axisview;
3135 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
3137 if (tv && tv->is_track()) {
3138 speed = tv->get_diskstream()->speed();
3141 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
3142 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
3143 drag_info.source_trackview = &clicked_regionview->get_time_axis_view();
3144 drag_info.dest_trackview = drag_info.source_trackview;
3145 // we want a move threshold
3146 drag_info.want_move_threshold = true;
3147 drag_info.brushing = true;
3149 begin_reversible_command (_("Drag region brush"));
3153 Editor::possibly_copy_regions_during_grab (GdkEvent* event)
3155 if (drag_info.copy && drag_info.move_threshold_passed && drag_info.want_move_threshold) {
3157 drag_info.want_move_threshold = false; // don't copy again
3159 /* duplicate the regionview(s) and region(s) */
3161 vector<RegionView*> new_regionviews;
3163 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3170 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
3171 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
3174 nrv = new AudioRegionView (*arv);
3176 nrv = new MidiRegionView (*mrv);
3181 const boost::shared_ptr<const Region> original = arv->region();
3182 boost::shared_ptr<Region> region_copy = RegionFactory::create (original);
3183 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> (region_copy);
3185 nrv->get_canvas_group()->show ();
3186 new_regionviews.push_back (nrv);
3189 if (new_regionviews.empty()) {
3193 /* reset selection to new regionviews. This will not set selection visual status for
3194 these regionviews since they don't belong to a track, so do that by hand too.
3197 selection->set (new_regionviews);
3199 for (vector<RegionView*>::iterator i = new_regionviews.begin(); i != new_regionviews.end(); ++i) {
3200 (*i)->set_selected (true);
3203 /* reset drag_info data to reflect the fact that we are dragging the copies */
3205 drag_info.data = new_regionviews.front();
3207 swap_grab (new_regionviews.front()->get_canvas_group (), 0, event->motion.time);
3212 Editor::check_region_drag_possible (RouteTimeAxisView** tv)
3214 /* Which trackview is this ? */
3216 TimeAxisView* tvp = trackview_by_y_position (drag_info.current_pointer_y);
3217 (*tv) = dynamic_cast<RouteTimeAxisView*>(tvp);
3219 /* The region motion is only processed if the pointer is over
3223 if (!(*tv) || !(*tv)->is_track()) {
3224 /* To make sure we hide the verbose canvas cursor when the mouse is
3225 not held over and audiotrack.
3227 hide_verbose_canvas_cursor ();
3234 struct RegionSelectionByPosition {
3235 bool operator() (RegionView*a, RegionView* b) {
3236 return a->region()->position () < b->region()->position();
3241 Editor::region_drag_splice_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
3243 RouteTimeAxisView* tv;
3245 if (!check_region_drag_possible (&tv)) {
3249 if (!drag_info.move_threshold_passed) {
3255 if (drag_info.current_pointer_x - drag_info.grab_x > 0) {
3261 RegionSelection copy (selection->regions);
3263 RegionSelectionByPosition cmp;
3266 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
3268 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
3274 boost::shared_ptr<Playlist> playlist;
3276 if ((playlist = atv->playlist()) == 0) {
3280 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
3285 if (drag_info.current_pointer_frame < (*i)->region()->last_frame() + 1) {
3289 if (drag_info.current_pointer_frame > (*i)->region()->first_frame()) {
3295 playlist->shuffle ((*i)->region(), dir);
3297 drag_info.grab_x = drag_info.current_pointer_x;
3302 Editor::region_drag_splice_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3307 Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
3311 RegionView* rv = reinterpret_cast<RegionView*> (drag_info.data);
3312 nframes_t pending_region_position = 0;
3313 int32_t pointer_y_span = 0, canvas_pointer_y_span = 0, original_pointer_order;
3314 int32_t visible_y_high = 0, visible_y_low = 512; //high meaning higher numbered.. not the height on the screen
3315 bool clamp_y_axis = false;
3316 vector<int32_t> height_list(512) ;
3317 vector<int32_t>::iterator j;
3318 RouteTimeAxisView* tv;
3320 possibly_copy_regions_during_grab (event);
3322 if (!check_region_drag_possible (&tv)) {
3326 original_pointer_order = drag_info.dest_trackview->order;
3328 /************************************************************
3330 ************************************************************/
3332 if (drag_info.brushing) {
3333 clamp_y_axis = true;
3338 if ((pointer_y_span = (drag_info.dest_trackview->order - tv->order)) != 0) {
3340 int32_t children = 0, numtracks = 0;
3341 // XXX hard coding track limit, oh my, so very very bad
3342 bitset <1024> tracks (0x00);
3343 /* get a bitmask representing the visible tracks */
3345 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
3346 TimeAxisView *tracklist_timeview;
3347 tracklist_timeview = (*i);
3348 RouteTimeAxisView* rtv2 = dynamic_cast<RouteTimeAxisView*>(tracklist_timeview);
3349 TimeAxisView::Children children_list;
3351 /* zeroes are audio tracks. ones are other types. */
3353 if (!rtv2->hidden()) {
3355 if (visible_y_high < rtv2->order) {
3356 visible_y_high = rtv2->order;
3358 if (visible_y_low > rtv2->order) {
3359 visible_y_low = rtv2->order;
3362 if (!rtv2->is_track()) {
3363 tracks = tracks |= (0x01 << rtv2->order);
3366 height_list[rtv2->order] = (*i)->height;
3368 if ((children_list = rtv2->get_child_list()).size() > 0) {
3369 for (TimeAxisView::Children::iterator j = children_list.begin(); j != children_list.end(); ++j) {
3370 tracks = tracks |= (0x01 << (rtv2->order + children));
3371 height_list[rtv2->order + children] = (*j)->height;
3379 /* find the actual span according to the canvas */
3381 canvas_pointer_y_span = pointer_y_span;
3382 if (drag_info.dest_trackview->order >= tv->order) {
3384 for (y = tv->order; y < drag_info.dest_trackview->order; y++) {
3385 if (height_list[y] == 0 ) {
3386 canvas_pointer_y_span--;
3391 for (y = drag_info.dest_trackview->order;y <= tv->order; y++) {
3392 if ( height_list[y] == 0 ) {
3393 canvas_pointer_y_span++;
3398 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3399 RegionView* rv2 = (*i);
3400 double ix1, ix2, iy1, iy2;
3403 if (rv2->region()->locked()) {
3407 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3408 rv2->get_canvas_group()->i2w (ix1, iy1);
3409 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3410 RouteTimeAxisView* rtv2 = dynamic_cast<RouteTimeAxisView*>(tvp2);
3412 if (rtv2->order != original_pointer_order) {
3413 /* this isn't the pointer track */
3415 if (canvas_pointer_y_span > 0) {
3417 /* moving up the canvas */
3418 if ((rtv2->order - canvas_pointer_y_span) >= visible_y_low) {
3420 int32_t visible_tracks = 0;
3421 while (visible_tracks < canvas_pointer_y_span ) {
3424 while (height_list[rtv2->order - (visible_tracks - n)] == 0) {
3425 /* we're passing through a hidden track */
3430 if (tracks[rtv2->order - (canvas_pointer_y_span - n)] != 0x00) {
3431 clamp_y_axis = true;
3435 clamp_y_axis = true;
3438 } else if (canvas_pointer_y_span < 0) {
3440 /*moving down the canvas*/
3442 if ((rtv2->order - (canvas_pointer_y_span - n)) <= visible_y_high) { // we will overflow
3445 int32_t visible_tracks = 0;
3447 while (visible_tracks > canvas_pointer_y_span ) {
3450 while (height_list[rtv2->order - (visible_tracks - n)] == 0) {
3454 if ( tracks[rtv2->order - ( canvas_pointer_y_span - n)] != 0x00) {
3455 clamp_y_axis = true;
3460 clamp_y_axis = true;
3466 /* this is the pointer's track */
3467 if ((rtv2->order - pointer_y_span) > visible_y_high) { // we will overflow
3468 clamp_y_axis = true;
3469 } else if ((rtv2->order - pointer_y_span) < visible_y_low) { // we will underflow
3470 clamp_y_axis = true;
3478 } else if (drag_info.dest_trackview == tv) {
3479 clamp_y_axis = true;
3483 if (!clamp_y_axis) {
3484 drag_info.dest_trackview = tv;
3487 /************************************************************
3489 ************************************************************/
3491 /* compute the amount of pointer motion in frames, and where
3492 the region would be if we moved it by that much.
3495 if ( drag_info.move_threshold_passed ) {
3497 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
3499 nframes_t sync_frame;
3500 nframes_t sync_offset;
3503 pending_region_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
3505 sync_offset = rv->region()->sync_offset (sync_dir);
3507 /* we don't handle a sync point that lies before zero.
3509 if (sync_dir >= 0 || (sync_dir < 0 && pending_region_position >= sync_offset)) {
3510 sync_frame = pending_region_position + (sync_dir*sync_offset);
3512 /* we snap if the snap modifier is not enabled.
3515 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3516 snap_to (sync_frame);
3519 pending_region_position = rv->region()->adjust_to_sync (sync_frame);
3522 pending_region_position = drag_info.last_frame_position;
3526 pending_region_position = 0;
3529 if (pending_region_position > max_frames - rv->region()->length()) {
3530 pending_region_position = drag_info.last_frame_position;
3533 // printf ("3: pending_region_position= %lu %lu\n", pending_region_position, drag_info.last_frame_position );
3535 bool x_move_allowed;
3537 if (Config->get_edit_mode() == Lock) {
3538 if (drag_info.copy) {
3539 x_move_allowed = !drag_info.x_constrained;
3541 /* in locked edit mode, reverse the usual meaning of x_constrained */
3542 x_move_allowed = drag_info.x_constrained;
3545 x_move_allowed = !drag_info.x_constrained;
3548 if ( pending_region_position != drag_info.last_frame_position && x_move_allowed ) {
3550 /* now compute the canvas unit distance we need to move the regionview
3551 to make it appear at the new location.
3554 if (pending_region_position > drag_info.last_frame_position) {
3555 x_delta = ((double) (pending_region_position - drag_info.last_frame_position) / frames_per_unit);
3557 x_delta = -((double) (drag_info.last_frame_position - pending_region_position) / frames_per_unit);
3560 drag_info.last_frame_position = pending_region_position;
3567 /* threshold not passed */
3572 /*************************************************************
3574 ************************************************************/
3576 if (x_delta == 0 && (pointer_y_span == 0)) {
3577 /* haven't reached next snap point, and we're not switching
3578 trackviews. nothing to do.
3585 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3587 RegionView* rv2 = (*i);
3589 // If any regionview is at zero, we need to know so we can stop further leftward motion.
3591 double ix1, ix2, iy1, iy2;
3592 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3593 rv2->get_canvas_group()->i2w (ix1, iy1);
3602 /*************************************************************
3604 ************************************************************/
3608 if (drag_info.first_move) {
3609 if (drag_info.move_threshold_passed) {
3620 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
3621 const list<RegionView*>& layered_regions = selection->regions.by_layer();
3623 for (list<RegionView*>::const_iterator i = layered_regions.begin(); i != layered_regions.end(); ++i) {
3625 RegionView* rv = (*i);
3626 double ix1, ix2, iy1, iy2;
3627 int32_t temp_pointer_y_span = pointer_y_span;
3629 if (rv->region()->locked()) {
3633 /* get item BBox, which will be relative to parent. so we have
3634 to query on a child, then convert to world coordinates using
3638 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3639 rv->get_canvas_group()->i2w (ix1, iy1);
3640 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3641 RouteTimeAxisView* canvas_rtv = dynamic_cast<RouteTimeAxisView*>(tvp2);
3642 RouteTimeAxisView* temp_rtv;
3644 if ((pointer_y_span != 0) && !clamp_y_axis) {
3647 for (j = height_list.begin(); j!= height_list.end(); j++) {
3648 if (x == canvas_rtv->order) {
3649 /* we found the track the region is on */
3650 if (x != original_pointer_order) {
3651 /*this isn't from the same track we're dragging from */
3652 temp_pointer_y_span = canvas_pointer_y_span;
3654 while (temp_pointer_y_span > 0) {
3655 /* we're moving up canvas-wise,
3656 so we need to find the next track height
3658 if (j != height_list.begin()) {
3661 if (x != original_pointer_order) {
3662 /* we're not from the dragged track, so ignore hidden tracks. */
3664 temp_pointer_y_span++;
3668 temp_pointer_y_span--;
3671 while (temp_pointer_y_span < 0) {
3673 if (x != original_pointer_order) {
3675 temp_pointer_y_span--;
3679 if (j != height_list.end()) {
3682 temp_pointer_y_span++;
3684 /* find out where we'll be when we move and set height accordingly */
3686 tvp2 = trackview_by_y_position (iy1 + y_delta);
3687 temp_rtv = dynamic_cast<RouteTimeAxisView*>(tvp2);
3688 rv->set_y_position_and_height (0, temp_rtv->height);
3690 /* if you un-comment the following, the region colours will follow the track colours whilst dragging,
3691 personally, i think this can confuse things, but never mind.
3694 //const GdkColor& col (temp_rtv->view->get_region_color());
3695 //rv->set_color (const_cast<GdkColor&>(col));
3703 /* prevent the regionview from being moved to before
3704 the zero position on the canvas.
3709 if (-x_delta > ix1) {
3712 } else if ((x_delta > 0) && (rv->region()->last_frame() > max_frames - x_delta)) {
3713 x_delta = max_frames - rv->region()->last_frame();
3717 if (drag_info.first_move) {
3719 /* hide any dependent views */
3721 rv->get_time_axis_view().hide_dependent_views (*rv);
3723 /* this is subtle. raising the regionview itself won't help,
3724 because raise_to_top() just puts the item on the top of
3725 its parent's stack. so, we need to put the trackview canvas_display group
3726 on the top, since its parent is the whole canvas.
3729 rv->get_canvas_group()->raise_to_top();
3730 rv->get_time_axis_view().canvas_display->raise_to_top();
3731 cursor_group->raise_to_top();
3733 rv->fake_set_opaque (true);
3736 if (drag_info.brushing) {
3737 mouse_brush_insert_region (rv, pending_region_position);
3739 rv->move (x_delta, y_delta);
3742 } /* foreach region */
3746 if (drag_info.first_move && drag_info.move_threshold_passed) {
3747 cursor_group->raise_to_top();
3748 drag_info.first_move = false;
3751 if (x_delta != 0 && !drag_info.brushing) {
3752 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3757 Editor::region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3759 bool nocommit = true;
3760 vector<RegionView*> copies;
3761 RouteTimeAxisView* source_tv;
3762 boost::shared_ptr<Diskstream> ds;
3763 boost::shared_ptr<Playlist> from_playlist;
3764 vector<RegionView*> new_selection;
3765 typedef set<boost::shared_ptr<Playlist> > PlaylistSet;
3766 PlaylistSet modified_playlists;
3767 pair<PlaylistSet::iterator,bool> insert_result;
3769 /* first_move is set to false if the regionview has been moved in the
3773 if (drag_info.first_move) {
3780 /* The regionview has been moved at some stage during the grab so we need
3781 to account for any mouse movement between this event and the last one.
3784 region_drag_motion_callback (item, event);
3786 if (Config->get_edit_mode() == Splice && !pre_drag_region_selection.empty()) {
3787 selection->set (pre_drag_region_selection);
3788 pre_drag_region_selection.clear ();
3791 if (drag_info.brushing) {
3792 /* all changes were made during motion event handlers */
3794 if (drag_info.copy) {
3795 for (list<RegionView*>::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
3796 copies.push_back (*i);
3805 /* reverse this here so that we have the correct logic to finalize
3809 if (Config->get_edit_mode() == Lock && !drag_info.copy) {
3810 drag_info.x_constrained = !drag_info.x_constrained;
3813 if (drag_info.copy) {
3814 if (drag_info.x_constrained) {
3815 op_string = _("fixed time region copy");
3817 op_string = _("region copy");
3820 if (drag_info.x_constrained) {
3821 op_string = _("fixed time region drag");
3823 op_string = _("region drag");
3827 begin_reversible_command (op_string);
3829 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ) {
3831 RegionView* rv = (*i);
3832 double ix1, ix2, iy1, iy2;
3833 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3834 rv->get_canvas_group()->i2w (ix1, iy1);
3835 TimeAxisView* dest_tv = trackview_by_y_position (iy1);
3836 RouteTimeAxisView* dest_rtv = dynamic_cast<RouteTimeAxisView*>(dest_tv);
3838 bool changed_tracks;
3839 bool changed_position;
3842 if (rv->region()->locked()) {
3847 /* adjust for track speed */
3851 if (dest_rtv && dest_rtv->get_diskstream()) {
3852 speed = dest_rtv->get_diskstream()->speed();
3855 changed_position = (drag_info.last_frame_position != (nframes_t) (rv->region()->position()/speed));
3856 changed_tracks = (dest_tv != &rv->get_time_axis_view());
3858 if (changed_position && !drag_info.x_constrained) {
3859 where = (nframes_t) (unit_to_frame (ix1) * speed);
3861 where = rv->region()->position();
3864 /* undo the previous hide_dependent_views so that xfades don't
3865 disappear on copying regions
3868 rv->get_time_axis_view().reveal_dependent_views (*rv);
3870 boost::shared_ptr<Region> new_region;
3872 if (drag_info.copy) {
3873 /* we already made a copy */
3874 new_region = rv->region();
3876 new_region = RegionFactory::create (rv->region());
3879 if (changed_tracks || drag_info.copy) {
3881 boost::shared_ptr<Playlist> to_playlist = dest_rtv->playlist();
3883 latest_regionviews.clear ();
3885 sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3887 insert_result = modified_playlists.insert (to_playlist);
3888 if (insert_result.second) {
3889 session->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
3892 to_playlist->add_region (new_region, where);
3896 if (!latest_regionviews.empty()) {
3897 // XXX why just the first one ? we only expect one
3898 dest_rtv->reveal_dependent_views (*latest_regionviews.front());
3899 new_selection.push_back (latest_regionviews.front());
3904 /* just change the model */
3906 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
3908 insert_result = modified_playlists.insert (playlist);
3909 if (insert_result.second) {
3910 session->add_command (new MementoCommand<Playlist>(*playlist, &playlist->get_state(), 0));
3913 rv->region()->set_position (where, (void*) this);
3916 if (changed_tracks && !drag_info.copy) {
3918 /* get the playlist where this drag started. we can't use rv->region()->playlist()
3919 because we may have copied the region and it has not been attached to a playlist.
3922 assert ((source_tv = dynamic_cast<RouteTimeAxisView*> (&rv->get_time_axis_view())));
3923 assert ((ds = source_tv->get_diskstream()));
3924 assert ((from_playlist = ds->playlist()));
3926 /* moved to a different audio track, without copying */
3928 /* the region that used to be in the old playlist is not
3929 moved to the new one - we use a copy of it. as a result,
3930 any existing editor for the region should no longer be
3934 rv->hide_region_editor();
3935 rv->fake_set_opaque (false);
3937 /* remove the region from the old playlist */
3939 insert_result = modified_playlists.insert (from_playlist);
3940 if (insert_result.second) {
3941 session->add_command (new MementoCommand<Playlist>(*from_playlist, &from_playlist->get_state(), 0));
3944 from_playlist->remove_region ((rv->region()));
3946 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
3947 was selected in all of them, then removing it from a playlist will have removed all
3948 trace of it from the selection (i.e. there were N regions selected, we removed 1,
3949 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
3950 corresponding regionview, and the selection is now empty).
3952 this could have invalidated any and all iterators into the region selection.
3954 the heuristic we use here is: if the region selection is empty, break out of the loop
3955 here. if the region selection is not empty, then restart the loop because we know that
3956 we must have removed at least the region(view) we've just been working on as well as any
3957 that we processed on previous iterations.
3959 EXCEPT .... if we are doing a copy drag, then the selection hasn't been modified and
3960 we can just iterate.
3963 if (selection->regions.empty()) {
3966 i = selection->regions.by_layer().begin();
3973 if (drag_info.copy) {
3974 copies.push_back (rv);
3979 if (new_selection.empty()) {
3980 if (drag_info.copy) {
3981 /* the region(view)s that are selected and being dragged around
3982 are copies and do not belong to any track. remove them
3983 from the selection right here.
3985 selection->clear_regions();
3988 /* this will clear any existing selection that would have been
3989 cleared in the other clause above
3991 selection->set (new_selection);
3996 for (set<boost::shared_ptr<Playlist> >::iterator p = modified_playlists.begin(); p != modified_playlists.end(); ++p) {
3997 session->add_command (new MementoCommand<Playlist>(*(*p), 0, &(*p)->get_state()));
3999 commit_reversible_command ();
4002 for (vector<RegionView*>::iterator x = copies.begin(); x != copies.end(); ++x) {
4008 Editor::create_region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
4010 if (drag_info.move_threshold_passed) {
4011 if (drag_info.first_move) {
4012 // TODO: create region-create-drag region view here
4013 drag_info.first_move = false;
4016 // TODO: resize region-create-drag region view here
4021 Editor::create_region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
4023 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (drag_info.dest_trackview);
4027 const boost::shared_ptr<MidiDiskstream> diskstream =
4028 boost::dynamic_pointer_cast<MidiDiskstream>(mtv->view()->trackview().track()->diskstream());
4031 warning << "Cannot create non-MIDI region" << endl;
4035 if (drag_info.first_move) {
4036 begin_reversible_command (_("create region"));
4037 XMLNode &before = mtv->playlist()->get_state();
4039 nframes_t start = drag_info.grab_frame;
4040 snap_to (start, -1);
4041 const Meter& m = session->tempo_map().meter_at(start);
4042 const Tempo& t = session->tempo_map().tempo_at(start);
4043 double length = floor (m.frames_per_bar(t, session->frame_rate()));
4045 boost::shared_ptr<Source> src = session->create_midi_source_for_session(*diskstream.get());
4047 mtv->playlist()->add_region (boost::dynamic_pointer_cast<MidiRegion>
4048 (RegionFactory::create(src, 0, (nframes_t) length,
4049 PBD::basename_nosuffix(src->name()))), start);
4050 XMLNode &after = mtv->playlist()->get_state();
4051 session->add_command(new MementoCommand<Playlist>(*mtv->playlist().get(), &before, &after));
4052 commit_reversible_command();
4055 create_region_drag_motion_callback (item, event);
4056 // TODO: create region-create-drag region here
4061 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
4063 /* Either add to or set the set the region selection, unless
4064 this is an alignment click (control used)
4067 if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
4068 TimeAxisView* tv = &rv.get_time_axis_view();
4069 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
4071 if (rtv && rtv->is_track()) {
4072 speed = rtv->get_diskstream()->speed();
4075 nframes64_t where = get_preferred_edit_position();
4079 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
4081 align_region (rv.region(), SyncPoint, (nframes_t) (where * speed));
4083 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
4085 align_region (rv.region(), End, (nframes_t) (where * speed));
4089 align_region (rv.region(), Start, (nframes_t) (where * speed));
4096 Editor::show_verbose_time_cursor (nframes_t frame, double offset, double xpos, double ypos)
4102 nframes_t frame_rate;
4111 if (Profile->get_sae() || Profile->get_small_screen()) {
4112 m = ARDOUR_UI::instance()->primary_clock.mode();
4114 m = ARDOUR_UI::instance()->secondary_clock.mode();
4118 case AudioClock::BBT:
4119 session->bbt_time (frame, bbt);
4120 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, bbt.bars, bbt.beats, bbt.ticks);
4123 case AudioClock::SMPTE:
4124 session->smpte_time (frame, smpte);
4125 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
4128 case AudioClock::MinSec:
4129 /* XXX this is copied from show_verbose_duration_cursor() */
4130 frame_rate = session->frame_rate();
4131 hours = frame / (frame_rate * 3600);
4132 frame = frame % (frame_rate * 3600);
4133 mins = frame / (frame_rate * 60);
4134 frame = frame % (frame_rate * 60);
4135 secs = (float) frame / (float) frame_rate;
4136 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
4140 snprintf (buf, sizeof(buf), "%u", frame);
4144 if (xpos >= 0 && ypos >=0) {
4145 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
4148 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
4150 show_verbose_canvas_cursor ();
4154 Editor::show_verbose_duration_cursor (nframes_t start, nframes_t end, double offset, double xpos, double ypos)
4161 nframes_t distance, frame_rate;
4163 Meter meter_at_start(session->tempo_map().meter_at(start));
4171 if (Profile->get_sae() || Profile->get_small_screen()) {
4172 m = ARDOUR_UI::instance()->primary_clock.mode ();
4174 m = ARDOUR_UI::instance()->secondary_clock.mode ();
4178 case AudioClock::BBT:
4179 session->bbt_time (start, sbbt);
4180 session->bbt_time (end, ebbt);
4183 /* XXX this computation won't work well if the
4184 user makes a selection that spans any meter changes.
4187 ebbt.bars -= sbbt.bars;
4188 if (ebbt.beats >= sbbt.beats) {
4189 ebbt.beats -= sbbt.beats;
4192 ebbt.beats = int(meter_at_start.beats_per_bar()) + ebbt.beats - sbbt.beats;
4194 if (ebbt.ticks >= sbbt.ticks) {
4195 ebbt.ticks -= sbbt.ticks;
4198 ebbt.ticks = int(Meter::ticks_per_beat) + ebbt.ticks - sbbt.ticks;
4201 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, ebbt.bars, ebbt.beats, ebbt.ticks);
4204 case AudioClock::SMPTE:
4205 session->smpte_duration (end - start, smpte);
4206 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
4209 case AudioClock::MinSec:
4210 /* XXX this stuff should be elsewhere.. */
4211 distance = end - start;
4212 frame_rate = session->frame_rate();
4213 hours = distance / (frame_rate * 3600);
4214 distance = distance % (frame_rate * 3600);
4215 mins = distance / (frame_rate * 60);
4216 distance = distance % (frame_rate * 60);
4217 secs = (float) distance / (float) frame_rate;
4218 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
4222 snprintf (buf, sizeof(buf), "%u", end - start);
4226 if (xpos >= 0 && ypos >=0) {
4227 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
4230 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
4233 show_verbose_canvas_cursor ();
4237 Editor::collect_new_region_view (RegionView* rv)
4239 latest_regionviews.push_back (rv);
4243 Editor::collect_and_select_new_region_view (RegionView* rv)
4246 latest_regionviews.push_back (rv);
4250 Editor::start_selection_grab (ArdourCanvas::Item* item, GdkEvent* event)
4252 if (clicked_regionview == 0) {
4256 /* lets try to create new Region for the selection */
4258 vector<boost::shared_ptr<Region> > new_regions;
4259 create_region_from_selection (new_regions);
4261 if (new_regions.empty()) {
4265 /* XXX fix me one day to use all new regions */
4267 boost::shared_ptr<Region> region (new_regions.front());
4269 /* add it to the current stream/playlist.
4271 tricky: the streamview for the track will add a new regionview. we will
4272 catch the signal it sends when it creates the regionview to
4273 set the regionview we want to then drag.
4276 latest_regionviews.clear();
4277 sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
4279 /* A selection grab currently creates two undo/redo operations, one for
4280 creating the new region and another for moving it.
4283 begin_reversible_command (_("selection grab"));
4285 boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
4287 XMLNode *before = &(playlist->get_state());
4288 clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
4289 XMLNode *after = &(playlist->get_state());
4290 session->add_command(new MementoCommand<Playlist>(*playlist, before, after));
4292 commit_reversible_command ();
4296 if (latest_regionviews.empty()) {
4297 /* something went wrong */
4301 /* we need to deselect all other regionviews, and select this one
4302 i'm ignoring undo stuff, because the region creation will take care of it
4304 selection->set (latest_regionviews);
4306 drag_info.item = latest_regionviews.front()->get_canvas_group();
4307 drag_info.data = latest_regionviews.front();
4308 drag_info.motion_callback = &Editor::region_drag_motion_callback;
4309 drag_info.finished_callback = &Editor::region_drag_finished_callback;
4313 drag_info.source_trackview = clicked_routeview;
4314 drag_info.dest_trackview = drag_info.source_trackview;
4315 drag_info.last_frame_position = latest_regionviews.front()->region()->position();
4316 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
4318 show_verbose_time_cursor (drag_info.last_frame_position, 10);
4322 Editor::cancel_selection ()
4324 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4325 (*i)->hide_selection ();
4327 selection->clear ();
4328 clicked_selection = 0;
4332 Editor::start_selection_op (ArdourCanvas::Item* item, GdkEvent* event, SelectionOp op)
4334 nframes_t start = 0;
4341 drag_info.item = item;
4342 drag_info.motion_callback = &Editor::drag_selection;
4343 drag_info.finished_callback = &Editor::end_selection_op;
4348 case CreateSelection:
4349 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
4350 drag_info.copy = true;
4352 drag_info.copy = false;
4354 start_grab (event, selector_cursor);
4357 case SelectionStartTrim:
4358 if (clicked_axisview) {
4359 clicked_axisview->order_selection_trims (item, true);
4361 start_grab (event, trimmer_cursor);
4362 start = selection->time[clicked_selection].start;
4363 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
4366 case SelectionEndTrim:
4367 if (clicked_axisview) {
4368 clicked_axisview->order_selection_trims (item, false);
4370 start_grab (event, trimmer_cursor);
4371 end = selection->time[clicked_selection].end;
4372 drag_info.pointer_frame_offset = drag_info.grab_frame - end;
4376 start = selection->time[clicked_selection].start;
4378 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
4382 if (selection_op == SelectionMove) {
4383 show_verbose_time_cursor(start, 10);
4385 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4390 Editor::drag_selection (ArdourCanvas::Item* item, GdkEvent* event)
4392 nframes_t start = 0;
4395 nframes_t pending_position;
4397 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
4398 pending_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
4400 pending_position = 0;
4403 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4404 snap_to (pending_position);
4407 /* only alter selection if the current frame is
4408 different from the last frame position (adjusted)
4411 if (pending_position == drag_info.last_pointer_frame) return;
4413 switch (selection_op) {
4414 case CreateSelection:
4416 if (drag_info.first_move) {
4417 snap_to (drag_info.grab_frame);
4420 if (pending_position < drag_info.grab_frame) {
4421 start = pending_position;
4422 end = drag_info.grab_frame;
4424 end = pending_position;
4425 start = drag_info.grab_frame;
4428 /* first drag: Either add to the selection
4429 or create a new selection->
4432 if (drag_info.first_move) {
4434 begin_reversible_command (_("range selection"));
4436 if (drag_info.copy) {
4437 /* adding to the selection */
4438 clicked_selection = selection->add (start, end);
4439 drag_info.copy = false;
4441 /* new selection-> */
4442 clicked_selection = selection->set (clicked_axisview, start, end);
4447 case SelectionStartTrim:
4449 if (drag_info.first_move) {
4450 begin_reversible_command (_("trim selection start"));
4453 start = selection->time[clicked_selection].start;
4454 end = selection->time[clicked_selection].end;
4456 if (pending_position > end) {
4459 start = pending_position;
4463 case SelectionEndTrim:
4465 if (drag_info.first_move) {
4466 begin_reversible_command (_("trim selection end"));
4469 start = selection->time[clicked_selection].start;
4470 end = selection->time[clicked_selection].end;
4472 if (pending_position < start) {
4475 end = pending_position;
4482 if (drag_info.first_move) {
4483 begin_reversible_command (_("move selection"));
4486 start = selection->time[clicked_selection].start;
4487 end = selection->time[clicked_selection].end;
4489 length = end - start;
4491 start = pending_position;
4494 end = start + length;
4499 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
4500 start_canvas_autoscroll (1, 0);
4504 selection->replace (clicked_selection, start, end);
4507 drag_info.last_pointer_frame = pending_position;
4508 drag_info.first_move = false;
4510 if (selection_op == SelectionMove) {
4511 show_verbose_time_cursor(start, 10);
4513 show_verbose_time_cursor(pending_position, 10);
4518 Editor::end_selection_op (ArdourCanvas::Item* item, GdkEvent* event)
4520 if (!drag_info.first_move) {
4521 drag_selection (item, event);
4522 /* XXX this is not object-oriented programming at all. ick */
4523 if (selection->time.consolidate()) {
4524 selection->TimeChanged ();
4526 commit_reversible_command ();
4528 /* just a click, no pointer movement.*/
4530 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
4532 selection->clear_time();
4537 /* XXX what happens if its a music selection? */
4538 session->set_audio_range (selection->time);
4539 stop_canvas_autoscroll ();
4543 Editor::start_trim (ArdourCanvas::Item* item, GdkEvent* event)
4546 TimeAxisView* tvp = clicked_axisview;
4547 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4549 if (tv && tv->is_track()) {
4550 speed = tv->get_diskstream()->speed();
4553 nframes_t region_start = (nframes_t) (clicked_regionview->region()->position() / speed);
4554 nframes_t region_end = (nframes_t) (clicked_regionview->region()->last_frame() / speed);
4555 nframes_t region_length = (nframes_t) (clicked_regionview->region()->length() / speed);
4557 //drag_info.item = clicked_regionview->get_name_highlight();
4558 drag_info.item = item;
4559 drag_info.motion_callback = &Editor::trim_motion_callback;
4560 drag_info.finished_callback = &Editor::trim_finished_callback;
4562 start_grab (event, trimmer_cursor);
4564 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
4565 trim_op = ContentsTrim;
4567 /* These will get overridden for a point trim.*/
4568 if (drag_info.current_pointer_frame < (region_start + region_length/2)) {
4569 /* closer to start */
4570 trim_op = StartTrim;
4571 } else if (drag_info.current_pointer_frame > (region_end - region_length/2)) {
4579 show_verbose_time_cursor(region_start, 10);
4582 show_verbose_time_cursor(region_end, 10);
4585 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4591 Editor::trim_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
4593 RegionView* rv = clicked_regionview;
4594 nframes_t frame_delta = 0;
4595 bool left_direction;
4596 bool obey_snap = !Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier());
4598 /* snap modifier works differently here..
4599 its' current state has to be passed to the
4600 various trim functions in order to work properly
4604 TimeAxisView* tvp = clicked_axisview;
4605 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4606 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
4608 if (tv && tv->is_track()) {
4609 speed = tv->get_diskstream()->speed();
4612 if (drag_info.last_pointer_frame > drag_info.current_pointer_frame) {
4613 left_direction = true;
4615 left_direction = false;
4619 snap_to (drag_info.current_pointer_frame);
4622 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
4626 if (drag_info.first_move) {
4632 trim_type = "Region start trim";
4635 trim_type = "Region end trim";
4638 trim_type = "Region content trim";
4642 begin_reversible_command (trim_type);
4644 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4645 (*i)->fake_set_opaque(false);
4646 (*i)->region()->freeze ();
4648 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
4650 arv->temporarily_hide_envelope ();
4652 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4653 insert_result = motion_frozen_playlists.insert (pl);
4654 if (insert_result.second) {
4655 session->add_command(new MementoCommand<Playlist>(*pl, &pl->get_state(), 0));
4661 if (left_direction) {
4662 frame_delta = (drag_info.last_pointer_frame - drag_info.current_pointer_frame);
4664 frame_delta = (drag_info.current_pointer_frame - drag_info.last_pointer_frame);
4669 if ((left_direction == false) && (drag_info.current_pointer_frame <= rv->region()->first_frame()/speed)) {
4672 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4673 single_start_trim (**i, frame_delta, left_direction, obey_snap);
4679 if ((left_direction == true) && (drag_info.current_pointer_frame > (nframes_t) (rv->region()->last_frame()/speed))) {
4682 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4683 single_end_trim (**i, frame_delta, left_direction, obey_snap);
4690 bool swap_direction = false;
4692 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
4693 swap_direction = true;
4696 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4697 i != selection->regions.by_layer().end(); ++i)
4699 single_contents_trim (**i, frame_delta, left_direction, swap_direction, obey_snap);
4707 show_verbose_time_cursor((nframes_t) (rv->region()->position()/speed), 10);
4710 show_verbose_time_cursor((nframes_t) (rv->region()->last_frame()/speed), 10);
4713 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4717 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4718 drag_info.first_move = false;
4722 Editor::single_contents_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool swap_direction, bool obey_snap)
4724 boost::shared_ptr<Region> region (rv.region());
4726 if (region->locked()) {
4730 nframes_t new_bound;
4733 TimeAxisView* tvp = clicked_axisview;
4734 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4736 if (tv && tv->is_track()) {
4737 speed = tv->get_diskstream()->speed();
4740 if (left_direction) {
4741 if (swap_direction) {
4742 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4744 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4747 if (swap_direction) {
4748 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4750 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4755 snap_to (new_bound);
4757 region->trim_start ((nframes_t) (new_bound * speed), this);
4758 rv.region_changed (StartChanged);
4762 Editor::single_start_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool obey_snap)
4764 boost::shared_ptr<Region> region (rv.region());
4766 if (region->locked()) {
4770 nframes_t new_bound;
4773 TimeAxisView* tvp = clicked_axisview;
4774 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4776 if (tv && tv->is_track()) {
4777 speed = tv->get_diskstream()->speed();
4780 if (left_direction) {
4781 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4783 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4787 snap_to (new_bound, (left_direction ? 0 : 1));
4790 region->trim_front ((nframes_t) (new_bound * speed), this);
4792 rv.region_changed (Change (LengthChanged|PositionChanged|StartChanged));
4796 Editor::single_end_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool obey_snap)
4798 boost::shared_ptr<Region> region (rv.region());
4800 if (region->locked()) {
4804 nframes_t new_bound;
4807 TimeAxisView* tvp = clicked_axisview;
4808 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4810 if (tv && tv->is_track()) {
4811 speed = tv->get_diskstream()->speed();
4814 if (left_direction) {
4815 new_bound = (nframes_t) ((region->last_frame() + 1)/speed) - frame_delta;
4817 new_bound = (nframes_t) ((region->last_frame() + 1)/speed) + frame_delta;
4821 snap_to (new_bound);
4823 region->trim_end ((nframes_t) (new_bound * speed), this);
4824 rv.region_changed (LengthChanged);
4828 Editor::trim_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
4830 if (!drag_info.first_move) {
4831 trim_motion_callback (item, event);
4833 if (!selection->selected (clicked_regionview)) {
4834 thaw_region_after_trim (*clicked_regionview);
4837 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4838 i != selection->regions.by_layer().end(); ++i)
4840 thaw_region_after_trim (**i);
4841 (*i)->fake_set_opaque (true);
4845 for (set<boost::shared_ptr<Playlist> >::iterator p = motion_frozen_playlists.begin(); p != motion_frozen_playlists.end(); ++p) {
4847 session->add_command (new MementoCommand<Playlist>(*(*p).get(), 0, &(*p)->get_state()));
4850 motion_frozen_playlists.clear ();
4852 commit_reversible_command();
4854 /* no mouse movement */
4860 Editor::point_trim (GdkEvent* event)
4862 RegionView* rv = clicked_regionview;
4863 nframes_t new_bound = drag_info.current_pointer_frame;
4865 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4866 snap_to (new_bound);
4869 /* Choose action dependant on which button was pressed */
4870 switch (event->button.button) {
4872 trim_op = StartTrim;
4873 begin_reversible_command (_("Start point trim"));
4875 if (selection->selected (rv)) {
4877 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4878 i != selection->regions.by_layer().end(); ++i)
4880 if (!(*i)->region()->locked()) {
4881 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4882 XMLNode &before = pl->get_state();
4883 (*i)->region()->trim_front (new_bound, this);
4884 XMLNode &after = pl->get_state();
4885 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4891 if (!rv->region()->locked()) {
4892 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
4893 XMLNode &before = pl->get_state();
4894 rv->region()->trim_front (new_bound, this);
4895 XMLNode &after = pl->get_state();
4896 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4900 commit_reversible_command();
4905 begin_reversible_command (_("End point trim"));
4907 if (selection->selected (rv)) {
4909 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
4911 if (!(*i)->region()->locked()) {
4912 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4913 XMLNode &before = pl->get_state();
4914 (*i)->region()->trim_end (new_bound, this);
4915 XMLNode &after = pl->get_state();
4916 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4922 if (!rv->region()->locked()) {
4923 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
4924 XMLNode &before = pl->get_state();
4925 rv->region()->trim_end (new_bound, this);
4926 XMLNode &after = pl->get_state();
4927 session->add_command (new MementoCommand<Playlist>(*pl.get(), &before, &after));
4931 commit_reversible_command();
4940 Editor::thaw_region_after_trim (RegionView& rv)
4942 boost::shared_ptr<Region> region (rv.region());
4944 if (region->locked()) {
4948 region->thaw (_("trimmed region"));
4949 XMLNode &after = region->playlist()->get_state();
4950 session->add_command (new MementoCommand<Playlist>(*(region->playlist()), 0, &after));
4952 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(&rv);
4954 arv->unhide_envelope ();
4958 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* event)
4963 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
4964 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
4968 Location* location = find_location_from_marker (marker, is_start);
4969 location->set_hidden (true, this);
4974 Editor::start_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event, RangeMarkerOp op)
4980 drag_info.item = item;
4981 drag_info.motion_callback = &Editor::drag_range_markerbar_op;
4982 drag_info.finished_callback = &Editor::end_range_markerbar_op;
4984 range_marker_op = op;
4986 if (!temp_location) {
4987 temp_location = new Location;
4991 case CreateRangeMarker:
4992 case CreateTransportMarker:
4993 case CreateCDMarker:
4995 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
4996 drag_info.copy = true;
4998 drag_info.copy = false;
5000 start_grab (event, selector_cursor);
5004 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
5009 Editor::drag_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
5011 nframes_t start = 0;
5013 ArdourCanvas::SimpleRect *crect;
5015 switch (range_marker_op) {
5016 case CreateRangeMarker:
5017 crect = range_bar_drag_rect;
5019 case CreateTransportMarker:
5020 crect = transport_bar_drag_rect;
5022 case CreateCDMarker:
5023 crect = cd_marker_bar_drag_rect;
5026 cerr << "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()" << endl;
5031 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
5032 snap_to (drag_info.current_pointer_frame);
5035 /* only alter selection if the current frame is
5036 different from the last frame position.
5039 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
5041 switch (range_marker_op) {
5042 case CreateRangeMarker:
5043 case CreateTransportMarker:
5044 case CreateCDMarker:
5045 if (drag_info.first_move) {
5046 snap_to (drag_info.grab_frame);
5049 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
5050 start = drag_info.current_pointer_frame;
5051 end = drag_info.grab_frame;
5053 end = drag_info.current_pointer_frame;
5054 start = drag_info.grab_frame;
5057 /* first drag: Either add to the selection
5058 or create a new selection.
5061 if (drag_info.first_move) {
5063 temp_location->set (start, end);
5067 update_marker_drag_item (temp_location);
5068 range_marker_drag_rect->show();
5069 range_marker_drag_rect->raise_to_top();
5075 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
5076 start_canvas_autoscroll (1, 0);
5080 temp_location->set (start, end);
5082 double x1 = frame_to_pixel (start);
5083 double x2 = frame_to_pixel (end);
5084 crect->property_x1() = x1;
5085 crect->property_x2() = x2;
5087 update_marker_drag_item (temp_location);
5090 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
5091 drag_info.first_move = false;
5093 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
5098 Editor::end_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
5100 Location * newloc = 0;
5104 if (!drag_info.first_move) {
5105 drag_range_markerbar_op (item, event);
5107 switch (range_marker_op) {
5108 case CreateRangeMarker:
5109 case CreateCDMarker:
5111 begin_reversible_command (_("new range marker"));
5112 XMLNode &before = session->locations()->get_state();
5113 session->locations()->next_available_name(rangename,"unnamed");
5114 if (range_marker_op == CreateCDMarker) {
5115 flags = Location::IsRangeMarker|Location::IsCDMarker;
5116 cd_marker_bar_drag_rect->hide();
5119 flags = Location::IsRangeMarker;
5120 range_bar_drag_rect->hide();
5122 newloc = new Location(temp_location->start(), temp_location->end(), rangename, (Location::Flags) flags);
5123 session->locations()->add (newloc, true);
5124 XMLNode &after = session->locations()->get_state();
5125 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
5126 commit_reversible_command ();
5128 range_marker_drag_rect->hide();
5132 case CreateTransportMarker:
5133 // popup menu to pick loop or punch
5134 new_transport_marker_context_menu (&event->button, item);
5139 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
5141 if (Keyboard::no_modifier_keys_pressed (&event->button) && range_marker_op != CreateCDMarker) {
5146 start = session->locations()->first_mark_before (drag_info.grab_frame);
5147 end = session->locations()->first_mark_after (drag_info.grab_frame);
5149 if (end == max_frames) {
5150 end = session->current_end_frame ();
5154 start = session->current_start_frame ();
5157 switch (mouse_mode) {
5159 /* find the two markers on either side and then make the selection from it */
5160 select_all_within (start, end, 0.0f, FLT_MAX, track_views, Selection::Set);
5164 /* find the two markers on either side of the click and make the range out of it */
5165 selection->set (0, start, end);
5174 stop_canvas_autoscroll ();
5180 Editor::start_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
5182 drag_info.item = item;
5183 drag_info.motion_callback = &Editor::drag_mouse_zoom;
5184 drag_info.finished_callback = &Editor::end_mouse_zoom;
5186 start_grab (event, zoom_cursor);
5188 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5192 Editor::drag_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
5197 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
5198 snap_to (drag_info.current_pointer_frame);
5200 if (drag_info.first_move) {
5201 snap_to (drag_info.grab_frame);
5205 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
5207 /* base start and end on initial click position */
5208 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
5209 start = drag_info.current_pointer_frame;
5210 end = drag_info.grab_frame;
5212 end = drag_info.current_pointer_frame;
5213 start = drag_info.grab_frame;
5218 if (drag_info.first_move) {
5220 zoom_rect->raise_to_top();
5223 reposition_zoom_rect(start, end);
5225 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
5226 drag_info.first_move = false;
5228 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5233 Editor::end_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
5235 if (!drag_info.first_move) {
5236 drag_mouse_zoom (item, event);
5238 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
5239 temporal_zoom_by_frame (drag_info.grab_frame, drag_info.last_pointer_frame, "mouse zoom");
5241 temporal_zoom_by_frame (drag_info.last_pointer_frame, drag_info.grab_frame, "mouse zoom");
5244 temporal_zoom_to_frame (false, drag_info.grab_frame);
5246 temporal_zoom_step (false);
5247 center_screen (drag_info.grab_frame);
5255 Editor::reposition_zoom_rect (nframes_t start, nframes_t end)
5257 double x1 = frame_to_pixel (start);
5258 double x2 = frame_to_pixel (end);
5259 double y2 = full_canvas_height - 1.0;
5261 zoom_rect->property_x1() = x1;
5262 zoom_rect->property_y1() = 1.0;
5263 zoom_rect->property_x2() = x2;
5264 zoom_rect->property_y2() = y2;
5268 Editor::start_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
5270 drag_info.item = item;
5271 drag_info.motion_callback = &Editor::drag_rubberband_select;
5272 drag_info.finished_callback = &Editor::end_rubberband_select;
5274 start_grab (event, cross_hair_cursor);
5276 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5280 Editor::drag_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
5287 /* use a bigger drag threshold than the default */
5289 if (abs ((int) (drag_info.current_pointer_frame - drag_info.grab_frame)) < 8) {
5293 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier()) && Config->get_rubberbanding_snaps_to_grid()) {
5294 if (drag_info.first_move) {
5295 snap_to (drag_info.grab_frame);
5297 snap_to (drag_info.current_pointer_frame);
5300 /* base start and end on initial click position */
5302 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
5303 start = drag_info.current_pointer_frame;
5304 end = drag_info.grab_frame;
5306 end = drag_info.current_pointer_frame;
5307 start = drag_info.grab_frame;
5310 if (drag_info.current_pointer_y < drag_info.grab_y) {
5311 y1 = drag_info.current_pointer_y;
5312 y2 = drag_info.grab_y;
5314 y2 = drag_info.current_pointer_y;
5315 y1 = drag_info.grab_y;
5319 if (start != end || y1 != y2) {
5321 double x1 = frame_to_pixel (start);
5322 double x2 = frame_to_pixel (end);
5324 rubberband_rect->property_x1() = x1;
5325 rubberband_rect->property_y1() = y1;
5326 rubberband_rect->property_x2() = x2;
5327 rubberband_rect->property_y2() = y2;
5329 rubberband_rect->show();
5330 rubberband_rect->raise_to_top();
5332 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
5333 drag_info.first_move = false;
5335 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5340 Editor::end_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
5342 if (!drag_info.first_move) {
5344 drag_rubberband_select (item, event);
5347 if (drag_info.current_pointer_y < drag_info.grab_y) {
5348 y1 = drag_info.current_pointer_y;
5349 y2 = drag_info.grab_y;
5352 y2 = drag_info.current_pointer_y;
5353 y1 = drag_info.grab_y;
5357 Selection::Operation op = Keyboard::selection_type (event->button.state);
5360 begin_reversible_command (_("rubberband selection"));
5362 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
5363 commit = select_all_within (drag_info.grab_frame, drag_info.last_pointer_frame, y1, y2, track_views, op);
5365 commit = select_all_within (drag_info.last_pointer_frame, drag_info.grab_frame, y1, y2, track_views, op);
5369 commit_reversible_command ();
5373 selection->clear_tracks();
5374 selection->clear_regions();
5375 selection->clear_points ();
5376 selection->clear_lines ();
5379 rubberband_rect->hide();
5384 Editor::mouse_rename_region (ArdourCanvas::Item* item, GdkEvent* event)
5386 using namespace Gtkmm2ext;
5388 ArdourPrompter prompter (false);
5390 prompter.set_prompt (_("Name for region:"));
5391 prompter.set_initial_text (clicked_regionview->region()->name());
5392 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
5393 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
5394 prompter.show_all ();
5395 switch (prompter.run ()) {
5396 case Gtk::RESPONSE_ACCEPT:
5398 prompter.get_result(str);
5400 clicked_regionview->region()->set_name (str);
5408 Editor::start_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
5410 drag_info.item = item;
5411 drag_info.motion_callback = &Editor::time_fx_motion;
5412 drag_info.finished_callback = &Editor::end_time_fx;
5416 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5420 Editor::time_fx_motion (ArdourCanvas::Item *item, GdkEvent* event)
5422 RegionView* rv = clicked_regionview;
5424 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
5425 snap_to (drag_info.current_pointer_frame);
5428 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
5432 if (drag_info.current_pointer_frame > rv->region()->position()) {
5433 rv->get_time_axis_view().show_timestretch (rv->region()->position(), drag_info.current_pointer_frame);
5436 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
5437 drag_info.first_move = false;
5439 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5443 Editor::end_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
5445 clicked_regionview->get_time_axis_view().hide_timestretch ();
5447 if (drag_info.first_move) {
5451 if (drag_info.last_pointer_frame < clicked_regionview->region()->position()) {
5452 /* backwards drag of the left edge - not usable */
5456 nframes_t newlen = drag_info.last_pointer_frame - clicked_regionview->region()->position();
5458 float percentage = (double) newlen / (double) clicked_regionview->region()->length();
5460 #ifndef USE_RUBBERBAND
5461 // Soundtouch uses percentage / 100 instead of normal (/ 1)
5462 if (clicked_regionview->region()->data_type() == DataType::AUDIO) {
5463 percentage = (float) ((double) newlen - (double) clicked_regionview->region()->length()) / ((double) newlen) * 100.0f;
5467 begin_reversible_command (_("timestretch"));
5469 // XXX how do timeFX on multiple regions ?
5472 rs.add (clicked_regionview);
5474 if (time_stretch (rs, percentage) == 0) {
5475 session->commit_reversible_command ();
5480 Editor::mouse_brush_insert_region (RegionView* rv, nframes_t pos)
5482 /* no brushing without a useful snap setting */
5485 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
5488 switch (snap_mode) {
5490 return; /* can't work because it allows region to be placed anywhere */
5495 switch (snap_type) {
5503 /* don't brush a copy over the original */
5505 if (pos == rv->region()->position()) {
5509 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&arv->get_time_axis_view());
5511 if (rtv == 0 || !rtv->is_track()) {
5515 boost::shared_ptr<Playlist> playlist = rtv->playlist();
5516 double speed = rtv->get_diskstream()->speed();
5518 XMLNode &before = playlist->get_state();
5519 playlist->add_region (boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (arv->audio_region())), (nframes_t) (pos * speed));
5520 XMLNode &after = playlist->get_state();
5521 session->add_command(new MementoCommand<Playlist>(*playlist.get(), &before, &after));
5523 // playlist is frozen, so we have to update manually
5525 playlist->Modified(); /* EMIT SIGNAL */
5529 Editor::track_height_step_timeout ()
5532 struct timeval delta;
5534 gettimeofday (&now, 0);
5535 timersub (&now, &last_track_height_step_timestamp, &delta);
5537 if (delta.tv_sec * 1000000 + delta.tv_usec > 250000) { /* milliseconds */
5538 current_stepping_trackview = 0;