3 Copyright (C) 2000-2001 Paul Davis
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
29 #include <pbd/error.h>
30 #include <gtkmm2ext/utils.h>
31 #include <gtkmm2ext/tearoff.h>
32 #include <pbd/memento_command.h>
33 #include <pbd/basename.h>
35 #include "ardour_ui.h"
37 #include "time_axis_view.h"
38 #include "audio_time_axis.h"
39 #include "audio_region_view.h"
40 #include "midi_region_view.h"
42 #include "streamview.h"
43 #include "region_gain_line.h"
44 #include "automation_time_axis.h"
45 #include "control_point.h"
48 #include "selection.h"
51 #include "rgb_macros.h"
53 #include <ardour/types.h>
54 #include <ardour/profile.h>
55 #include <ardour/route.h>
56 #include <ardour/audio_track.h>
57 #include <ardour/audio_diskstream.h>
58 #include <ardour/midi_diskstream.h>
59 #include <ardour/playlist.h>
60 #include <ardour/audioplaylist.h>
61 #include <ardour/audioregion.h>
62 #include <ardour/midi_region.h>
63 #include <ardour/dB.h>
64 #include <ardour/utils.h>
65 #include <ardour/region_factory.h>
66 #include <ardour/source_factory.h>
73 using namespace ARDOUR;
77 using namespace Editing;
79 const static double ZERO_GAIN_FRACTION = gain_to_slider_position(dB_to_coefficient(0.0));
82 Editor::mouse_frame (nframes64_t& where, bool& in_track_canvas) const
86 Gdk::ModifierType mask;
87 Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->track_canvas->get_window();
88 Glib::RefPtr<const Gdk::Window> pointer_window;
94 pointer_window = canvas_window->get_pointer (x, y, mask);
96 if (pointer_window == track_canvas->get_bin_window()) {
99 in_track_canvas = true;
102 in_track_canvas = false;
107 event.type = GDK_BUTTON_RELEASE;
111 where = event_frame (&event, 0, 0);
116 Editor::event_frame (GdkEvent* event, double* pcx, double* pcy) const
130 switch (event->type) {
131 case GDK_BUTTON_RELEASE:
132 case GDK_BUTTON_PRESS:
133 case GDK_2BUTTON_PRESS:
134 case GDK_3BUTTON_PRESS:
136 *pcx = event->button.x;
137 *pcy = event->button.y;
138 _trackview_group->w2i(*pcx, *pcy);
140 case GDK_MOTION_NOTIFY:
142 *pcx = event->motion.x;
143 *pcy = event->motion.y;
144 _trackview_group->w2i(*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;
232 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 (midi_tools_tearoff) {
353 if (mouse_mode == MouseNote) {
354 midi_tools_tearoff->show();
356 midi_tools_tearoff->hide();
360 ignore_mouse_mode_toggle = false;
362 set_canvas_cursor ();
366 Editor::step_mouse_mode (bool next)
368 switch (current_mouse_mode()) {
370 if (next) set_mouse_mode (MouseRange);
371 else set_mouse_mode (MouseTimeFX);
375 if (next) set_mouse_mode (MouseZoom);
376 else set_mouse_mode (MouseObject);
380 if (next) set_mouse_mode (MouseGain);
381 else set_mouse_mode (MouseRange);
385 if (next) set_mouse_mode (MouseTimeFX);
386 else set_mouse_mode (MouseZoom);
390 if (next) set_mouse_mode (MouseAudition);
391 else set_mouse_mode (MouseGain);
395 if (next) set_mouse_mode (MouseObject);
396 else set_mouse_mode (MouseTimeFX);
400 if (next) set_mouse_mode (MouseObject);
401 else set_mouse_mode (MouseAudition);
407 Editor::midi_edit_mode_toggled (MidiEditMode m)
409 if (ignore_midi_edit_mode_toggle) {
415 if (midi_tool_pencil_button.get_active())
416 set_midi_edit_mode (m);
420 if (midi_tool_select_button.get_active())
421 set_midi_edit_mode (m);
425 if (midi_tool_resize_button.get_active())
426 set_midi_edit_mode (m);
430 if (midi_tool_erase_button.get_active())
431 set_midi_edit_mode (m);
438 set_midi_edit_cursor(m);
443 Editor::set_midi_edit_mode (MidiEditMode m, bool force)
445 if (drag_info.item) {
449 if (!force && m == midi_edit_mode) {
457 ignore_midi_edit_mode_toggle = true;
459 switch (midi_edit_mode) {
461 midi_tool_pencil_button.set_active (true);
465 midi_tool_select_button.set_active (true);
469 midi_tool_resize_button.set_active (true);
473 midi_tool_erase_button.set_active (true);
477 ignore_midi_edit_mode_toggle = false;
479 set_midi_edit_cursor (current_midi_edit_mode());
482 track_canvas->get_window()->set_cursor(*current_canvas_cursor);
487 Editor::set_midi_edit_cursor (MidiEditMode m)
489 switch (midi_edit_mode) {
491 current_canvas_cursor = midi_pencil_cursor;
495 current_canvas_cursor = midi_select_cursor;
499 current_canvas_cursor = midi_resize_cursor;
503 current_canvas_cursor = midi_erase_cursor;
509 Editor::button_selection (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
511 /* in object/audition/timefx mode, any button press sets
512 the selection if the object can be selected. this is a
513 bit of hack, because we want to avoid this if the
514 mouse operation is a region alignment.
516 note: not dbl-click or triple-click
519 if (((mouse_mode != MouseObject) &&
520 (mouse_mode != MouseAudition || item_type != RegionItem) &&
521 (mouse_mode != MouseTimeFX || item_type != RegionItem) &&
522 (mouse_mode != MouseRange)) ||
524 ((event->type != GDK_BUTTON_PRESS && event->type != GDK_BUTTON_RELEASE) || event->button.button > 3)) {
529 if (event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) {
531 if ((event->button.state & Keyboard::RelevantModifierKeyMask) && event->button.button != 1) {
533 /* almost no selection action on modified button-2 or button-3 events */
535 if (item_type != RegionItem && event->button.button != 2) {
541 Selection::Operation op = Keyboard::selection_type (event->button.state);
542 bool press = (event->type == GDK_BUTTON_PRESS);
544 // begin_reversible_command (_("select on click"));
548 if (mouse_mode != MouseRange) {
549 set_selected_regionview_from_click (press, op, true);
550 } else if (event->type == GDK_BUTTON_PRESS) {
551 set_selected_track_as_side_effect ();
555 case RegionViewNameHighlight:
557 if (mouse_mode != MouseRange) {
558 set_selected_regionview_from_click (press, op, true);
559 } else if (event->type == GDK_BUTTON_PRESS) {
560 set_selected_track_as_side_effect ();
565 case FadeInHandleItem:
567 case FadeOutHandleItem:
569 if (mouse_mode != MouseRange) {
570 set_selected_regionview_from_click (press, op, true);
571 } else if (event->type == GDK_BUTTON_PRESS) {
572 set_selected_track_as_side_effect ();
576 case ControlPointItem:
577 set_selected_track_as_side_effect ();
578 if (mouse_mode != MouseRange) {
579 set_selected_control_point_from_click (op, false);
584 /* for context click or range selection, select track */
585 if (event->button.button == 3) {
586 set_selected_track_as_side_effect ();
587 } else if (event->type == GDK_BUTTON_PRESS && mouse_mode == MouseRange) {
588 set_selected_track_as_side_effect ();
592 case AutomationTrackItem:
593 set_selected_track_as_side_effect (true);
602 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
604 Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->track_canvas->get_window();
607 Glib::RefPtr<const Gdk::Window> pointer_window;
610 Gdk::ModifierType mask;
612 pointer_window = canvas_window->get_pointer (x, y, mask);
614 if (pointer_window == track_canvas->get_bin_window()) {
615 track_canvas->window_to_world (x, y, wx, wy);
616 allow_vertical_scroll = true;
618 allow_vertical_scroll = false;
622 track_canvas->grab_focus();
624 if (session && session->actively_recording()) {
628 button_selection (item, event, item_type);
630 if (drag_info.item == 0 &&
631 (Keyboard::is_delete_event (&event->button) ||
632 Keyboard::is_context_menu_event (&event->button) ||
633 Keyboard::is_edit_event (&event->button))) {
635 /* handled by button release */
639 switch (event->button.button) {
642 if (event->type == GDK_BUTTON_PRESS) {
644 if (drag_info.item) {
645 drag_info.item->ungrab (event->button.time);
648 /* single mouse clicks on any of these item types operate
649 independent of mouse mode, mostly because they are
650 not on the main track canvas or because we want
655 case PlayheadCursorItem:
656 start_cursor_grab (item, event);
660 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
661 hide_marker (item, event);
663 start_marker_grab (item, event);
667 case TempoMarkerItem:
668 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
669 start_tempo_marker_copy_grab (item, event);
671 start_tempo_marker_grab (item, event);
675 case MeterMarkerItem:
676 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
677 start_meter_marker_copy_grab (item, event);
679 start_meter_marker_grab (item, event);
689 case RangeMarkerBarItem:
690 start_range_markerbar_op (item, event, CreateRangeMarker);
694 case CdMarkerBarItem:
695 start_range_markerbar_op (item, event, CreateCDMarker);
699 case TransportMarkerBarItem:
700 start_range_markerbar_op (item, event, CreateTransportMarker);
709 switch (mouse_mode) {
712 case StartSelectionTrimItem:
713 start_selection_op (item, event, SelectionStartTrim);
716 case EndSelectionTrimItem:
717 start_selection_op (item, event, SelectionEndTrim);
721 if (Keyboard::modifier_state_contains
722 (event->button.state, Keyboard::ModifierMask(Keyboard::SecondaryModifier))) {
723 // contains and not equals because I can't use alt as a modifier alone.
724 start_selection_grab (item, event);
725 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
726 /* grab selection for moving */
727 start_selection_op (item, event, SelectionMove);
729 /* this was debated, but decided the more common action was to
730 make a new selection */
731 start_selection_op (item, event, CreateSelection);
736 start_selection_op (item, event, CreateSelection);
742 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)) &&
743 event->type == GDK_BUTTON_PRESS) {
745 start_rubberband_select (item, event);
747 } else if (event->type == GDK_BUTTON_PRESS) {
750 case FadeInHandleItem:
751 start_fade_in_grab (item, event);
754 case FadeOutHandleItem:
755 start_fade_out_grab (item, event);
759 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
760 start_region_copy_grab (item, event);
761 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
762 start_region_brush_grab (item, event);
764 start_region_grab (item, event);
768 case RegionViewNameHighlight:
769 start_trim (item, event);
774 /* rename happens on edit clicks */
775 start_trim (clicked_regionview->get_name_highlight(), event);
779 case ControlPointItem:
780 start_control_point_grab (item, event);
784 case AutomationLineItem:
785 start_line_grab_from_line (item, event);
790 case AutomationTrackItem:
791 start_rubberband_select (item, event);
795 case ImageFrameHandleStartItem:
796 imageframe_start_handle_op(item, event) ;
799 case ImageFrameHandleEndItem:
800 imageframe_end_handle_op(item, event) ;
803 case MarkerViewHandleStartItem:
804 markerview_item_start_handle_op(item, event) ;
807 case MarkerViewHandleEndItem:
808 markerview_item_end_handle_op(item, event) ;
812 start_markerview_grab(item, event) ;
815 start_imageframe_grab(item, event) ;
833 // start_line_grab_from_regionview (item, event);
837 start_line_grab_from_line (item, event);
840 case ControlPointItem:
841 start_control_point_grab (item, event);
852 case ControlPointItem:
853 start_control_point_grab (item, event);
856 case AutomationLineItem:
857 start_line_grab_from_line (item, event);
861 // XXX need automation mode to identify which
863 // start_line_grab_from_regionview (item, event);
873 if (event->type == GDK_BUTTON_PRESS) {
874 start_mouse_zoom (item, event);
881 if (item_type == RegionItem) {
882 start_time_fx (item, event);
889 scrub_reverse_distance = 0;
890 last_scrub_x = event->button.x;
891 scrubbing_direction = 0;
892 track_canvas->get_window()->set_cursor (*transparent_cursor);
893 /* rest handled in motion & release */
897 start_create_region_grab (item, event);
906 switch (mouse_mode) {
908 if (event->type == GDK_BUTTON_PRESS) {
911 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
912 start_region_copy_grab (item, event);
914 start_region_grab (item, event);
918 case ControlPointItem:
919 start_control_point_grab (item, event);
930 case RegionViewNameHighlight:
931 start_trim (item, event);
936 start_trim (clicked_regionview->get_name_highlight(), event);
947 if (event->type == GDK_BUTTON_PRESS) {
948 /* relax till release */
955 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
956 temporal_zoom_session();
958 temporal_zoom_to_frame (true, event_frame(event));
981 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
983 nframes64_t where = event_frame (event, 0, 0);
984 AutomationTimeAxisView* atv = 0;
986 /* no action if we're recording */
988 if (session && session->actively_recording()) {
992 /* first, see if we're finishing a drag ... */
994 if (drag_info.item) {
995 if (end_grab (item, event)) {
996 /* grab dragged, so do nothing else */
1001 button_selection (item, event, item_type);
1003 /* edit events get handled here */
1005 if (drag_info.item == 0 && Keyboard::is_edit_event (&event->button)) {
1006 switch (item_type) {
1011 case TempoMarkerItem:
1012 edit_tempo_marker (item);
1015 case MeterMarkerItem:
1016 edit_meter_marker (item);
1019 case RegionViewName:
1020 if (clicked_regionview->name_active()) {
1021 return mouse_rename_region (item, event);
1031 /* context menu events get handled here */
1033 if (Keyboard::is_context_menu_event (&event->button)) {
1035 if (drag_info.item == 0) {
1037 /* no matter which button pops up the context menu, tell the menu
1038 widget to use button 1 to drive menu selection.
1041 switch (item_type) {
1043 case FadeInHandleItem:
1045 case FadeOutHandleItem:
1046 popup_fade_context_menu (1, event->button.time, item, item_type);
1050 popup_track_context_menu (1, event->button.time, item_type, false, where);
1054 case RegionViewNameHighlight:
1055 case RegionViewName:
1056 popup_track_context_menu (1, event->button.time, item_type, false, where);
1060 popup_track_context_menu (1, event->button.time, item_type, true, where);
1063 case AutomationTrackItem:
1064 popup_track_context_menu (1, event->button.time, item_type, false, where);
1068 case RangeMarkerBarItem:
1069 case TransportMarkerBarItem:
1070 case CdMarkerBarItem:
1073 popup_ruler_menu (pixel_to_frame(event->button.x), item_type);
1077 marker_context_menu (&event->button, item);
1080 case TempoMarkerItem:
1081 tm_marker_context_menu (&event->button, item);
1084 case MeterMarkerItem:
1085 tm_marker_context_menu (&event->button, item);
1088 case CrossfadeViewItem:
1089 popup_track_context_menu (1, event->button.time, item_type, false, where);
1093 case ImageFrameItem:
1094 popup_imageframe_edit_menu(1, event->button.time, item, true) ;
1096 case ImageFrameTimeAxisItem:
1097 popup_imageframe_edit_menu(1, event->button.time, item, false) ;
1099 case MarkerViewItem:
1100 popup_marker_time_axis_edit_menu(1, event->button.time, item, true) ;
1102 case MarkerTimeAxisItem:
1103 popup_marker_time_axis_edit_menu(1, event->button.time, item, false) ;
1115 /* delete events get handled here */
1117 if (drag_info.item == 0 && Keyboard::is_delete_event (&event->button)) {
1119 switch (item_type) {
1120 case TempoMarkerItem:
1121 remove_tempo_marker (item);
1124 case MeterMarkerItem:
1125 remove_meter_marker (item);
1129 remove_marker (*item, event);
1133 if (mouse_mode == MouseObject) {
1134 remove_clicked_region ();
1138 case ControlPointItem:
1139 if (mouse_mode == MouseGain) {
1140 remove_gain_control_point (item, event);
1142 remove_control_point (item, event);
1152 switch (event->button.button) {
1155 switch (item_type) {
1156 /* see comments in button_press_handler */
1157 case PlayheadCursorItem:
1160 case AutomationLineItem:
1161 case StartSelectionTrimItem:
1162 case EndSelectionTrimItem:
1166 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1167 snap_to (where, 0, true);
1169 mouse_add_new_marker (where);
1172 case CdMarkerBarItem:
1173 // if we get here then a dragged range wasn't done
1174 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1175 snap_to (where, 0, true);
1177 mouse_add_new_marker (where, true);
1181 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1184 mouse_add_new_tempo_event (where);
1188 mouse_add_new_meter_event (pixel_to_frame (event->button.x));
1196 switch (mouse_mode) {
1198 switch (item_type) {
1199 case AutomationTrackItem:
1200 atv = dynamic_cast<AutomationTimeAxisView*>(clicked_routeview);
1202 atv->add_automation_event (item, event, where, event->button.y);
1214 // Gain only makes sense for audio regions
1216 if (!dynamic_cast<AudioRegionView*>(clicked_regionview)) {
1220 switch (item_type) {
1222 dynamic_cast<AudioRegionView*>(clicked_regionview)->add_gain_point_event (item, event);
1226 case AutomationTrackItem:
1227 dynamic_cast<AutomationTimeAxisView*>(clicked_axisview)->
1228 add_automation_event (item, event, where, event->button.y);
1238 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1239 if (scrubbing_direction == 0) {
1240 /* no drag, just a click */
1241 switch (item_type) {
1243 play_selected_region ();
1249 /* make sure we stop */
1250 session->request_transport_speed (0.0);
1264 switch (mouse_mode) {
1267 switch (item_type) {
1269 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1271 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier|Keyboard::SecondaryModifier))) {
1274 // Button2 click is unused
1287 // x_style_paste (where, 1.0);
1307 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1313 if (last_item_entered != item) {
1314 last_item_entered = item;
1315 last_item_entered_n = 0;
1318 switch (item_type) {
1319 case ControlPointItem:
1320 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1321 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1322 cp->set_visible (true);
1326 at_y = cp->get_y ();
1327 cp->item()->i2w (at_x, at_y);
1331 fraction = 1.0 - (cp->get_y() / cp->line().height());
1333 if (is_drawable() && !_scrubbing) {
1334 track_canvas->get_window()->set_cursor (*fader_cursor);
1337 last_item_entered_n++;
1338 set_verbose_canvas_cursor (cp->line().get_verbose_cursor_string (fraction), at_x, at_y);
1339 if (last_item_entered_n < 10) {
1340 show_verbose_canvas_cursor ();
1346 if (mouse_mode == MouseGain) {
1347 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1349 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredGainLine.get();
1350 if (is_drawable()) {
1351 track_canvas->get_window()->set_cursor (*fader_cursor);
1356 case AutomationLineItem:
1357 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1359 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1361 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredAutomationLine.get();
1363 if (is_drawable()) {
1364 track_canvas->get_window()->set_cursor (*fader_cursor);
1369 case RegionViewNameHighlight:
1370 if (is_drawable() && mouse_mode == MouseObject) {
1371 track_canvas->get_window()->set_cursor (*trimmer_cursor);
1375 case StartSelectionTrimItem:
1376 case EndSelectionTrimItem:
1379 case ImageFrameHandleStartItem:
1380 case ImageFrameHandleEndItem:
1381 case MarkerViewHandleStartItem:
1382 case MarkerViewHandleEndItem:
1385 if (is_drawable()) {
1386 track_canvas->get_window()->set_cursor (*trimmer_cursor);
1390 case PlayheadCursorItem:
1391 if (is_drawable()) {
1392 switch (_edit_point) {
1394 track_canvas->get_window()->set_cursor (*grabber_edit_point_cursor);
1397 track_canvas->get_window()->set_cursor (*grabber_cursor);
1403 case RegionViewName:
1405 /* when the name is not an active item, the entire name highlight is for trimming */
1407 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1408 if (mouse_mode == MouseObject && is_drawable()) {
1409 track_canvas->get_window()->set_cursor (*trimmer_cursor);
1415 case AutomationTrackItem:
1416 if (is_drawable()) {
1417 Gdk::Cursor *cursor;
1418 switch (mouse_mode) {
1420 cursor = selector_cursor;
1423 cursor = zoom_cursor;
1426 cursor = cross_hair_cursor;
1430 track_canvas->get_window()->set_cursor (*cursor);
1432 AutomationTimeAxisView* atv;
1433 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1434 clear_entered_track = false;
1435 set_entered_track (atv);
1441 case RangeMarkerBarItem:
1442 case TransportMarkerBarItem:
1443 case CdMarkerBarItem:
1446 if (is_drawable()) {
1447 track_canvas->get_window()->set_cursor (*timebar_cursor);
1452 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1455 entered_marker = marker;
1456 marker->set_color_rgba (ARDOUR_UI::config()->canvasvar_EnteredMarker.get());
1458 case MeterMarkerItem:
1459 case TempoMarkerItem:
1460 if (is_drawable()) {
1461 track_canvas->get_window()->set_cursor (*timebar_cursor);
1464 case FadeInHandleItem:
1465 case FadeOutHandleItem:
1466 if (mouse_mode == MouseObject) {
1467 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1469 rect->property_fill_color_rgba() = 0;
1470 rect->property_outline_pixels() = 1;
1479 /* second pass to handle entered track status in a comprehensible way.
1482 switch (item_type) {
1484 case AutomationLineItem:
1485 case ControlPointItem:
1486 /* these do not affect the current entered track state */
1487 clear_entered_track = false;
1490 case AutomationTrackItem:
1491 /* handled above already */
1495 set_entered_track (0);
1503 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1512 switch (item_type) {
1513 case ControlPointItem:
1514 cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
1515 if (cp->line().the_list()->interpolation() != AutomationList::Discrete) {
1516 if (cp->line().npoints() > 1 && !cp->selected()) {
1517 cp->set_visible (false);
1521 if (is_drawable()) {
1522 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1525 hide_verbose_canvas_cursor ();
1528 case RegionViewNameHighlight:
1529 case StartSelectionTrimItem:
1530 case EndSelectionTrimItem:
1531 case PlayheadCursorItem:
1534 case ImageFrameHandleStartItem:
1535 case ImageFrameHandleEndItem:
1536 case MarkerViewHandleStartItem:
1537 case MarkerViewHandleEndItem:
1540 if (is_drawable()) {
1541 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1546 case AutomationLineItem:
1547 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1549 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1551 line->property_fill_color_rgba() = al->get_line_color();
1553 if (is_drawable()) {
1554 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1558 case RegionViewName:
1559 /* see enter_handler() for notes */
1560 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1561 if (is_drawable() && mouse_mode == MouseObject) {
1562 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1567 case RangeMarkerBarItem:
1568 case TransportMarkerBarItem:
1569 case CdMarkerBarItem:
1573 if (is_drawable()) {
1574 track_canvas->get_window()->set_cursor (*timebar_cursor);
1579 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1583 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
1584 location_flags_changed (loc, this);
1587 case MeterMarkerItem:
1588 case TempoMarkerItem:
1590 if (is_drawable()) {
1591 track_canvas->get_window()->set_cursor (*timebar_cursor);
1596 case FadeInHandleItem:
1597 case FadeOutHandleItem:
1598 rv = static_cast<RegionView*>(item->get_data ("regionview"));
1600 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1602 rect->property_fill_color_rgba() = rv->get_fill_color();
1603 rect->property_outline_pixels() = 0;
1608 case AutomationTrackItem:
1609 if (is_drawable()) {
1610 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1611 clear_entered_track = true;
1612 Glib::signal_idle().connect (mem_fun(*this, &Editor::left_automation_track));
1624 Editor::left_automation_track ()
1626 if (clear_entered_track) {
1627 set_entered_track (0);
1628 clear_entered_track = false;
1638 if (scrubbing_direction == 0) {
1640 session->request_locate (drag_info.current_pointer_frame, false);
1641 session->request_transport_speed (0.1);
1642 scrubbing_direction = 1;
1646 if (last_scrub_x > drag_info.current_pointer_x) {
1648 /* pointer moved to the left */
1650 if (scrubbing_direction > 0) {
1652 /* we reversed direction to go backwards */
1655 scrub_reverse_distance += (int) (last_scrub_x - drag_info.current_pointer_x);
1659 /* still moving to the left (backwards) */
1661 scrub_reversals = 0;
1662 scrub_reverse_distance = 0;
1664 delta = 0.01 * (last_scrub_x - drag_info.current_pointer_x);
1665 session->request_transport_speed (session->transport_speed() - delta);
1669 /* pointer moved to the right */
1671 if (scrubbing_direction < 0) {
1672 /* we reversed direction to go forward */
1675 scrub_reverse_distance += (int) (drag_info.current_pointer_x - last_scrub_x);
1678 /* still moving to the right */
1680 scrub_reversals = 0;
1681 scrub_reverse_distance = 0;
1683 delta = 0.01 * (drag_info.current_pointer_x - last_scrub_x);
1684 session->request_transport_speed (session->transport_speed() + delta);
1688 /* if there have been more than 2 opposite motion moves detected, or one that moves
1689 back more than 10 pixels, reverse direction
1692 if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
1694 if (scrubbing_direction > 0) {
1695 /* was forwards, go backwards */
1696 session->request_transport_speed (-0.1);
1697 scrubbing_direction = -1;
1699 /* was backwards, go forwards */
1700 session->request_transport_speed (0.1);
1701 scrubbing_direction = 1;
1704 scrub_reverse_distance = 0;
1705 scrub_reversals = 0;
1709 last_scrub_x = drag_info.current_pointer_x;
1713 Editor::motion_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type, bool from_autoscroll)
1715 if (event->motion.is_hint) {
1718 /* We call this so that MOTION_NOTIFY events continue to be
1719 delivered to the canvas. We need to do this because we set
1720 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
1721 the density of the events, at the expense of a round-trip
1722 to the server. Given that this will mostly occur on cases
1723 where DISPLAY = :0.0, and given the cost of what the motion
1724 event might do, its a good tradeoff.
1727 track_canvas->get_pointer (x, y);
1730 if (current_stepping_trackview) {
1731 /* don't keep the persistent stepped trackview if the mouse moves */
1732 current_stepping_trackview = 0;
1733 step_timeout.disconnect ();
1736 if (session && session->actively_recording()) {
1737 /* Sorry. no dragging stuff around while we record */
1741 drag_info.item_type = item_type;
1742 drag_info.last_pointer_x = drag_info.current_pointer_x;
1743 drag_info.last_pointer_y = drag_info.current_pointer_y;
1744 drag_info.current_pointer_frame = event_frame (event, &drag_info.current_pointer_x,
1745 &drag_info.current_pointer_y);
1748 switch (mouse_mode) {
1760 if (!from_autoscroll && drag_info.item) {
1761 /* item != 0 is the best test i can think of for dragging.
1763 if (!drag_info.move_threshold_passed) {
1765 bool x_threshold_passed = (::llabs ((nframes64_t) (drag_info.current_pointer_x - drag_info.grab_x)) > 4LL);
1766 bool y_threshold_passed = (::llabs ((nframes64_t) (drag_info.current_pointer_y - drag_info.grab_y)) > 4LL);
1768 drag_info.move_threshold_passed = (x_threshold_passed || y_threshold_passed);
1770 // and change the initial grab loc/frame if this drag info wants us to
1772 if (drag_info.want_move_threshold && drag_info.move_threshold_passed) {
1773 drag_info.grab_frame = drag_info.current_pointer_frame;
1774 drag_info.grab_x = drag_info.current_pointer_x;
1775 drag_info.grab_y = drag_info.current_pointer_y;
1776 drag_info.last_pointer_frame = drag_info.grab_frame;
1777 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
1782 switch (item_type) {
1783 case PlayheadCursorItem:
1785 case ControlPointItem:
1786 case RangeMarkerBarItem:
1787 case TransportMarkerBarItem:
1788 case CdMarkerBarItem:
1789 case TempoMarkerItem:
1790 case MeterMarkerItem:
1791 case RegionViewNameHighlight:
1792 case StartSelectionTrimItem:
1793 case EndSelectionTrimItem:
1796 case AutomationLineItem:
1797 case FadeInHandleItem:
1798 case FadeOutHandleItem:
1801 case ImageFrameHandleStartItem:
1802 case ImageFrameHandleEndItem:
1803 case MarkerViewHandleStartItem:
1804 case MarkerViewHandleEndItem:
1807 if (drag_info.item && (event->motion.state & Gdk::BUTTON1_MASK ||
1808 (event->motion.state & Gdk::BUTTON2_MASK))) {
1809 if (!from_autoscroll) {
1810 maybe_autoscroll_horizontally (&event->motion);
1812 (this->*(drag_info.motion_callback)) (item, event);
1821 switch (mouse_mode) {
1827 if (drag_info.item && (event->motion.state & GDK_BUTTON1_MASK ||
1828 (event->motion.state & GDK_BUTTON2_MASK))) {
1829 if (!from_autoscroll) {
1830 maybe_autoscroll (&event->motion);
1832 (this->*(drag_info.motion_callback)) (item, event);
1843 track_canvas_motion (event);
1844 // drag_info.last_pointer_frame = drag_info.current_pointer_frame;
1852 Editor::break_drag ()
1854 stop_canvas_autoscroll ();
1855 hide_verbose_canvas_cursor ();
1857 if (drag_info.item) {
1858 drag_info.item->ungrab (0);
1860 /* put it back where it came from */
1865 drag_info.item->i2w (cxw, cyw);
1866 drag_info.item->move (drag_info.original_x - cxw, drag_info.original_y - cyw);
1873 Editor::finalize_drag ()
1876 drag_info.copy = false;
1877 drag_info.motion_callback = 0;
1878 drag_info.finished_callback = 0;
1879 drag_info.dest_trackview = 0;
1880 drag_info.source_trackview = 0;
1881 drag_info.last_frame_position = 0;
1882 drag_info.grab_frame = 0;
1883 drag_info.last_pointer_frame = 0;
1884 drag_info.current_pointer_frame = 0;
1885 drag_info.brushing = false;
1886 range_marker_drag_rect->hide();
1887 drag_info.clear_copied_locations ();
1891 Editor::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
1893 if (drag_info.item == 0) {
1894 fatal << _("programming error: start_grab called without drag item") << endmsg;
1900 cursor = which_grabber_cursor ();
1903 // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
1905 if (event->button.button == 2) {
1906 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
1907 drag_info.y_constrained = true;
1908 drag_info.x_constrained = false;
1910 drag_info.y_constrained = false;
1911 drag_info.x_constrained = true;
1914 drag_info.x_constrained = false;
1915 drag_info.y_constrained = false;
1918 drag_info.grab_frame = event_frame (event, &drag_info.grab_x, &drag_info.grab_y);
1919 drag_info.last_pointer_frame = drag_info.grab_frame;
1920 drag_info.current_pointer_frame = drag_info.grab_frame;
1921 drag_info.current_pointer_x = drag_info.grab_x;
1922 drag_info.current_pointer_y = drag_info.grab_y;
1923 drag_info.last_pointer_x = drag_info.current_pointer_x;
1924 drag_info.last_pointer_y = drag_info.current_pointer_y;
1925 drag_info.cumulative_x_drag = 0;
1926 drag_info.cumulative_y_drag = 0;
1927 drag_info.first_move = true;
1928 drag_info.move_threshold_passed = false;
1929 drag_info.want_move_threshold = false;
1930 drag_info.pointer_frame_offset = 0;
1931 drag_info.brushing = false;
1932 drag_info.clear_copied_locations ();
1934 drag_info.original_x = 0;
1935 drag_info.original_y = 0;
1936 drag_info.item->i2w (drag_info.original_x, drag_info.original_y);
1938 drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK,
1940 event->button.time);
1942 if (session && session->transport_rolling()) {
1943 drag_info.was_rolling = true;
1945 drag_info.was_rolling = false;
1948 switch (snap_type) {
1949 case SnapToRegionStart:
1950 case SnapToRegionEnd:
1951 case SnapToRegionSync:
1952 case SnapToRegionBoundary:
1953 build_region_boundary_cache ();
1961 Editor::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t time)
1963 drag_info.item->ungrab (0);
1964 drag_info.item = new_item;
1967 cursor = which_grabber_cursor ();
1970 drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK, *cursor, time);
1974 Editor::end_grab (ArdourCanvas::Item* item, GdkEvent* event)
1976 bool did_drag = false;
1978 stop_canvas_autoscroll ();
1980 if (drag_info.item == 0) {
1984 drag_info.item->ungrab (event->button.time);
1986 if (drag_info.finished_callback) {
1987 drag_info.last_pointer_x = drag_info.current_pointer_x;
1988 drag_info.last_pointer_y = drag_info.current_pointer_y;
1989 (this->*(drag_info.finished_callback)) (item, event);
1992 did_drag = !drag_info.first_move;
1994 hide_verbose_canvas_cursor();
2002 Editor::start_fade_in_grab (ArdourCanvas::Item* item, GdkEvent* event)
2004 drag_info.item = item;
2005 drag_info.motion_callback = &Editor::fade_in_drag_motion_callback;
2006 drag_info.finished_callback = &Editor::fade_in_drag_finished_callback;
2010 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
2011 fatal << _("programming error: fade in canvas item has no regionview data pointer!") << endmsg;
2015 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
2018 drag_info.pointer_frame_offset = drag_info.grab_frame - ((nframes64_t) arv->audio_region()->fade_in()->back()->when + arv->region()->position());
2022 Editor::fade_in_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2024 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
2026 nframes64_t fade_length;
2028 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2029 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2035 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2039 if (pos < (arv->region()->position() + 64)) {
2040 fade_length = 64; // this should be a minimum defined somewhere
2041 } else if (pos > arv->region()->last_frame()) {
2042 fade_length = arv->region()->length();
2044 fade_length = pos - arv->region()->position();
2046 /* mapover the region selection */
2048 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2050 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2056 tmp->reset_fade_in_shape_width (fade_length);
2059 show_verbose_duration_cursor (arv->region()->position(), arv->region()->position() + fade_length, 10);
2061 drag_info.first_move = false;
2065 Editor::fade_in_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2067 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
2069 nframes64_t fade_length;
2071 if (drag_info.first_move) return;
2073 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2074 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2079 if (pos < (arv->region()->position() + 64)) {
2080 fade_length = 64; // this should be a minimum defined somewhere
2081 } else if (pos > arv->region()->last_frame()) {
2082 fade_length = arv->region()->length();
2084 fade_length = pos - arv->region()->position();
2087 begin_reversible_command (_("change fade in length"));
2089 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2091 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2097 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
2098 XMLNode &before = alist->get_state();
2100 tmp->audio_region()->set_fade_in_length (fade_length);
2101 tmp->audio_region()->set_fade_in_active (true);
2103 XMLNode &after = alist->get_state();
2104 session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2107 commit_reversible_command ();
2111 Editor::start_fade_out_grab (ArdourCanvas::Item* item, GdkEvent* event)
2113 drag_info.item = item;
2114 drag_info.motion_callback = &Editor::fade_out_drag_motion_callback;
2115 drag_info.finished_callback = &Editor::fade_out_drag_finished_callback;
2119 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
2120 fatal << _("programming error: fade out canvas item has no regionview data pointer!") << endmsg;
2124 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
2126 drag_info.pointer_frame_offset = drag_info.grab_frame - (arv->region()->length() - (nframes64_t) arv->audio_region()->fade_out()->back()->when + arv->region()->position());
2130 Editor::fade_out_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2132 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
2134 nframes64_t fade_length;
2136 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2137 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2142 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2146 if (pos > (arv->region()->last_frame() - 64)) {
2147 fade_length = 64; // this should really be a minimum fade defined somewhere
2149 else if (pos < arv->region()->position()) {
2150 fade_length = arv->region()->length();
2153 fade_length = arv->region()->last_frame() - pos;
2156 /* mapover the region selection */
2158 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2160 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2166 tmp->reset_fade_out_shape_width (fade_length);
2169 show_verbose_duration_cursor (arv->region()->last_frame() - fade_length, arv->region()->last_frame(), 10);
2171 drag_info.first_move = false;
2175 Editor::fade_out_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2177 if (drag_info.first_move) return;
2179 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
2181 nframes64_t fade_length;
2183 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2184 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2190 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2194 if (pos > (arv->region()->last_frame() - 64)) {
2195 fade_length = 64; // this should really be a minimum fade defined somewhere
2197 else if (pos < arv->region()->position()) {
2198 fade_length = arv->region()->length();
2201 fade_length = arv->region()->last_frame() - pos;
2204 begin_reversible_command (_("change fade out length"));
2206 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2208 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2214 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
2215 XMLNode &before = alist->get_state();
2217 tmp->audio_region()->set_fade_out_length (fade_length);
2218 tmp->audio_region()->set_fade_out_active (true);
2220 XMLNode &after = alist->get_state();
2221 session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2224 commit_reversible_command ();
2228 Editor::start_cursor_grab (ArdourCanvas::Item* item, GdkEvent* event)
2230 drag_info.item = item;
2231 drag_info.motion_callback = &Editor::cursor_drag_motion_callback;
2232 drag_info.finished_callback = &Editor::cursor_drag_finished_callback;
2236 if ((drag_info.data = (item->get_data ("cursor"))) == 0) {
2237 fatal << _("programming error: cursor canvas item has no cursor data pointer!") << endmsg;
2241 Cursor* cursor = (Cursor *) drag_info.data;
2243 if (cursor == playhead_cursor) {
2244 _dragging_playhead = true;
2246 if (session && drag_info.was_rolling) {
2247 session->request_stop ();
2250 if (session && session->is_auditioning()) {
2251 session->cancel_audition ();
2255 drag_info.pointer_frame_offset = drag_info.grab_frame - cursor->current_frame;
2257 show_verbose_time_cursor (cursor->current_frame, 10);
2261 Editor::cursor_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2263 Cursor* cursor = (Cursor *) drag_info.data;
2264 nframes64_t adjusted_frame;
2266 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2267 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2273 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2274 if (cursor == playhead_cursor) {
2275 snap_to (adjusted_frame);
2279 if (adjusted_frame == drag_info.last_pointer_frame) return;
2281 cursor->set_position (adjusted_frame);
2283 show_verbose_time_cursor (cursor->current_frame, 10);
2286 track_canvas->update_now ();
2288 UpdateAllTransportClocks (cursor->current_frame);
2290 drag_info.last_pointer_frame = adjusted_frame;
2291 drag_info.first_move = false;
2295 Editor::cursor_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2297 if (drag_info.first_move) return;
2299 cursor_drag_motion_callback (item, event);
2301 _dragging_playhead = false;
2303 if (item == &playhead_cursor->canvas_item) {
2305 session->request_locate (playhead_cursor->current_frame, drag_info.was_rolling);
2311 Editor::update_marker_drag_item (Location *location)
2313 double x1 = frame_to_pixel (location->start());
2314 double x2 = frame_to_pixel (location->end());
2316 if (location->is_mark()) {
2317 marker_drag_line_points.front().set_x(x1);
2318 marker_drag_line_points.back().set_x(x1);
2319 marker_drag_line->property_points() = marker_drag_line_points;
2321 range_marker_drag_rect->property_x1() = x1;
2322 range_marker_drag_rect->property_x2() = x2;
2328 Editor::start_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2332 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2333 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2339 Location *location = find_location_from_marker (marker, is_start);
2341 drag_info.item = item;
2342 drag_info.data = marker;
2343 drag_info.motion_callback = &Editor::marker_drag_motion_callback;
2344 drag_info.finished_callback = &Editor::marker_drag_finished_callback;
2348 _dragging_edit_point = true;
2350 drag_info.pointer_frame_offset = drag_info.grab_frame - (is_start ? location->start() : location->end());
2352 update_marker_drag_item (location);
2354 if (location->is_mark()) {
2355 // marker_drag_line->show();
2356 // marker_drag_line->raise_to_top();
2358 range_marker_drag_rect->show();
2359 //range_marker_drag_rect->raise_to_top();
2363 show_verbose_time_cursor (location->start(), 10);
2365 show_verbose_time_cursor (location->end(), 10);
2368 Selection::Operation op = Keyboard::selection_type (event->button.state);
2371 case Selection::Toggle:
2372 selection->toggle (marker);
2374 case Selection::Set:
2375 if (!selection->selected (marker)) {
2376 selection->set (marker);
2379 case Selection::Extend:
2381 Locations::LocationList ll;
2382 list<Marker*> to_add;
2384 selection->markers.range (s, e);
2385 s = min (marker->position(), s);
2386 e = max (marker->position(), e);
2389 if (e < max_frames) {
2392 session->locations()->find_all_between (s, e, ll, Location::Flags (0));
2393 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
2394 LocationMarkers* lm = find_location_markers (*i);
2397 to_add.push_back (lm->start);
2400 to_add.push_back (lm->end);
2404 if (!to_add.empty()) {
2405 selection->add (to_add);
2409 case Selection::Add:
2410 selection->add (marker);
2414 /* set up copies for us to manipulate during the drag */
2416 drag_info.clear_copied_locations ();
2418 for (MarkerSelection::iterator i = selection->markers.begin(); i != selection->markers.end(); ++i) {
2419 Location *l = find_location_from_marker (*i, is_start);
2420 drag_info.copied_locations.push_back (new Location (*l));
2425 Editor::marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2427 nframes64_t f_delta = 0;
2428 nframes64_t newframe;
2430 bool move_both = false;
2431 Marker* dragged_marker = (Marker*) drag_info.data;
2433 Location *real_location;
2434 Location *copy_location;
2436 if (drag_info.pointer_frame_offset <= drag_info.current_pointer_frame) {
2437 newframe = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2442 nframes64_t next = newframe;
2444 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2445 snap_to (newframe, 0, true);
2448 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
2452 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2456 MarkerSelection::iterator i;
2457 list<Location*>::iterator x;
2459 /* find the marker we're dragging, and compute the delta */
2461 for (i = selection->markers.begin(), x = drag_info.copied_locations.begin();
2462 x != drag_info.copied_locations.end() && i != selection->markers.end();
2468 if (marker == dragged_marker) {
2470 if ((real_location = find_location_from_marker (marker, is_start)) == 0) {
2475 if (real_location->is_mark()) {
2476 f_delta = newframe - copy_location->start();
2480 switch (marker->type()) {
2482 case Marker::LoopStart:
2483 case Marker::PunchIn:
2484 f_delta = newframe - copy_location->start();
2488 case Marker::LoopEnd:
2489 case Marker::PunchOut:
2490 f_delta = newframe - copy_location->end();
2493 /* what kind of marker is this ? */
2501 if (i == selection->markers.end()) {
2502 /* hmm, impossible - we didn't find the dragged marker */
2506 /* now move them all */
2508 for (i = selection->markers.begin(), x = drag_info.copied_locations.begin();
2509 x != drag_info.copied_locations.end() && i != selection->markers.end();
2515 /* call this to find out if its the start or end */
2517 if ((real_location = find_location_from_marker (marker, is_start)) == 0) {
2521 if (real_location->locked()) {
2525 if (copy_location->is_mark()) {
2529 copy_location->set_start (copy_location->start() + f_delta);
2533 nframes64_t new_start = copy_location->start() + f_delta;
2534 nframes64_t new_end = copy_location->end() + f_delta;
2536 if (is_start) { // start-of-range marker
2539 copy_location->set_start (new_start);
2540 copy_location->set_end (new_end);
2541 } else if (new_start < copy_location->end()) {
2542 copy_location->set_start (new_start);
2544 snap_to (next, 1, true);
2545 copy_location->set_end (next);
2546 copy_location->set_start (newframe);
2549 } else { // end marker
2552 copy_location->set_end (new_end);
2553 copy_location->set_start (new_start);
2554 } else if (new_end > copy_location->start()) {
2555 copy_location->set_end (new_end);
2556 } else if (newframe > 0) {
2557 snap_to (next, -1, true);
2558 copy_location->set_start (next);
2559 copy_location->set_end (newframe);
2563 update_marker_drag_item (copy_location);
2565 LocationMarkers* lm = find_location_markers (real_location);
2568 lm->set_position (copy_location->start(), copy_location->end());
2572 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
2573 drag_info.first_move = false;
2575 if (drag_info.copied_locations.empty()) {
2579 edit_point_clock.set (drag_info.copied_locations.front()->start());
2580 show_verbose_time_cursor (newframe, 10);
2583 track_canvas->update_now ();
2585 edit_point_clock.set (copy_location->start());
2589 Editor::marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2591 if (drag_info.first_move) {
2593 /* just a click, do nothing but finish
2594 off the selection process
2597 Selection::Operation op = Keyboard::selection_type (event->button.state);
2598 Marker* marker = (Marker *) drag_info.data;
2601 case Selection::Set:
2602 if (selection->selected (marker) && selection->markers.size() > 1) {
2603 selection->set (marker);
2607 case Selection::Toggle:
2608 case Selection::Extend:
2609 case Selection::Add:
2616 _dragging_edit_point = false;
2619 begin_reversible_command ( _("move marker") );
2620 XMLNode &before = session->locations()->get_state();
2622 MarkerSelection::iterator i;
2623 list<Location*>::iterator x;
2626 for (i = selection->markers.begin(), x = drag_info.copied_locations.begin();
2627 x != drag_info.copied_locations.end() && i != selection->markers.end();
2630 Location * location = find_location_from_marker ((*i), is_start);
2634 if (location->locked()) {
2638 if (location->is_mark()) {
2639 location->set_start ((*x)->start());
2641 location->set ((*x)->start(), (*x)->end());
2646 XMLNode &after = session->locations()->get_state();
2647 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
2648 commit_reversible_command ();
2650 marker_drag_line->hide();
2651 range_marker_drag_rect->hide();
2655 Editor::start_meter_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2658 MeterMarker* meter_marker;
2660 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2661 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2665 meter_marker = dynamic_cast<MeterMarker*> (marker);
2667 MetricSection& section (meter_marker->meter());
2669 if (!section.movable()) {
2673 drag_info.item = item;
2674 drag_info.copy = false;
2675 drag_info.data = marker;
2676 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2677 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2681 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2683 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2687 Editor::start_meter_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2690 MeterMarker* meter_marker;
2692 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2693 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2697 meter_marker = dynamic_cast<MeterMarker*> (marker);
2699 // create a dummy marker for visual representation of moving the copy.
2700 // The actual copying is not done before we reach the finish callback.
2702 snprintf (name, sizeof(name), "%g/%g", meter_marker->meter().beats_per_bar(), meter_marker->meter().note_divisor ());
2703 MeterMarker* new_marker = new MeterMarker(*this, *meter_group, ARDOUR_UI::config()->canvasvar_MeterMarker.get(), name,
2704 *new MeterSection(meter_marker->meter()));
2706 drag_info.item = &new_marker->the_item();
2707 drag_info.copy = true;
2708 drag_info.data = new_marker;
2709 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2710 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2714 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2716 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2720 Editor::meter_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2722 MeterMarker* marker = (MeterMarker *) drag_info.data;
2723 nframes64_t adjusted_frame;
2725 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2726 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2732 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2733 snap_to (adjusted_frame);
2736 if (adjusted_frame == drag_info.last_pointer_frame) return;
2738 marker->set_position (adjusted_frame);
2741 drag_info.last_pointer_frame = adjusted_frame;
2742 drag_info.first_move = false;
2744 show_verbose_time_cursor (adjusted_frame, 10);
2748 Editor::meter_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2750 if (drag_info.first_move) return;
2752 meter_marker_drag_motion_callback (drag_info.item, event);
2754 MeterMarker* marker = (MeterMarker *) drag_info.data;
2757 TempoMap& map (session->tempo_map());
2758 map.bbt_time (drag_info.last_pointer_frame, when);
2760 if (drag_info.copy == true) {
2761 begin_reversible_command (_("copy meter mark"));
2762 XMLNode &before = map.get_state();
2763 map.add_meter (marker->meter(), when);
2764 XMLNode &after = map.get_state();
2765 session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2766 commit_reversible_command ();
2768 // delete the dummy marker we used for visual representation of copying.
2769 // a new visual marker will show up automatically.
2772 begin_reversible_command (_("move meter mark"));
2773 XMLNode &before = map.get_state();
2774 map.move_meter (marker->meter(), when);
2775 XMLNode &after = map.get_state();
2776 session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2777 commit_reversible_command ();
2782 Editor::start_tempo_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2785 TempoMarker* tempo_marker;
2787 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2788 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2792 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2793 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2797 MetricSection& section (tempo_marker->tempo());
2799 if (!section.movable()) {
2803 drag_info.item = item;
2804 drag_info.copy = false;
2805 drag_info.data = marker;
2806 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2807 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2811 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2812 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2816 Editor::start_tempo_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2819 TempoMarker* tempo_marker;
2821 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2822 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2826 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2827 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2831 // create a dummy marker for visual representation of moving the copy.
2832 // The actual copying is not done before we reach the finish callback.
2834 snprintf (name, sizeof (name), "%.2f", tempo_marker->tempo().beats_per_minute());
2835 TempoMarker* new_marker = new TempoMarker(*this, *tempo_group, ARDOUR_UI::config()->canvasvar_TempoMarker.get(), name,
2836 *new TempoSection(tempo_marker->tempo()));
2838 drag_info.item = &new_marker->the_item();
2839 drag_info.copy = true;
2840 drag_info.data = new_marker;
2841 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2842 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2846 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2848 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2852 Editor::tempo_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2854 TempoMarker* marker = (TempoMarker *) drag_info.data;
2855 nframes64_t adjusted_frame;
2857 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2858 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2864 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2865 snap_to (adjusted_frame);
2868 if (adjusted_frame == drag_info.last_pointer_frame) return;
2870 /* OK, we've moved far enough to make it worth actually move the thing. */
2872 marker->set_position (adjusted_frame);
2874 show_verbose_time_cursor (adjusted_frame, 10);
2876 drag_info.last_pointer_frame = adjusted_frame;
2877 drag_info.first_move = false;
2881 Editor::tempo_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2883 if (drag_info.first_move) return;
2885 tempo_marker_drag_motion_callback (drag_info.item, event);
2887 TempoMarker* marker = (TempoMarker *) drag_info.data;
2890 TempoMap& map (session->tempo_map());
2891 map.bbt_time (drag_info.last_pointer_frame, when);
2893 if (drag_info.copy == true) {
2894 begin_reversible_command (_("copy tempo mark"));
2895 XMLNode &before = map.get_state();
2896 map.add_tempo (marker->tempo(), when);
2897 XMLNode &after = map.get_state();
2898 session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2899 commit_reversible_command ();
2901 // delete the dummy marker we used for visual representation of copying.
2902 // a new visual marker will show up automatically.
2905 begin_reversible_command (_("move tempo mark"));
2906 XMLNode &before = map.get_state();
2907 map.move_tempo (marker->tempo(), when);
2908 XMLNode &after = map.get_state();
2909 session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2910 commit_reversible_command ();
2915 Editor::remove_gain_control_point (ArdourCanvas::Item*item, GdkEvent* event)
2917 ControlPoint* control_point;
2919 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2920 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2924 // We shouldn't remove the first or last gain point
2925 if (control_point->line().is_last_point(*control_point) ||
2926 control_point->line().is_first_point(*control_point)) {
2930 control_point->line().remove_point (*control_point);
2934 Editor::remove_control_point (ArdourCanvas::Item* item, GdkEvent* event)
2936 ControlPoint* control_point;
2938 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2939 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2943 control_point->line().remove_point (*control_point);
2947 Editor::start_control_point_grab (ArdourCanvas::Item* item, GdkEvent* event)
2949 ControlPoint* control_point;
2951 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2952 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2956 drag_info.item = item;
2957 drag_info.data = control_point;
2958 drag_info.motion_callback = &Editor::control_point_drag_motion_callback;
2959 drag_info.finished_callback = &Editor::control_point_drag_finished_callback;
2961 start_grab (event, fader_cursor);
2963 // start the grab at the center of the control point so
2964 // the point doesn't 'jump' to the mouse after the first drag
2965 drag_info.grab_x = control_point->get_x();
2966 drag_info.grab_y = control_point->get_y();
2968 control_point->line().parent_group().i2w(drag_info.grab_x, drag_info.grab_y);
2969 track_canvas->w2c(drag_info.grab_x, drag_info.grab_y, drag_info.grab_x, drag_info.grab_y);
2971 drag_info.grab_frame = pixel_to_frame(drag_info.grab_x);
2973 control_point->line().start_drag (control_point, drag_info.grab_frame, 0);
2975 float fraction = 1.0 - (control_point->get_y() / control_point->line().height());
2976 set_verbose_canvas_cursor (control_point->line().get_verbose_cursor_string (fraction),
2977 drag_info.current_pointer_x + 10, drag_info.current_pointer_y + 10);
2979 show_verbose_canvas_cursor ();
2983 Editor::control_point_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2985 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2987 double dx = drag_info.current_pointer_x - drag_info.last_pointer_x;
2988 double dy = drag_info.current_pointer_y - drag_info.last_pointer_y;
2990 if (event->button.state & Keyboard::SecondaryModifier) {
2995 double cx = drag_info.grab_x + drag_info.cumulative_x_drag + dx;
2996 double cy = drag_info.grab_y + drag_info.cumulative_y_drag + dy;
2998 // calculate zero crossing point. back off by .01 to stay on the
2999 // positive side of zero
3001 double zero_gain_y = (1.0 - ZERO_GAIN_FRACTION) * cp->line().height() - .01;
3002 cp->line().parent_group().i2w(_unused, zero_gain_y);
3004 // make sure we hit zero when passing through
3005 if ((cy < zero_gain_y and (cy - dy) > zero_gain_y)
3006 or (cy > zero_gain_y and (cy - dy) < zero_gain_y)) {
3010 if (drag_info.x_constrained) {
3011 cx = drag_info.grab_x;
3013 if (drag_info.y_constrained) {
3014 cy = drag_info.grab_y;
3017 drag_info.cumulative_x_drag = cx - drag_info.grab_x;
3018 drag_info.cumulative_y_drag = cy - drag_info.grab_y;
3020 cp->line().parent_group().w2i (cx, cy);
3024 cy = min ((double) cp->line().height(), cy);
3026 //translate cx to frames
3027 nframes64_t cx_frames = unit_to_frame (cx);
3029 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier()) && !drag_info.x_constrained) {
3030 snap_to (cx_frames);
3033 float fraction = 1.0 - (cy / cp->line().height());
3037 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) {
3043 cp->line().point_drag (*cp, cx_frames , fraction, push);
3045 set_verbose_canvas_cursor_text (cp->line().get_verbose_cursor_string (fraction));
3047 drag_info.first_move = false;
3051 Editor::control_point_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3053 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
3055 if (drag_info.first_move) {
3059 if ((event->type == GDK_BUTTON_RELEASE) && (event->button.button == 1) && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3060 reset_point_selection ();
3064 control_point_drag_motion_callback (item, event);
3066 cp->line().end_drag (cp);
3070 Editor::start_line_grab_from_regionview (ArdourCanvas::Item* item, GdkEvent* event)
3072 switch (mouse_mode) {
3074 assert(dynamic_cast<AudioRegionView*>(clicked_regionview));
3075 start_line_grab (dynamic_cast<AudioRegionView*>(clicked_regionview)->get_gain_line(), event);
3083 Editor::start_line_grab_from_line (ArdourCanvas::Item* item, GdkEvent* event)
3087 if ((al = reinterpret_cast<AutomationLine*> (item->get_data ("line"))) == 0) {
3088 fatal << _("programming error: line canvas item has no line pointer!") << endmsg;
3092 start_line_grab (al, event);
3096 Editor::start_line_grab (AutomationLine* line, GdkEvent* event)
3100 nframes64_t frame_within_region;
3102 /* need to get x coordinate in terms of parent (TimeAxisItemView)
3106 cx = event->button.x;
3107 cy = event->button.y;
3108 line->parent_group().w2i (cx, cy);
3109 frame_within_region = (nframes64_t) floor (cx * frames_per_unit);
3111 if (!line->control_points_adjacent (frame_within_region, current_line_drag_info.before,
3112 current_line_drag_info.after)) {
3113 /* no adjacent points */
3117 drag_info.item = &line->grab_item();
3118 drag_info.data = line;
3119 drag_info.motion_callback = &Editor::line_drag_motion_callback;
3120 drag_info.finished_callback = &Editor::line_drag_finished_callback;
3122 start_grab (event, fader_cursor);
3124 double fraction = 1.0 - (cy / line->height());
3126 line->start_drag (0, drag_info.grab_frame, fraction);
3128 set_verbose_canvas_cursor (line->get_verbose_cursor_string (fraction),
3129 drag_info.current_pointer_x + 10, drag_info.current_pointer_y + 10);
3130 show_verbose_canvas_cursor ();
3134 Editor::line_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
3136 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
3138 double dy = drag_info.current_pointer_y - drag_info.last_pointer_y;
3140 if (event->button.state & Keyboard::SecondaryModifier) {
3144 double cy = drag_info.grab_y + drag_info.cumulative_y_drag + dy;
3146 // calculate zero crossing point. back off by .01 to stay on the
3147 // positive side of zero
3149 double zero_gain_y = (1.0 - ZERO_GAIN_FRACTION) * line->height() - .01;
3150 line->parent_group().i2w(_unused, zero_gain_y);
3152 // make sure we hit zero when passing through
3153 if ((cy < zero_gain_y and (cy - dy) > zero_gain_y)
3154 or (cy > zero_gain_y and (cy - dy) < zero_gain_y)) {
3158 drag_info.cumulative_y_drag = cy - drag_info.grab_y;
3161 cy = min ((double) line->height(), cy);
3163 double fraction = 1.0 - (cy / line->height());
3167 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) {
3173 line->line_drag (current_line_drag_info.before, current_line_drag_info.after, fraction, push);
3175 set_verbose_canvas_cursor_text (line->get_verbose_cursor_string (fraction));
3179 Editor::line_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3181 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
3182 line_drag_motion_callback (item, event);
3187 Editor::start_region_grab (ArdourCanvas::Item* item, GdkEvent* event)
3189 if (selection->regions.empty() || clicked_regionview == 0) {
3192 _region_motion_group->raise_to_top ();
3193 drag_info.copy = false;
3194 drag_info.item = item;
3195 drag_info.data = clicked_regionview;
3197 if (Config->get_edit_mode() == Splice) {
3198 drag_info.motion_callback = &Editor::region_drag_splice_motion_callback;
3199 drag_info.finished_callback = &Editor::region_drag_splice_finished_callback;
3201 drag_info.motion_callback = &Editor::region_drag_motion_callback;
3202 drag_info.finished_callback = &Editor::region_drag_finished_callback;
3208 TimeAxisView* tvp = clicked_axisview;
3209 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
3211 if (tv && tv->is_track()) {
3212 speed = tv->get_diskstream()->speed();
3215 drag_info.last_frame_position = (nframes64_t) (clicked_regionview->region()->position() / speed);
3216 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
3217 drag_info.source_trackview = &clicked_regionview->get_time_axis_view();
3218 drag_info.dest_trackview = drag_info.source_trackview;
3219 // we want a move threshold
3220 drag_info.want_move_threshold = true;
3221 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3223 begin_reversible_command (_("move region(s)"));
3225 /* sync the canvas to what we think is its current state */
3226 track_canvas->update_now();
3230 Editor::start_create_region_grab (ArdourCanvas::Item* item, GdkEvent* event)
3232 drag_info.copy = false;
3233 drag_info.item = item;
3234 drag_info.data = clicked_axisview;
3235 drag_info.source_trackview = clicked_axisview;
3236 drag_info.dest_trackview = drag_info.source_trackview;
3237 drag_info.motion_callback = &Editor::create_region_drag_motion_callback;
3238 drag_info.finished_callback = &Editor::create_region_drag_finished_callback;
3244 Editor::start_region_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
3246 if (selection->regions.empty() || clicked_regionview == 0) {
3249 _region_motion_group->raise_to_top ();
3250 drag_info.copy = true;
3251 drag_info.item = item;
3252 drag_info.data = clicked_regionview;
3256 TimeAxisView* tv = &clicked_regionview->get_time_axis_view();
3257 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
3260 if (rtv && rtv->is_track()) {
3261 speed = rtv->get_diskstream()->speed();
3264 drag_info.source_trackview = &clicked_regionview->get_time_axis_view();
3265 drag_info.dest_trackview = drag_info.source_trackview;
3266 drag_info.last_frame_position = (nframes64_t) (clicked_regionview->region()->position() / speed);
3267 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
3268 // we want a move threshold
3269 drag_info.want_move_threshold = true;
3270 drag_info.motion_callback = &Editor::region_drag_motion_callback;
3271 drag_info.finished_callback = &Editor::region_drag_finished_callback;
3272 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3276 Editor::start_region_brush_grab (ArdourCanvas::Item* item, GdkEvent* event)
3278 if (selection->regions.empty() || clicked_regionview == 0 || Config->get_edit_mode() == Splice) {
3282 drag_info.copy = false;
3283 drag_info.item = item;
3284 drag_info.data = clicked_regionview;
3285 drag_info.motion_callback = &Editor::region_drag_motion_callback;
3286 drag_info.finished_callback = &Editor::region_drag_finished_callback;
3291 TimeAxisView* tvp = clicked_axisview;
3292 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
3294 if (tv && tv->is_track()) {
3295 speed = tv->get_diskstream()->speed();
3298 drag_info.last_frame_position = (nframes64_t) (clicked_regionview->region()->position() / speed);
3299 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
3300 drag_info.source_trackview = &clicked_regionview->get_time_axis_view();
3301 drag_info.dest_trackview = drag_info.source_trackview;
3302 // we want a move threshold
3303 drag_info.want_move_threshold = true;
3304 drag_info.brushing = true;
3306 begin_reversible_command (_("Drag region brush"));
3310 Editor::possibly_copy_regions_during_grab (GdkEvent* event)
3312 if (drag_info.copy && drag_info.move_threshold_passed && drag_info.want_move_threshold) {
3314 drag_info.want_move_threshold = false; // don't copy again
3316 /* duplicate the regionview(s) and region(s) */
3318 vector<RegionView*> new_regionviews;
3320 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3325 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
3326 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
3328 const boost::shared_ptr<const Region> original = rv->region();
3329 boost::shared_ptr<Region> region_copy = RegionFactory::create (original);
3332 boost::shared_ptr<AudioRegion> audioregion_copy
3333 = boost::dynamic_pointer_cast<AudioRegion>(region_copy);
3334 nrv = new AudioRegionView (*arv, audioregion_copy);
3336 boost::shared_ptr<MidiRegion> midiregion_copy
3337 = boost::dynamic_pointer_cast<MidiRegion>(region_copy);
3338 nrv = new MidiRegionView (*mrv, midiregion_copy);
3343 nrv->get_canvas_group()->show ();
3344 new_regionviews.push_back (nrv);
3347 if (new_regionviews.empty()) {
3351 /* reset selection to new regionviews. This will not set selection visual status for
3352 these regionviews since they don't belong to a track, so do that by hand too.
3355 selection->set (new_regionviews);
3357 for (vector<RegionView*>::iterator i = new_regionviews.begin(); i != new_regionviews.end(); ++i) {
3358 (*i)->set_selected (true);
3361 /* reset drag_info data to reflect the fact that we are dragging the copies */
3363 drag_info.data = new_regionviews.front();
3365 swap_grab (new_regionviews.front()->get_canvas_group (), 0, event->motion.time);
3367 sync the canvas to what we think is its current state
3368 without it, the canvas seems to
3369 "forget" to update properly after the upcoming reparent()
3370 ..only if the mouse is in rapid motion at the time of the grab.
3371 something to do with regionview creation raking so long?
3373 track_canvas->update_now();
3378 Editor::check_region_drag_possible (RouteTimeAxisView** tv)
3380 /* Which trackview is this ? */
3382 TimeAxisView* tvp = trackview_by_y_position (drag_info.current_pointer_y);
3383 (*tv) = dynamic_cast<RouteTimeAxisView*>(tvp);
3385 /* The region motion is only processed if the pointer is over
3389 if (!(*tv) || !(*tv)->is_track()) {
3390 /* To make sure we hide the verbose canvas cursor when the mouse is
3391 not held over and audiotrack.
3393 hide_verbose_canvas_cursor ();
3400 struct RegionSelectionByPosition {
3401 bool operator() (RegionView*a, RegionView* b) {
3402 return a->region()->position () < b->region()->position();
3407 Editor::region_drag_splice_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
3409 RouteTimeAxisView* tv;
3411 if (!check_region_drag_possible (&tv)) {
3415 if (!drag_info.move_threshold_passed) {
3421 if (drag_info.current_pointer_x - drag_info.grab_x > 0) {
3427 RegionSelection copy (selection->regions);
3429 RegionSelectionByPosition cmp;
3432 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
3434 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
3440 boost::shared_ptr<Playlist> playlist;
3442 if ((playlist = atv->playlist()) == 0) {
3446 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
3451 if (drag_info.current_pointer_frame < (*i)->region()->last_frame() + 1) {
3455 if (drag_info.current_pointer_frame > (*i)->region()->first_frame()) {
3461 playlist->shuffle ((*i)->region(), dir);
3463 drag_info.grab_x = drag_info.current_pointer_x;
3468 Editor::region_drag_splice_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3473 Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
3477 nframes64_t pending_region_position = 0;
3478 int32_t pointer_y_span = 0, canvas_pointer_y_span = 0, original_pointer_order;
3479 int32_t visible_y_high = 0, visible_y_low = 512; //high meaning higher numbered.. not the height on the screen
3480 bool clamp_y_axis = false;
3481 vector<int32_t> height_list(512) ;
3482 vector<int32_t>::iterator j;
3483 RouteTimeAxisView* tv;
3485 possibly_copy_regions_during_grab (event);
3487 if (!check_region_drag_possible (&tv)) {
3491 original_pointer_order = drag_info.dest_trackview->order;
3493 /************************************************************
3495 ************************************************************/
3497 if (drag_info.brushing) {
3498 clamp_y_axis = true;
3503 if ((pointer_y_span = (drag_info.dest_trackview->order - tv->order)) != 0) {
3505 int32_t children = 0, numtracks = 0;
3506 // XXX hard coding track limit, oh my, so very very bad
3507 bitset <1024> tracks (0x00);
3508 /* get a bitmask representing the visible tracks */
3510 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
3511 TimeAxisView *tracklist_timeview;
3512 tracklist_timeview = (*i);
3513 RouteTimeAxisView* rtv2 = dynamic_cast<RouteTimeAxisView*>(tracklist_timeview);
3514 TimeAxisView::Children children_list;
3516 /* zeroes are audio tracks. ones are other types. */
3518 if (!rtv2->hidden()) {
3520 if (visible_y_high < rtv2->order) {
3521 visible_y_high = rtv2->order;
3523 if (visible_y_low > rtv2->order) {
3524 visible_y_low = rtv2->order;
3527 if (!rtv2->is_track()) {
3528 tracks = tracks |= (0x01 << rtv2->order);
3531 height_list[rtv2->order] = (*i)->current_height();
3534 if ((children_list = rtv2->get_child_list()).size() > 0) {
3535 for (TimeAxisView::Children::iterator j = children_list.begin(); j != children_list.end(); ++j) {
3536 tracks = tracks |= (0x01 << (rtv2->order + children));
3537 height_list[rtv2->order + children] = (*j)->current_height();
3545 /* find the actual span according to the canvas */
3547 canvas_pointer_y_span = pointer_y_span;
3548 if (drag_info.dest_trackview->order >= tv->order) {
3550 for (y = tv->order; y < drag_info.dest_trackview->order; y++) {
3551 if (height_list[y] == 0 ) {
3552 canvas_pointer_y_span--;
3557 for (y = drag_info.dest_trackview->order;y <= tv->order; y++) {
3558 if ( height_list[y] == 0 ) {
3559 canvas_pointer_y_span++;
3564 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3565 RegionView* rv2 = (*i);
3566 double ix1, ix2, iy1, iy2;
3569 if (rv2->region()->locked()) {
3573 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3574 rv2->get_canvas_frame()->i2w (ix1, iy1);
3575 iy1 += vertical_adjustment.get_value() - canvas_timebars_vsize;
3577 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3578 RouteTimeAxisView* rtv2 = dynamic_cast<RouteTimeAxisView*>(tvp2);
3580 if (rtv2->order != original_pointer_order) {
3581 /* this isn't the pointer track */
3583 if (canvas_pointer_y_span > 0) {
3585 /* moving up the canvas */
3586 if ((rtv2->order - canvas_pointer_y_span) >= visible_y_low) {
3588 int32_t visible_tracks = 0;
3589 while (visible_tracks < canvas_pointer_y_span ) {
3592 while (height_list[rtv2->order - (visible_tracks - n)] == 0) {
3593 /* we're passing through a hidden track */
3598 if (tracks[rtv2->order - (canvas_pointer_y_span - n)] != 0x00) {
3599 clamp_y_axis = true;
3603 clamp_y_axis = true;
3606 } else if (canvas_pointer_y_span < 0) {
3608 /*moving down the canvas*/
3610 if ((rtv2->order - (canvas_pointer_y_span - n)) <= visible_y_high) { // we will overflow
3613 int32_t visible_tracks = 0;
3615 while (visible_tracks > canvas_pointer_y_span ) {
3618 while (height_list[rtv2->order - (visible_tracks - n)] == 0) {
3622 if ( tracks[rtv2->order - ( canvas_pointer_y_span - n)] != 0x00) {
3623 clamp_y_axis = true;
3628 clamp_y_axis = true;
3634 /* this is the pointer's track */
3635 if ((rtv2->order - pointer_y_span) > visible_y_high) { // we will overflow
3636 clamp_y_axis = true;
3637 } else if ((rtv2->order - pointer_y_span) < visible_y_low) { // we will underflow
3638 clamp_y_axis = true;
3646 } else if (drag_info.dest_trackview == tv) {
3647 clamp_y_axis = true;
3651 if (!clamp_y_axis) {
3652 drag_info.dest_trackview = tv;
3655 /************************************************************
3657 ************************************************************/
3659 /* compute the amount of pointer motion in frames, and where
3660 the region would be if we moved it by that much.
3662 if ( drag_info.move_threshold_passed ) {
3664 if (drag_info.current_pointer_frame >= drag_info.pointer_frame_offset) {
3666 nframes64_t sync_frame;
3667 nframes64_t sync_offset;
3670 pending_region_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
3672 sync_offset = clicked_regionview->region()->sync_offset (sync_dir);
3674 /* we don't handle a sync point that lies before zero.
3676 if (sync_dir >= 0 || (sync_dir < 0 && pending_region_position >= sync_offset)) {
3677 sync_frame = pending_region_position + (sync_dir*sync_offset);
3679 /* we snap if the snap modifier is not enabled.
3682 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3683 snap_to (sync_frame);
3686 pending_region_position = clicked_regionview->region()->adjust_to_sync (sync_frame);
3689 pending_region_position = drag_info.last_frame_position;
3693 pending_region_position = 0;
3696 if (pending_region_position > max_frames - clicked_regionview->region()->length()) {
3697 pending_region_position = drag_info.last_frame_position;
3700 // printf ("3: pending_region_position= %lu %lu\n", pending_region_position, drag_info.last_frame_position );
3702 bool x_move_allowed;
3704 if (Config->get_edit_mode() == Lock) {
3705 if (drag_info.copy) {
3706 x_move_allowed = !drag_info.x_constrained;
3708 /* in locked edit mode, reverse the usual meaning of x_constrained */
3709 x_move_allowed = drag_info.x_constrained;
3712 x_move_allowed = !drag_info.x_constrained;
3715 if (( pending_region_position != drag_info.last_frame_position) && x_move_allowed ) {
3717 /* now compute the canvas unit distance we need to move the regionview
3718 to make it appear at the new location.
3721 if (pending_region_position > drag_info.last_frame_position) {
3722 x_delta = ((double) (pending_region_position - drag_info.last_frame_position) / frames_per_unit);
3724 x_delta = -((double) (drag_info.last_frame_position - pending_region_position) / frames_per_unit);
3725 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3727 RegionView* rv2 = (*i);
3729 // If any regionview is at zero, we need to know so we can stop further leftward motion.
3731 double ix1, ix2, iy1, iy2;
3732 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3733 rv2->get_canvas_frame()->i2w (ix1, iy1);
3735 if (-x_delta > ix1 + horizontal_adjustment.get_value()) {
3738 pending_region_position = drag_info.last_frame_position;
3745 drag_info.last_frame_position = pending_region_position;
3752 /* threshold not passed */
3757 /*************************************************************
3759 ************************************************************/
3761 if (x_delta == 0 && (pointer_y_span == 0)) {
3762 /* haven't reached next snap point, and we're not switching
3763 trackviews. nothing to do.
3768 /*************************************************************
3770 ************************************************************/
3771 bool do_move = true;
3772 if (drag_info.first_move) {
3773 if (!drag_info.move_threshold_passed) {
3780 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
3781 const list<RegionView*>& layered_regions = selection->regions.by_layer();
3783 for (list<RegionView*>::const_iterator i = layered_regions.begin(); i != layered_regions.end(); ++i) {
3785 RegionView* rv = (*i);
3786 double ix1, ix2, iy1, iy2;
3787 int32_t temp_pointer_y_span = pointer_y_span;
3789 if (rv->region()->locked()) {
3793 /* get item BBox, which will be relative to parent. so we have
3794 to query on a child, then convert to world coordinates using
3798 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3799 rv->get_canvas_frame()->i2w (ix1, iy1);
3801 cerr << "adjust y from " << iy1 << " using "
3802 << vertical_adjustment.get_value() << " - "
3803 << canvas_timebars_vsize
3806 iy1 += get_trackview_group_vertical_offset ();;
3808 if (drag_info.first_move) {
3810 // hide any dependent views
3812 rv->get_time_axis_view().hide_dependent_views (*rv);
3815 reparent to a non scrolling group so that we can keep the
3816 region selection above all time axis views.
3817 reparenting means we have to move the rv as the two
3818 parent groups have different coordinates.
3821 rv->get_canvas_group()->property_y() = iy1 - 1;
3822 rv->get_canvas_group()->reparent(*_region_motion_group);
3824 rv->fake_set_opaque (true);
3827 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3828 RouteTimeAxisView* canvas_rtv = dynamic_cast<RouteTimeAxisView*>(tvp2);
3829 RouteTimeAxisView* temp_rtv;
3831 if ((pointer_y_span != 0) && !clamp_y_axis) {
3834 for (j = height_list.begin(); j!= height_list.end(); j++) {
3835 if (x == canvas_rtv->order) {
3836 /* we found the track the region is on */
3837 if (x != original_pointer_order) {
3838 /*this isn't from the same track we're dragging from */
3839 temp_pointer_y_span = canvas_pointer_y_span;
3841 while (temp_pointer_y_span > 0) {
3842 /* we're moving up canvas-wise,
3843 so we need to find the next track height
3845 if (j != height_list.begin()) {
3848 if (x != original_pointer_order) {
3849 /* we're not from the dragged track, so ignore hidden tracks. */
3851 temp_pointer_y_span++;
3855 temp_pointer_y_span--;
3858 while (temp_pointer_y_span < 0) {
3860 if (x != original_pointer_order) {
3862 temp_pointer_y_span--;
3866 if (j != height_list.end()) {
3869 temp_pointer_y_span++;
3871 /* find out where we'll be when we move and set height accordingly */
3873 tvp2 = trackview_by_y_position (iy1 + y_delta);
3874 temp_rtv = dynamic_cast<RouteTimeAxisView*>(tvp2);
3875 rv->set_height (temp_rtv->current_height());
3877 /* if you un-comment the following, the region colours will follow the track colours whilst dragging,
3878 personally, i think this can confuse things, but never mind.
3881 //const GdkColor& col (temp_rtv->view->get_region_color());
3882 //rv->set_color (const_cast<GdkColor&>(col));
3889 if (drag_info.brushing) {
3890 mouse_brush_insert_region (rv, pending_region_position);
3892 rv->move (x_delta, y_delta);
3895 } /* foreach region */
3899 if (drag_info.first_move && drag_info.move_threshold_passed) {
3900 cursor_group->raise_to_top();
3901 drag_info.first_move = false;
3904 if (x_delta != 0 && !drag_info.brushing) {
3905 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3910 Editor::region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3912 bool nocommit = true;
3913 vector<RegionView*> copies;
3914 RouteTimeAxisView* source_tv;
3915 boost::shared_ptr<Diskstream> ds;
3916 boost::shared_ptr<Playlist> from_playlist;
3917 vector<RegionView*> new_selection;
3918 typedef set<boost::shared_ptr<Playlist> > PlaylistSet;
3919 PlaylistSet modified_playlists;
3920 PlaylistSet frozen_playlists;
3921 list <sigc::connection> modified_playlist_connections;
3922 pair<PlaylistSet::iterator,bool> insert_result, frozen_insert_result;
3923 nframes64_t drag_delta;
3924 bool changed_tracks, changed_position;
3926 /* first_move is set to false if the regionview has been moved in the
3930 if (drag_info.first_move) {
3937 if (Config->get_edit_mode() == Splice && !pre_drag_region_selection.empty()) {
3938 selection->set (pre_drag_region_selection);
3939 pre_drag_region_selection.clear ();
3942 if (drag_info.brushing) {
3943 /* all changes were made during motion event handlers */
3945 if (drag_info.copy) {
3946 for (list<RegionView*>::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
3947 copies.push_back (*i);
3956 /* reverse this here so that we have the correct logic to finalize
3960 if (Config->get_edit_mode() == Lock && !drag_info.copy) {
3961 drag_info.x_constrained = !drag_info.x_constrained;
3964 if (drag_info.copy) {
3965 if (drag_info.x_constrained) {
3966 op_string = _("fixed time region copy");
3968 op_string = _("region copy");
3971 if (drag_info.x_constrained) {
3972 op_string = _("fixed time region drag");
3974 op_string = _("region drag");
3978 begin_reversible_command (op_string);
3980 changed_position = (drag_info.last_frame_position != (nframes64_t) (clicked_regionview->region()->position()));
3981 changed_tracks = (trackview_by_y_position (drag_info.current_pointer_y) != &clicked_regionview->get_time_axis_view());
3983 drag_delta = clicked_regionview->region()->position() - drag_info.last_frame_position;
3985 track_canvas->update_now ();
3987 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ) {
3989 RegionView* rv = (*i);
3990 double ix1, ix2, iy1, iy2;
3991 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3992 rv->get_canvas_frame()->i2w (ix1, iy1);
3993 iy1 += vertical_adjustment.get_value() - canvas_timebars_vsize;
3995 TimeAxisView* dest_tv = trackview_by_y_position (iy1);
3996 RouteTimeAxisView* dest_rtv = dynamic_cast<RouteTimeAxisView*>(dest_tv);
3999 if (rv->region()->locked()) {
4004 changed_position = (drag_info.last_frame_position != (nframes_t) (rv->region()->position()));
4005 changed_tracks = (dest_tv != &rv->get_time_axis_view());
4007 if (changed_position && !drag_info.x_constrained) {
4008 where = (nframes_t) unit_to_frame (ix1);
4010 where = rv->region()->position();
4013 boost::shared_ptr<Region> new_region;
4015 if (drag_info.copy) {
4016 /* we already made a copy */
4017 new_region = rv->region();
4019 /* undo the previous hide_dependent_views so that xfades don't
4020 disappear on copying regions
4023 //rv->get_time_axis_view().reveal_dependent_views (*rv);
4025 } else if (changed_tracks && dest_rtv->playlist()) {
4026 new_region = RegionFactory::create (rv->region());
4029 if (changed_tracks || drag_info.copy) {
4031 boost::shared_ptr<Playlist> to_playlist = dest_rtv->playlist();
4037 latest_regionviews.clear ();
4039 sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
4041 insert_result = modified_playlists.insert (to_playlist);
4042 if (insert_result.second) {
4043 session->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
4046 to_playlist->add_region (new_region, where);
4050 if (!latest_regionviews.empty()) {
4051 // XXX why just the first one ? we only expect one
4052 // commented out in nick_m's canvas reworking. is that intended?
4053 //dest_atv->reveal_dependent_views (*latest_regionviews.front());
4054 new_selection.push_back (latest_regionviews.front());
4059 motion on the same track. plonk the previously reparented region
4060 back to its original canvas group (its streamview).
4061 No need to do anything for copies as they are fake regions which will be deleted.
4064 rv->get_canvas_group()->reparent (*dest_rtv->view()->canvas_item());
4065 rv->get_canvas_group()->property_y() = 0;
4067 /* just change the model */
4069 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
4071 insert_result = modified_playlists.insert (playlist);
4072 if (insert_result.second) {
4073 session->add_command (new MementoCommand<Playlist>(*playlist, &playlist->get_state(), 0));
4075 /* freeze to avoid lots of relayering in the case of a multi-region drag */
4076 frozen_insert_result = frozen_playlists.insert(playlist);
4077 if (frozen_insert_result.second) {
4081 rv->region()->set_position (where, (void*) this);
4084 if (changed_tracks && !drag_info.copy) {
4086 /* get the playlist where this drag started. we can't use rv->region()->playlist()
4087 because we may have copied the region and it has not been attached to a playlist.
4090 assert ((source_tv = dynamic_cast<RouteTimeAxisView*> (&rv->get_time_axis_view())));
4091 assert ((ds = source_tv->get_diskstream()));
4092 assert ((from_playlist = ds->playlist()));
4094 /* moved to a different audio track, without copying */
4096 /* the region that used to be in the old playlist is not
4097 moved to the new one - we use a copy of it. as a result,
4098 any existing editor for the region should no longer be
4102 rv->hide_region_editor();
4103 rv->fake_set_opaque (false);
4105 /* remove the region from the old playlist */
4107 insert_result = modified_playlists.insert (from_playlist);
4108 if (insert_result.second) {
4109 session->add_command (new MementoCommand<Playlist>(*from_playlist, &from_playlist->get_state(), 0));
4112 from_playlist->remove_region ((rv->region()));
4114 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
4115 was selected in all of them, then removing it from a playlist will have removed all
4116 trace of it from the selection (i.e. there were N regions selected, we removed 1,
4117 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
4118 corresponding regionview, and the selection is now empty).
4120 this could have invalidated any and all iterators into the region selection.
4122 the heuristic we use here is: if the region selection is empty, break out of the loop
4123 here. if the region selection is not empty, then restart the loop because we know that
4124 we must have removed at least the region(view) we've just been working on as well as any
4125 that we processed on previous iterations.
4127 EXCEPT .... if we are doing a copy drag, then the selection hasn't been modified and
4128 we can just iterate.
4131 if (selection->regions.empty()) {
4134 i = selection->regions.by_layer().begin();
4141 if (drag_info.copy) {
4142 copies.push_back (rv);
4146 if (new_selection.empty()) {
4147 if (drag_info.copy) {
4148 /* the region(view)s that are selected and being dragged around
4149 are copies and do not belong to any track. remove them
4150 from the selection right here.
4152 selection->clear_regions();
4155 /* this will clear any existing selection that would have been
4156 cleared in the other clause above
4158 selection->set (new_selection);
4161 for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
4167 for (set<boost::shared_ptr<Playlist> >::iterator p = modified_playlists.begin(); p != modified_playlists.end(); ++p) {
4168 session->add_command (new MementoCommand<Playlist>(*(*p), 0, &(*p)->get_state()));
4170 commit_reversible_command ();
4173 for (vector<RegionView*>::iterator x = copies.begin(); x != copies.end(); ++x) {
4180 Editor::create_region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
4182 if (drag_info.move_threshold_passed) {
4183 if (drag_info.first_move) {
4184 // TODO: create region-create-drag region view here
4185 drag_info.first_move = false;
4188 // TODO: resize region-create-drag region view here
4193 Editor::create_region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
4195 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (drag_info.dest_trackview);
4199 const boost::shared_ptr<MidiDiskstream> diskstream =
4200 boost::dynamic_pointer_cast<MidiDiskstream>(mtv->view()->trackview().track()->diskstream());
4203 warning << "Cannot create non-MIDI region" << endl;
4207 if (drag_info.first_move) {
4208 begin_reversible_command (_("create region"));
4209 XMLNode &before = mtv->playlist()->get_state();
4211 nframes64_t start = drag_info.grab_frame;
4212 snap_to (start, -1);
4213 const Meter& m = session->tempo_map().meter_at(start);
4214 const Tempo& t = session->tempo_map().tempo_at(start);
4215 double length = floor (m.frames_per_bar(t, session->frame_rate()));
4217 boost::shared_ptr<Source> src = session->create_midi_source_for_session(*diskstream.get());
4219 mtv->playlist()->add_region (boost::dynamic_pointer_cast<MidiRegion>
4220 (RegionFactory::create(src, 0, (nframes_t) length,
4221 PBD::basename_nosuffix(src->name()))), start);
4222 XMLNode &after = mtv->playlist()->get_state();
4223 session->add_command(new MementoCommand<Playlist>(*mtv->playlist().get(), &before, &after));
4224 commit_reversible_command();
4227 create_region_drag_motion_callback (item, event);
4228 // TODO: create region-create-drag region here
4233 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
4235 /* Either add to or set the set the region selection, unless
4236 this is an alignment click (control used)
4239 if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
4240 TimeAxisView* tv = &rv.get_time_axis_view();
4241 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
4243 if (rtv && rtv->is_track()) {
4244 speed = rtv->get_diskstream()->speed();
4247 nframes64_t where = get_preferred_edit_position();
4251 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
4253 align_region (rv.region(), SyncPoint, (nframes64_t) (where * speed));
4255 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
4257 align_region (rv.region(), End, (nframes64_t) (where * speed));
4261 align_region (rv.region(), Start, (nframes64_t) (where * speed));
4268 Editor::show_verbose_time_cursor (nframes64_t frame, double offset, double xpos, double ypos)
4274 nframes64_t frame_rate;
4283 if (Profile->get_sae() || Profile->get_small_screen()) {
4284 m = ARDOUR_UI::instance()->primary_clock.mode();
4286 m = ARDOUR_UI::instance()->secondary_clock.mode();
4290 case AudioClock::BBT:
4291 session->bbt_time (frame, bbt);
4292 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, bbt.bars, bbt.beats, bbt.ticks);
4295 case AudioClock::SMPTE:
4296 session->smpte_time (frame, smpte);
4297 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
4300 case AudioClock::MinSec:
4301 /* XXX this is copied from show_verbose_duration_cursor() */
4302 frame_rate = session->frame_rate();
4303 hours = frame / (frame_rate * 3600);
4304 frame = frame % (frame_rate * 3600);
4305 mins = frame / (frame_rate * 60);
4306 frame = frame % (frame_rate * 60);
4307 secs = (float) frame / (float) frame_rate;
4308 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
4312 snprintf (buf, sizeof(buf), "%" PRIi64, frame);
4316 if (xpos >= 0 && ypos >=0) {
4317 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
4320 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset - horizontal_adjustment.get_value(), drag_info.current_pointer_y + offset - vertical_adjustment.get_value() + canvas_timebars_vsize);
4322 show_verbose_canvas_cursor ();
4326 Editor::show_verbose_duration_cursor (nframes64_t start, nframes64_t end, double offset, double xpos, double ypos)
4333 nframes64_t distance, frame_rate;
4335 Meter meter_at_start(session->tempo_map().meter_at(start));
4343 if (Profile->get_sae() || Profile->get_small_screen()) {
4344 m = ARDOUR_UI::instance()->primary_clock.mode ();
4346 m = ARDOUR_UI::instance()->secondary_clock.mode ();
4350 case AudioClock::BBT:
4351 session->bbt_time (start, sbbt);
4352 session->bbt_time (end, ebbt);
4355 /* XXX this computation won't work well if the
4356 user makes a selection that spans any meter changes.
4359 ebbt.bars -= sbbt.bars;
4360 if (ebbt.beats >= sbbt.beats) {
4361 ebbt.beats -= sbbt.beats;
4364 ebbt.beats = int(meter_at_start.beats_per_bar()) + ebbt.beats - sbbt.beats;
4366 if (ebbt.ticks >= sbbt.ticks) {
4367 ebbt.ticks -= sbbt.ticks;
4370 ebbt.ticks = int(Meter::ticks_per_beat) + ebbt.ticks - sbbt.ticks;
4373 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, ebbt.bars, ebbt.beats, ebbt.ticks);
4376 case AudioClock::SMPTE:
4377 session->smpte_duration (end - start, smpte);
4378 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
4381 case AudioClock::MinSec:
4382 /* XXX this stuff should be elsewhere.. */
4383 distance = end - start;
4384 frame_rate = session->frame_rate();
4385 hours = distance / (frame_rate * 3600);
4386 distance = distance % (frame_rate * 3600);
4387 mins = distance / (frame_rate * 60);
4388 distance = distance % (frame_rate * 60);
4389 secs = (float) distance / (float) frame_rate;
4390 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
4394 snprintf (buf, sizeof(buf), "%" PRIi64, end - start);
4398 if (xpos >= 0 && ypos >=0) {
4399 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
4402 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
4405 show_verbose_canvas_cursor ();
4409 Editor::collect_new_region_view (RegionView* rv)
4411 latest_regionviews.push_back (rv);
4415 Editor::collect_and_select_new_region_view (RegionView* rv)
4418 latest_regionviews.push_back (rv);
4422 Editor::start_selection_grab (ArdourCanvas::Item* item, GdkEvent* event)
4424 if (clicked_regionview == 0) {
4428 /* lets try to create new Region for the selection */
4430 vector<boost::shared_ptr<Region> > new_regions;
4431 create_region_from_selection (new_regions);
4433 if (new_regions.empty()) {
4437 /* XXX fix me one day to use all new regions */
4439 boost::shared_ptr<Region> region (new_regions.front());
4441 /* add it to the current stream/playlist.
4443 tricky: the streamview for the track will add a new regionview. we will
4444 catch the signal it sends when it creates the regionview to
4445 set the regionview we want to then drag.
4448 latest_regionviews.clear();
4449 sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
4451 /* A selection grab currently creates two undo/redo operations, one for
4452 creating the new region and another for moving it.
4455 begin_reversible_command (_("selection grab"));
4457 boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
4459 XMLNode *before = &(playlist->get_state());
4460 clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
4461 XMLNode *after = &(playlist->get_state());
4462 session->add_command(new MementoCommand<Playlist>(*playlist, before, after));
4464 commit_reversible_command ();
4468 if (latest_regionviews.empty()) {
4469 /* something went wrong */
4473 /* we need to deselect all other regionviews, and select this one
4474 i'm ignoring undo stuff, because the region creation will take care of it
4476 selection->set (latest_regionviews);
4478 drag_info.item = latest_regionviews.front()->get_canvas_group();
4479 drag_info.data = latest_regionviews.front();
4480 drag_info.motion_callback = &Editor::region_drag_motion_callback;
4481 drag_info.finished_callback = &Editor::region_drag_finished_callback;
4485 drag_info.source_trackview = clicked_routeview;
4486 drag_info.dest_trackview = drag_info.source_trackview;
4487 drag_info.last_frame_position = latest_regionviews.front()->region()->position();
4488 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
4490 show_verbose_time_cursor (drag_info.last_frame_position, 10);
4494 Editor::cancel_selection ()
4496 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4497 (*i)->hide_selection ();
4499 selection->clear ();
4500 clicked_selection = 0;
4504 Editor::start_selection_op (ArdourCanvas::Item* item, GdkEvent* event, SelectionOp op)
4506 nframes64_t start = 0;
4507 nframes64_t end = 0;
4513 drag_info.item = item;
4514 drag_info.motion_callback = &Editor::drag_selection;
4515 drag_info.finished_callback = &Editor::end_selection_op;
4520 case CreateSelection:
4521 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
4522 drag_info.copy = true;
4524 drag_info.copy = false;
4526 start_grab (event, selector_cursor);
4529 case SelectionStartTrim:
4530 if (clicked_axisview) {
4531 clicked_axisview->order_selection_trims (item, true);
4533 start_grab (event, trimmer_cursor);
4534 start = selection->time[clicked_selection].start;
4535 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
4538 case SelectionEndTrim:
4539 if (clicked_axisview) {
4540 clicked_axisview->order_selection_trims (item, false);
4542 start_grab (event, trimmer_cursor);
4543 end = selection->time[clicked_selection].end;
4544 drag_info.pointer_frame_offset = drag_info.grab_frame - end;
4548 start = selection->time[clicked_selection].start;
4550 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
4554 if (selection_op == SelectionMove) {
4555 show_verbose_time_cursor(start, 10);
4557 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4562 Editor::drag_selection (ArdourCanvas::Item* item, GdkEvent* event)
4564 nframes64_t start = 0;
4565 nframes64_t end = 0;
4567 nframes64_t pending_position;
4569 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
4570 pending_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
4572 pending_position = 0;
4575 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4576 snap_to (pending_position);
4579 /* only alter selection if the current frame is
4580 different from the last frame position (adjusted)
4583 if (pending_position == drag_info.last_pointer_frame) return;
4585 switch (selection_op) {
4586 case CreateSelection:
4588 if (drag_info.first_move) {
4589 snap_to (drag_info.grab_frame);
4592 if (pending_position < drag_info.grab_frame) {
4593 start = pending_position;
4594 end = drag_info.grab_frame;
4596 end = pending_position;
4597 start = drag_info.grab_frame;
4600 /* first drag: Either add to the selection
4601 or create a new selection->
4604 if (drag_info.first_move) {
4606 begin_reversible_command (_("range selection"));
4608 if (drag_info.copy) {
4609 /* adding to the selection */
4610 clicked_selection = selection->add (start, end);
4611 drag_info.copy = false;
4613 /* new selection-> */
4614 clicked_selection = selection->set (clicked_axisview, start, end);
4619 case SelectionStartTrim:
4621 if (drag_info.first_move) {
4622 begin_reversible_command (_("trim selection start"));
4625 start = selection->time[clicked_selection].start;
4626 end = selection->time[clicked_selection].end;
4628 if (pending_position > end) {
4631 start = pending_position;
4635 case SelectionEndTrim:
4637 if (drag_info.first_move) {
4638 begin_reversible_command (_("trim selection end"));
4641 start = selection->time[clicked_selection].start;
4642 end = selection->time[clicked_selection].end;
4644 if (pending_position < start) {
4647 end = pending_position;
4654 if (drag_info.first_move) {
4655 begin_reversible_command (_("move selection"));
4658 start = selection->time[clicked_selection].start;
4659 end = selection->time[clicked_selection].end;
4661 length = end - start;
4663 start = pending_position;
4666 end = start + length;
4671 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
4672 start_canvas_autoscroll (1, 0);
4676 selection->replace (clicked_selection, start, end);
4679 drag_info.last_pointer_frame = pending_position;
4680 drag_info.first_move = false;
4682 if (selection_op == SelectionMove) {
4683 show_verbose_time_cursor(start, 10);
4685 show_verbose_time_cursor(pending_position, 10);
4690 Editor::end_selection_op (ArdourCanvas::Item* item, GdkEvent* event)
4692 if (!drag_info.first_move) {
4693 drag_selection (item, event);
4694 /* XXX this is not object-oriented programming at all. ick */
4695 if (selection->time.consolidate()) {
4696 selection->TimeChanged ();
4698 commit_reversible_command ();
4700 /* just a click, no pointer movement.*/
4702 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
4704 selection->clear_time();
4709 /* XXX what happens if its a music selection? */
4710 session->set_audio_range (selection->time);
4711 stop_canvas_autoscroll ();
4715 Editor::start_trim (ArdourCanvas::Item* item, GdkEvent* event)
4718 TimeAxisView* tvp = clicked_axisview;
4719 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4721 if (tv && tv->is_track()) {
4722 speed = tv->get_diskstream()->speed();
4725 nframes64_t region_start = (nframes64_t) (clicked_regionview->region()->position() / speed);
4726 nframes64_t region_end = (nframes64_t) (clicked_regionview->region()->last_frame() / speed);
4727 nframes64_t region_length = (nframes64_t) (clicked_regionview->region()->length() / speed);
4729 //drag_info.item = clicked_regionview->get_name_highlight();
4730 drag_info.item = item;
4731 drag_info.motion_callback = &Editor::trim_motion_callback;
4732 drag_info.finished_callback = &Editor::trim_finished_callback;
4734 start_grab (event, trimmer_cursor);
4736 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
4737 trim_op = ContentsTrim;
4739 /* These will get overridden for a point trim.*/
4740 if (drag_info.current_pointer_frame < (region_start + region_length/2)) {
4741 /* closer to start */
4742 trim_op = StartTrim;
4743 } else if (drag_info.current_pointer_frame > (region_end - region_length/2)) {
4751 show_verbose_time_cursor(region_start, 10);
4754 show_verbose_time_cursor(region_end, 10);
4757 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4763 Editor::trim_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
4765 RegionView* rv = clicked_regionview;
4766 nframes64_t frame_delta = 0;
4767 bool left_direction;
4768 bool obey_snap = !Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier());
4770 /* snap modifier works differently here..
4771 its' current state has to be passed to the
4772 various trim functions in order to work properly
4776 TimeAxisView* tvp = clicked_axisview;
4777 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4778 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
4780 if (tv && tv->is_track()) {
4781 speed = tv->get_diskstream()->speed();
4784 if (drag_info.last_pointer_frame > drag_info.current_pointer_frame) {
4785 left_direction = true;
4787 left_direction = false;
4791 snap_to (drag_info.current_pointer_frame);
4794 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
4798 if (drag_info.first_move) {
4804 trim_type = "Region start trim";
4807 trim_type = "Region end trim";
4810 trim_type = "Region content trim";
4814 begin_reversible_command (trim_type);
4816 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4817 (*i)->fake_set_opaque(false);
4818 (*i)->region()->freeze ();
4820 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
4822 arv->temporarily_hide_envelope ();
4824 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4825 insert_result = motion_frozen_playlists.insert (pl);
4826 if (insert_result.second) {
4827 session->add_command(new MementoCommand<Playlist>(*pl, &pl->get_state(), 0));
4833 if (left_direction) {
4834 frame_delta = (drag_info.last_pointer_frame - drag_info.current_pointer_frame);
4836 frame_delta = (drag_info.current_pointer_frame - drag_info.last_pointer_frame);
4841 if ((left_direction == false) && (drag_info.current_pointer_frame <= rv->region()->first_frame()/speed)) {
4844 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4845 single_start_trim (**i, frame_delta, left_direction, obey_snap);
4851 if ((left_direction == true) && (drag_info.current_pointer_frame > (nframes64_t) (rv->region()->last_frame()/speed))) {
4854 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4855 single_end_trim (**i, frame_delta, left_direction, obey_snap);
4862 bool swap_direction = false;
4864 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
4865 swap_direction = true;
4868 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4869 i != selection->regions.by_layer().end(); ++i)
4871 single_contents_trim (**i, frame_delta, left_direction, swap_direction, obey_snap);
4879 show_verbose_time_cursor((nframes64_t) (rv->region()->position()/speed), 10);
4882 show_verbose_time_cursor((nframes64_t) (rv->region()->last_frame()/speed), 10);
4885 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4889 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4890 drag_info.first_move = false;
4894 Editor::single_contents_trim (RegionView& rv, nframes64_t frame_delta, bool left_direction, bool swap_direction, bool obey_snap)
4896 boost::shared_ptr<Region> region (rv.region());
4898 if (region->locked()) {
4902 nframes64_t new_bound;
4905 TimeAxisView* tvp = clicked_axisview;
4906 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4908 if (tv && tv->is_track()) {
4909 speed = tv->get_diskstream()->speed();
4912 if (left_direction) {
4913 if (swap_direction) {
4914 new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
4916 new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
4919 if (swap_direction) {
4920 new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
4922 new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
4927 snap_to (new_bound);
4929 region->trim_start ((nframes64_t) (new_bound * speed), this);
4930 rv.region_changed (StartChanged);
4934 Editor::single_start_trim (RegionView& rv, nframes64_t frame_delta, bool left_direction, bool obey_snap)
4936 boost::shared_ptr<Region> region (rv.region());
4938 if (region->locked()) {
4942 nframes64_t new_bound;
4945 TimeAxisView* tvp = clicked_axisview;
4946 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4948 if (tv && tv->is_track()) {
4949 speed = tv->get_diskstream()->speed();
4952 if (left_direction) {
4953 new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
4955 new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
4959 snap_to (new_bound, (left_direction ? 0 : 1));
4962 region->trim_front ((nframes64_t) (new_bound * speed), this);
4964 rv.region_changed (Change (LengthChanged|PositionChanged|StartChanged));
4968 Editor::single_end_trim (RegionView& rv, nframes64_t frame_delta, bool left_direction, bool obey_snap)
4970 boost::shared_ptr<Region> region (rv.region());
4972 if (region->locked()) {
4976 nframes64_t new_bound;
4979 TimeAxisView* tvp = clicked_axisview;
4980 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4982 if (tv && tv->is_track()) {
4983 speed = tv->get_diskstream()->speed();
4986 if (left_direction) {
4987 new_bound = (nframes64_t) ((region->last_frame() + 1)/speed) - frame_delta;
4989 new_bound = (nframes64_t) ((region->last_frame() + 1)/speed) + frame_delta;
4993 snap_to (new_bound);
4995 region->trim_end ((nframes64_t) (new_bound * speed), this);
4996 rv.region_changed (LengthChanged);
5000 Editor::trim_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
5002 if (!drag_info.first_move) {
5003 trim_motion_callback (item, event);
5005 if (!selection->selected (clicked_regionview)) {
5006 thaw_region_after_trim (*clicked_regionview);
5009 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
5010 i != selection->regions.by_layer().end(); ++i)
5012 thaw_region_after_trim (**i);
5013 (*i)->fake_set_opaque (true);
5017 for (set<boost::shared_ptr<Playlist> >::iterator p = motion_frozen_playlists.begin(); p != motion_frozen_playlists.end(); ++p) {
5019 session->add_command (new MementoCommand<Playlist>(*(*p).get(), 0, &(*p)->get_state()));
5022 motion_frozen_playlists.clear ();
5024 commit_reversible_command();
5026 /* no mouse movement */
5032 Editor::point_trim (GdkEvent* event)
5034 RegionView* rv = clicked_regionview;
5035 nframes64_t new_bound = drag_info.current_pointer_frame;
5037 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
5038 snap_to (new_bound);
5041 /* Choose action dependant on which button was pressed */
5042 switch (event->button.button) {
5044 trim_op = StartTrim;
5045 begin_reversible_command (_("Start point trim"));
5047 if (selection->selected (rv)) {
5049 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
5050 i != selection->regions.by_layer().end(); ++i)
5052 if (!(*i)->region()->locked()) {
5053 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
5054 XMLNode &before = pl->get_state();
5055 (*i)->region()->trim_front (new_bound, this);
5056 XMLNode &after = pl->get_state();
5057 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
5063 if (!rv->region()->locked()) {
5064 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
5065 XMLNode &before = pl->get_state();
5066 rv->region()->trim_front (new_bound, this);
5067 XMLNode &after = pl->get_state();
5068 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
5072 commit_reversible_command();
5077 begin_reversible_command (_("End point trim"));
5079 if (selection->selected (rv)) {
5081 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
5083 if (!(*i)->region()->locked()) {
5084 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
5085 XMLNode &before = pl->get_state();
5086 (*i)->region()->trim_end (new_bound, this);
5087 XMLNode &after = pl->get_state();
5088 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
5094 if (!rv->region()->locked()) {
5095 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
5096 XMLNode &before = pl->get_state();
5097 rv->region()->trim_end (new_bound, this);
5098 XMLNode &after = pl->get_state();
5099 session->add_command (new MementoCommand<Playlist>(*pl.get(), &before, &after));
5103 commit_reversible_command();
5112 Editor::thaw_region_after_trim (RegionView& rv)
5114 boost::shared_ptr<Region> region (rv.region());
5116 if (region->locked()) {
5120 region->thaw (_("trimmed region"));
5121 XMLNode &after = region->playlist()->get_state();
5122 session->add_command (new MementoCommand<Playlist>(*(region->playlist()), 0, &after));
5124 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(&rv);
5126 arv->unhide_envelope ();
5130 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* event)
5135 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
5136 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
5140 Location* location = find_location_from_marker (marker, is_start);
5141 location->set_hidden (true, this);
5146 Editor::start_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event, RangeMarkerOp op)
5152 drag_info.item = item;
5153 drag_info.motion_callback = &Editor::drag_range_markerbar_op;
5154 drag_info.finished_callback = &Editor::end_range_markerbar_op;
5156 range_marker_op = op;
5158 if (!temp_location) {
5159 temp_location = new Location;
5163 case CreateRangeMarker:
5164 case CreateTransportMarker:
5165 case CreateCDMarker:
5167 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
5168 drag_info.copy = true;
5170 drag_info.copy = false;
5172 start_grab (event, selector_cursor);
5176 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
5181 Editor::drag_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
5183 nframes64_t start = 0;
5184 nframes64_t end = 0;
5185 ArdourCanvas::SimpleRect *crect;
5187 switch (range_marker_op) {
5188 case CreateRangeMarker:
5189 crect = range_bar_drag_rect;
5191 case CreateTransportMarker:
5192 crect = transport_bar_drag_rect;
5194 case CreateCDMarker:
5195 crect = cd_marker_bar_drag_rect;
5198 cerr << "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()" << endl;
5203 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
5204 snap_to (drag_info.current_pointer_frame);
5207 /* only alter selection if the current frame is
5208 different from the last frame position.
5211 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
5213 switch (range_marker_op) {
5214 case CreateRangeMarker:
5215 case CreateTransportMarker:
5216 case CreateCDMarker:
5217 if (drag_info.first_move) {
5218 snap_to (drag_info.grab_frame);
5221 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
5222 start = drag_info.current_pointer_frame;
5223 end = drag_info.grab_frame;
5225 end = drag_info.current_pointer_frame;
5226 start = drag_info.grab_frame;
5229 /* first drag: Either add to the selection
5230 or create a new selection.
5233 if (drag_info.first_move) {
5235 temp_location->set (start, end);
5239 update_marker_drag_item (temp_location);
5240 range_marker_drag_rect->show();
5241 //range_marker_drag_rect->raise_to_top();
5247 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
5248 start_canvas_autoscroll (1, 0);
5252 temp_location->set (start, end);
5254 double x1 = frame_to_pixel (start);
5255 double x2 = frame_to_pixel (end);
5256 crect->property_x1() = x1;
5257 crect->property_x2() = x2;
5259 update_marker_drag_item (temp_location);
5262 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
5263 drag_info.first_move = false;
5265 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
5270 Editor::end_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
5272 Location * newloc = 0;
5276 if (!drag_info.first_move) {
5277 drag_range_markerbar_op (item, event);
5279 switch (range_marker_op) {
5280 case CreateRangeMarker:
5281 case CreateCDMarker:
5283 begin_reversible_command (_("new range marker"));
5284 XMLNode &before = session->locations()->get_state();
5285 session->locations()->next_available_name(rangename,"unnamed");
5286 if (range_marker_op == CreateCDMarker) {
5287 flags = Location::IsRangeMarker|Location::IsCDMarker;
5288 cd_marker_bar_drag_rect->hide();
5291 flags = Location::IsRangeMarker;
5292 range_bar_drag_rect->hide();
5294 newloc = new Location(temp_location->start(), temp_location->end(), rangename, (Location::Flags) flags);
5295 session->locations()->add (newloc, true);
5296 XMLNode &after = session->locations()->get_state();
5297 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
5298 commit_reversible_command ();
5300 range_marker_drag_rect->hide();
5304 case CreateTransportMarker:
5305 // popup menu to pick loop or punch
5306 new_transport_marker_context_menu (&event->button, item);
5311 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
5313 if (Keyboard::no_modifier_keys_pressed (&event->button) && range_marker_op != CreateCDMarker) {
5318 start = session->locations()->first_mark_before (drag_info.grab_frame);
5319 end = session->locations()->first_mark_after (drag_info.grab_frame);
5321 if (end == max_frames) {
5322 end = session->current_end_frame ();
5326 start = session->current_start_frame ();
5329 switch (mouse_mode) {
5331 /* find the two markers on either side and then make the selection from it */
5332 select_all_within (start, end, 0.0f, FLT_MAX, track_views, Selection::Set);
5336 /* find the two markers on either side of the click and make the range out of it */
5337 selection->set (0, start, end);
5346 stop_canvas_autoscroll ();
5352 Editor::start_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
5354 drag_info.item = item;
5355 drag_info.motion_callback = &Editor::drag_mouse_zoom;
5356 drag_info.finished_callback = &Editor::end_mouse_zoom;
5358 start_grab (event, zoom_cursor);
5360 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5364 Editor::drag_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
5369 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
5370 snap_to (drag_info.current_pointer_frame);
5372 if (drag_info.first_move) {
5373 snap_to (drag_info.grab_frame);
5377 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
5379 /* base start and end on initial click position */
5380 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
5381 start = drag_info.current_pointer_frame;
5382 end = drag_info.grab_frame;
5384 end = drag_info.current_pointer_frame;
5385 start = drag_info.grab_frame;
5390 if (drag_info.first_move) {
5392 zoom_rect->raise_to_top();
5395 reposition_zoom_rect(start, end);
5397 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
5398 drag_info.first_move = false;
5400 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5405 Editor::end_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
5407 if (!drag_info.first_move) {
5408 drag_mouse_zoom (item, event);
5410 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
5411 temporal_zoom_by_frame (drag_info.grab_frame, drag_info.last_pointer_frame, "mouse zoom");
5413 temporal_zoom_by_frame (drag_info.last_pointer_frame, drag_info.grab_frame, "mouse zoom");
5416 temporal_zoom_to_frame (false, drag_info.grab_frame);
5418 temporal_zoom_step (false);
5419 center_screen (drag_info.grab_frame);
5427 Editor::reposition_zoom_rect (nframes64_t start, nframes64_t end)
5429 double x1 = frame_to_pixel (start);
5430 double x2 = frame_to_pixel (end);
5431 double y2 = full_canvas_height - 1.0;
5433 zoom_rect->property_x1() = x1;
5434 zoom_rect->property_y1() = 1.0;
5435 zoom_rect->property_x2() = x2;
5436 zoom_rect->property_y2() = y2;
5440 Editor::start_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
5442 drag_info.item = item;
5443 drag_info.motion_callback = &Editor::drag_rubberband_select;
5444 drag_info.finished_callback = &Editor::end_rubberband_select;
5446 start_grab (event, cross_hair_cursor);
5448 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5452 Editor::drag_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
5459 /* use a bigger drag threshold than the default */
5461 if (abs ((int) (drag_info.current_pointer_frame - drag_info.grab_frame)) < 8) {
5465 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier()) && Config->get_rubberbanding_snaps_to_grid()) {
5466 if (drag_info.first_move) {
5467 snap_to (drag_info.grab_frame);
5469 snap_to (drag_info.current_pointer_frame);
5472 /* base start and end on initial click position */
5474 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
5475 start = drag_info.current_pointer_frame;
5476 end = drag_info.grab_frame;
5478 end = drag_info.current_pointer_frame;
5479 start = drag_info.grab_frame;
5482 if (drag_info.current_pointer_y < drag_info.grab_y) {
5483 y1 = drag_info.current_pointer_y;
5484 y2 = drag_info.grab_y;
5486 y2 = drag_info.current_pointer_y;
5487 y1 = drag_info.grab_y;
5491 if (start != end || y1 != y2) {
5493 double x1 = frame_to_pixel (start);
5494 double x2 = frame_to_pixel (end);
5496 rubberband_rect->property_x1() = x1;
5497 rubberband_rect->property_y1() = y1;
5498 rubberband_rect->property_x2() = x2;
5499 rubberband_rect->property_y2() = y2;
5501 rubberband_rect->show();
5502 rubberband_rect->raise_to_top();
5504 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
5505 drag_info.first_move = false;
5507 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5512 Editor::end_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
5514 if (!drag_info.first_move) {
5516 drag_rubberband_select (item, event);
5519 if (drag_info.current_pointer_y < drag_info.grab_y) {
5520 y1 = drag_info.current_pointer_y;
5521 y2 = drag_info.grab_y;
5523 y2 = drag_info.current_pointer_y;
5524 y1 = drag_info.grab_y;
5528 Selection::Operation op = Keyboard::selection_type (event->button.state);
5531 begin_reversible_command (_("rubberband selection"));
5533 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
5534 commit = select_all_within (drag_info.grab_frame, drag_info.last_pointer_frame, y1, y2, track_views, op);
5536 commit = select_all_within (drag_info.last_pointer_frame, drag_info.grab_frame, y1, y2, track_views, op);
5540 commit_reversible_command ();
5544 selection->clear_tracks();
5545 selection->clear_regions();
5546 selection->clear_points ();
5547 selection->clear_lines ();
5550 rubberband_rect->hide();
5555 Editor::mouse_rename_region (ArdourCanvas::Item* item, GdkEvent* event)
5557 using namespace Gtkmm2ext;
5559 ArdourPrompter prompter (false);
5561 prompter.set_prompt (_("Name for region:"));
5562 prompter.set_initial_text (clicked_regionview->region()->name());
5563 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
5564 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
5565 prompter.show_all ();
5566 switch (prompter.run ()) {
5567 case Gtk::RESPONSE_ACCEPT:
5569 prompter.get_result(str);
5571 clicked_regionview->region()->set_name (str);
5579 Editor::start_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
5581 drag_info.item = item;
5582 drag_info.motion_callback = &Editor::time_fx_motion;
5583 drag_info.finished_callback = &Editor::end_time_fx;
5587 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5591 Editor::time_fx_motion (ArdourCanvas::Item *item, GdkEvent* event)
5593 RegionView* rv = clicked_regionview;
5595 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
5596 snap_to (drag_info.current_pointer_frame);
5599 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
5603 if (drag_info.current_pointer_frame > rv->region()->position()) {
5604 rv->get_time_axis_view().show_timestretch (rv->region()->position(), drag_info.current_pointer_frame);
5607 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
5608 drag_info.first_move = false;
5610 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5614 Editor::end_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
5616 clicked_regionview->get_time_axis_view().hide_timestretch ();
5618 if (drag_info.first_move) {
5622 if (drag_info.last_pointer_frame < clicked_regionview->region()->position()) {
5623 /* backwards drag of the left edge - not usable */
5627 nframes64_t newlen = drag_info.last_pointer_frame - clicked_regionview->region()->position();
5629 float percentage = (double) newlen / (double) clicked_regionview->region()->length();
5631 #ifndef USE_RUBBERBAND
5632 // Soundtouch uses percentage / 100 instead of normal (/ 1)
5633 if (clicked_regionview->region()->data_type() == DataType::AUDIO) {
5634 percentage = (float) ((double) newlen - (double) clicked_regionview->region()->length()) / ((double) newlen) * 100.0f;
5638 begin_reversible_command (_("timestretch"));
5640 // XXX how do timeFX on multiple regions ?
5643 rs.add (clicked_regionview);
5645 if (time_stretch (rs, percentage) == 0) {
5646 session->commit_reversible_command ();
5651 Editor::mouse_brush_insert_region (RegionView* rv, nframes64_t pos)
5653 /* no brushing without a useful snap setting */
5655 switch (snap_mode) {
5657 return; /* can't work because it allows region to be placed anywhere */
5662 switch (snap_type) {
5670 /* don't brush a copy over the original */
5672 if (pos == rv->region()->position()) {
5676 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
5678 if (rtv == 0 || !rtv->is_track()) {
5682 boost::shared_ptr<Playlist> playlist = rtv->playlist();
5683 double speed = rtv->get_diskstream()->speed();
5685 XMLNode &before = playlist->get_state();
5686 playlist->add_region (RegionFactory::create (rv->region()), (nframes64_t) (pos * speed));
5687 XMLNode &after = playlist->get_state();
5688 session->add_command(new MementoCommand<Playlist>(*playlist.get(), &before, &after));
5690 // playlist is frozen, so we have to update manually
5692 playlist->Modified(); /* EMIT SIGNAL */
5696 Editor::track_height_step_timeout ()
5698 if (get_microseconds() - last_track_height_step_timestamp < 250000) {
5699 current_stepping_trackview = 0;