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 if (changed_position && !drag_info.x_constrained) {
4005 where = rv->region()->position() - drag_delta;
4007 where = rv->region()->position();
4010 boost::shared_ptr<Region> new_region;
4012 if (drag_info.copy) {
4013 /* we already made a copy */
4014 new_region = rv->region();
4016 /* undo the previous hide_dependent_views so that xfades don't
4017 disappear on copying regions
4020 //rv->get_time_axis_view().reveal_dependent_views (*rv);
4022 } else if (changed_tracks && dest_rtv->playlist()) {
4023 new_region = RegionFactory::create (rv->region());
4026 if (changed_tracks || drag_info.copy) {
4028 boost::shared_ptr<Playlist> to_playlist = dest_rtv->playlist();
4034 latest_regionviews.clear ();
4036 sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
4038 insert_result = modified_playlists.insert (to_playlist);
4039 if (insert_result.second) {
4040 session->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
4043 to_playlist->add_region (new_region, where);
4047 if (!latest_regionviews.empty()) {
4048 // XXX why just the first one ? we only expect one
4049 // commented out in nick_m's canvas reworking. is that intended?
4050 //dest_atv->reveal_dependent_views (*latest_regionviews.front());
4051 new_selection.push_back (latest_regionviews.front());
4056 motion on the same track. plonk the previously reparented region
4057 back to its original canvas group (its streamview).
4058 No need to do anything for copies as they are fake regions which will be deleted.
4061 rv->get_canvas_group()->reparent (*dest_rtv->view()->canvas_item());
4062 rv->get_canvas_group()->property_y() = 0;
4064 /* just change the model */
4066 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
4068 insert_result = modified_playlists.insert (playlist);
4069 if (insert_result.second) {
4070 session->add_command (new MementoCommand<Playlist>(*playlist, &playlist->get_state(), 0));
4072 /* freeze to avoid lots of relayering in the case of a multi-region drag */
4073 frozen_insert_result = frozen_playlists.insert(playlist);
4074 if (frozen_insert_result.second) {
4078 rv->region()->set_position (where, (void*) this);
4081 if (changed_tracks && !drag_info.copy) {
4083 /* get the playlist where this drag started. we can't use rv->region()->playlist()
4084 because we may have copied the region and it has not been attached to a playlist.
4087 assert ((source_tv = dynamic_cast<RouteTimeAxisView*> (&rv->get_time_axis_view())));
4088 assert ((ds = source_tv->get_diskstream()));
4089 assert ((from_playlist = ds->playlist()));
4091 /* moved to a different audio track, without copying */
4093 /* the region that used to be in the old playlist is not
4094 moved to the new one - we use a copy of it. as a result,
4095 any existing editor for the region should no longer be
4099 rv->hide_region_editor();
4100 rv->fake_set_opaque (false);
4102 /* remove the region from the old playlist */
4104 insert_result = modified_playlists.insert (from_playlist);
4105 if (insert_result.second) {
4106 session->add_command (new MementoCommand<Playlist>(*from_playlist, &from_playlist->get_state(), 0));
4109 from_playlist->remove_region ((rv->region()));
4111 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
4112 was selected in all of them, then removing it from a playlist will have removed all
4113 trace of it from the selection (i.e. there were N regions selected, we removed 1,
4114 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
4115 corresponding regionview, and the selection is now empty).
4117 this could have invalidated any and all iterators into the region selection.
4119 the heuristic we use here is: if the region selection is empty, break out of the loop
4120 here. if the region selection is not empty, then restart the loop because we know that
4121 we must have removed at least the region(view) we've just been working on as well as any
4122 that we processed on previous iterations.
4124 EXCEPT .... if we are doing a copy drag, then the selection hasn't been modified and
4125 we can just iterate.
4128 if (selection->regions.empty()) {
4131 i = selection->regions.by_layer().begin();
4138 if (drag_info.copy) {
4139 copies.push_back (rv);
4143 if (new_selection.empty()) {
4144 if (drag_info.copy) {
4145 /* the region(view)s that are selected and being dragged around
4146 are copies and do not belong to any track. remove them
4147 from the selection right here.
4149 selection->clear_regions();
4152 /* this will clear any existing selection that would have been
4153 cleared in the other clause above
4155 selection->set (new_selection);
4158 for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
4164 for (set<boost::shared_ptr<Playlist> >::iterator p = modified_playlists.begin(); p != modified_playlists.end(); ++p) {
4165 session->add_command (new MementoCommand<Playlist>(*(*p), 0, &(*p)->get_state()));
4167 commit_reversible_command ();
4170 for (vector<RegionView*>::iterator x = copies.begin(); x != copies.end(); ++x) {
4177 Editor::create_region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
4179 if (drag_info.move_threshold_passed) {
4180 if (drag_info.first_move) {
4181 // TODO: create region-create-drag region view here
4182 drag_info.first_move = false;
4185 // TODO: resize region-create-drag region view here
4190 Editor::create_region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
4192 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (drag_info.dest_trackview);
4196 const boost::shared_ptr<MidiDiskstream> diskstream =
4197 boost::dynamic_pointer_cast<MidiDiskstream>(mtv->view()->trackview().track()->diskstream());
4200 warning << "Cannot create non-MIDI region" << endl;
4204 if (drag_info.first_move) {
4205 begin_reversible_command (_("create region"));
4206 XMLNode &before = mtv->playlist()->get_state();
4208 nframes64_t start = drag_info.grab_frame;
4209 snap_to (start, -1);
4210 const Meter& m = session->tempo_map().meter_at(start);
4211 const Tempo& t = session->tempo_map().tempo_at(start);
4212 double length = floor (m.frames_per_bar(t, session->frame_rate()));
4214 boost::shared_ptr<Source> src = session->create_midi_source_for_session(*diskstream.get());
4216 mtv->playlist()->add_region (boost::dynamic_pointer_cast<MidiRegion>
4217 (RegionFactory::create(src, 0, (nframes_t) length,
4218 PBD::basename_nosuffix(src->name()))), start);
4219 XMLNode &after = mtv->playlist()->get_state();
4220 session->add_command(new MementoCommand<Playlist>(*mtv->playlist().get(), &before, &after));
4221 commit_reversible_command();
4224 create_region_drag_motion_callback (item, event);
4225 // TODO: create region-create-drag region here
4230 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
4232 /* Either add to or set the set the region selection, unless
4233 this is an alignment click (control used)
4236 if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
4237 TimeAxisView* tv = &rv.get_time_axis_view();
4238 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
4240 if (rtv && rtv->is_track()) {
4241 speed = rtv->get_diskstream()->speed();
4244 nframes64_t where = get_preferred_edit_position();
4248 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
4250 align_region (rv.region(), SyncPoint, (nframes64_t) (where * speed));
4252 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
4254 align_region (rv.region(), End, (nframes64_t) (where * speed));
4258 align_region (rv.region(), Start, (nframes64_t) (where * speed));
4265 Editor::show_verbose_time_cursor (nframes64_t frame, double offset, double xpos, double ypos)
4271 nframes64_t frame_rate;
4280 if (Profile->get_sae() || Profile->get_small_screen()) {
4281 m = ARDOUR_UI::instance()->primary_clock.mode();
4283 m = ARDOUR_UI::instance()->secondary_clock.mode();
4287 case AudioClock::BBT:
4288 session->bbt_time (frame, bbt);
4289 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, bbt.bars, bbt.beats, bbt.ticks);
4292 case AudioClock::SMPTE:
4293 session->smpte_time (frame, smpte);
4294 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
4297 case AudioClock::MinSec:
4298 /* XXX this is copied from show_verbose_duration_cursor() */
4299 frame_rate = session->frame_rate();
4300 hours = frame / (frame_rate * 3600);
4301 frame = frame % (frame_rate * 3600);
4302 mins = frame / (frame_rate * 60);
4303 frame = frame % (frame_rate * 60);
4304 secs = (float) frame / (float) frame_rate;
4305 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
4309 snprintf (buf, sizeof(buf), "%" PRIi64, frame);
4313 if (xpos >= 0 && ypos >=0) {
4314 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
4317 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);
4319 show_verbose_canvas_cursor ();
4323 Editor::show_verbose_duration_cursor (nframes64_t start, nframes64_t end, double offset, double xpos, double ypos)
4330 nframes64_t distance, frame_rate;
4332 Meter meter_at_start(session->tempo_map().meter_at(start));
4340 if (Profile->get_sae() || Profile->get_small_screen()) {
4341 m = ARDOUR_UI::instance()->primary_clock.mode ();
4343 m = ARDOUR_UI::instance()->secondary_clock.mode ();
4347 case AudioClock::BBT:
4348 session->bbt_time (start, sbbt);
4349 session->bbt_time (end, ebbt);
4352 /* XXX this computation won't work well if the
4353 user makes a selection that spans any meter changes.
4356 ebbt.bars -= sbbt.bars;
4357 if (ebbt.beats >= sbbt.beats) {
4358 ebbt.beats -= sbbt.beats;
4361 ebbt.beats = int(meter_at_start.beats_per_bar()) + ebbt.beats - sbbt.beats;
4363 if (ebbt.ticks >= sbbt.ticks) {
4364 ebbt.ticks -= sbbt.ticks;
4367 ebbt.ticks = int(Meter::ticks_per_beat) + ebbt.ticks - sbbt.ticks;
4370 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, ebbt.bars, ebbt.beats, ebbt.ticks);
4373 case AudioClock::SMPTE:
4374 session->smpte_duration (end - start, smpte);
4375 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
4378 case AudioClock::MinSec:
4379 /* XXX this stuff should be elsewhere.. */
4380 distance = end - start;
4381 frame_rate = session->frame_rate();
4382 hours = distance / (frame_rate * 3600);
4383 distance = distance % (frame_rate * 3600);
4384 mins = distance / (frame_rate * 60);
4385 distance = distance % (frame_rate * 60);
4386 secs = (float) distance / (float) frame_rate;
4387 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
4391 snprintf (buf, sizeof(buf), "%" PRIi64, end - start);
4395 if (xpos >= 0 && ypos >=0) {
4396 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
4399 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
4402 show_verbose_canvas_cursor ();
4406 Editor::collect_new_region_view (RegionView* rv)
4408 latest_regionviews.push_back (rv);
4412 Editor::collect_and_select_new_region_view (RegionView* rv)
4415 latest_regionviews.push_back (rv);
4419 Editor::start_selection_grab (ArdourCanvas::Item* item, GdkEvent* event)
4421 if (clicked_regionview == 0) {
4425 /* lets try to create new Region for the selection */
4427 vector<boost::shared_ptr<Region> > new_regions;
4428 create_region_from_selection (new_regions);
4430 if (new_regions.empty()) {
4434 /* XXX fix me one day to use all new regions */
4436 boost::shared_ptr<Region> region (new_regions.front());
4438 /* add it to the current stream/playlist.
4440 tricky: the streamview for the track will add a new regionview. we will
4441 catch the signal it sends when it creates the regionview to
4442 set the regionview we want to then drag.
4445 latest_regionviews.clear();
4446 sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
4448 /* A selection grab currently creates two undo/redo operations, one for
4449 creating the new region and another for moving it.
4452 begin_reversible_command (_("selection grab"));
4454 boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
4456 XMLNode *before = &(playlist->get_state());
4457 clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
4458 XMLNode *after = &(playlist->get_state());
4459 session->add_command(new MementoCommand<Playlist>(*playlist, before, after));
4461 commit_reversible_command ();
4465 if (latest_regionviews.empty()) {
4466 /* something went wrong */
4470 /* we need to deselect all other regionviews, and select this one
4471 i'm ignoring undo stuff, because the region creation will take care of it
4473 selection->set (latest_regionviews);
4475 drag_info.item = latest_regionviews.front()->get_canvas_group();
4476 drag_info.data = latest_regionviews.front();
4477 drag_info.motion_callback = &Editor::region_drag_motion_callback;
4478 drag_info.finished_callback = &Editor::region_drag_finished_callback;
4482 drag_info.source_trackview = clicked_routeview;
4483 drag_info.dest_trackview = drag_info.source_trackview;
4484 drag_info.last_frame_position = latest_regionviews.front()->region()->position();
4485 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
4487 show_verbose_time_cursor (drag_info.last_frame_position, 10);
4491 Editor::cancel_selection ()
4493 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4494 (*i)->hide_selection ();
4496 selection->clear ();
4497 clicked_selection = 0;
4501 Editor::start_selection_op (ArdourCanvas::Item* item, GdkEvent* event, SelectionOp op)
4503 nframes64_t start = 0;
4504 nframes64_t end = 0;
4510 drag_info.item = item;
4511 drag_info.motion_callback = &Editor::drag_selection;
4512 drag_info.finished_callback = &Editor::end_selection_op;
4517 case CreateSelection:
4518 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
4519 drag_info.copy = true;
4521 drag_info.copy = false;
4523 start_grab (event, selector_cursor);
4526 case SelectionStartTrim:
4527 if (clicked_axisview) {
4528 clicked_axisview->order_selection_trims (item, true);
4530 start_grab (event, trimmer_cursor);
4531 start = selection->time[clicked_selection].start;
4532 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
4535 case SelectionEndTrim:
4536 if (clicked_axisview) {
4537 clicked_axisview->order_selection_trims (item, false);
4539 start_grab (event, trimmer_cursor);
4540 end = selection->time[clicked_selection].end;
4541 drag_info.pointer_frame_offset = drag_info.grab_frame - end;
4545 start = selection->time[clicked_selection].start;
4547 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
4551 if (selection_op == SelectionMove) {
4552 show_verbose_time_cursor(start, 10);
4554 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4559 Editor::drag_selection (ArdourCanvas::Item* item, GdkEvent* event)
4561 nframes64_t start = 0;
4562 nframes64_t end = 0;
4564 nframes64_t pending_position;
4566 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
4567 pending_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
4569 pending_position = 0;
4572 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4573 snap_to (pending_position);
4576 /* only alter selection if the current frame is
4577 different from the last frame position (adjusted)
4580 if (pending_position == drag_info.last_pointer_frame) return;
4582 switch (selection_op) {
4583 case CreateSelection:
4585 if (drag_info.first_move) {
4586 snap_to (drag_info.grab_frame);
4589 if (pending_position < drag_info.grab_frame) {
4590 start = pending_position;
4591 end = drag_info.grab_frame;
4593 end = pending_position;
4594 start = drag_info.grab_frame;
4597 /* first drag: Either add to the selection
4598 or create a new selection->
4601 if (drag_info.first_move) {
4603 begin_reversible_command (_("range selection"));
4605 if (drag_info.copy) {
4606 /* adding to the selection */
4607 clicked_selection = selection->add (start, end);
4608 drag_info.copy = false;
4610 /* new selection-> */
4611 clicked_selection = selection->set (clicked_axisview, start, end);
4616 case SelectionStartTrim:
4618 if (drag_info.first_move) {
4619 begin_reversible_command (_("trim selection start"));
4622 start = selection->time[clicked_selection].start;
4623 end = selection->time[clicked_selection].end;
4625 if (pending_position > end) {
4628 start = pending_position;
4632 case SelectionEndTrim:
4634 if (drag_info.first_move) {
4635 begin_reversible_command (_("trim selection end"));
4638 start = selection->time[clicked_selection].start;
4639 end = selection->time[clicked_selection].end;
4641 if (pending_position < start) {
4644 end = pending_position;
4651 if (drag_info.first_move) {
4652 begin_reversible_command (_("move selection"));
4655 start = selection->time[clicked_selection].start;
4656 end = selection->time[clicked_selection].end;
4658 length = end - start;
4660 start = pending_position;
4663 end = start + length;
4668 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
4669 start_canvas_autoscroll (1, 0);
4673 selection->replace (clicked_selection, start, end);
4676 drag_info.last_pointer_frame = pending_position;
4677 drag_info.first_move = false;
4679 if (selection_op == SelectionMove) {
4680 show_verbose_time_cursor(start, 10);
4682 show_verbose_time_cursor(pending_position, 10);
4687 Editor::end_selection_op (ArdourCanvas::Item* item, GdkEvent* event)
4689 if (!drag_info.first_move) {
4690 drag_selection (item, event);
4691 /* XXX this is not object-oriented programming at all. ick */
4692 if (selection->time.consolidate()) {
4693 selection->TimeChanged ();
4695 commit_reversible_command ();
4697 /* just a click, no pointer movement.*/
4699 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
4701 selection->clear_time();
4706 /* XXX what happens if its a music selection? */
4707 session->set_audio_range (selection->time);
4708 stop_canvas_autoscroll ();
4712 Editor::start_trim (ArdourCanvas::Item* item, GdkEvent* event)
4715 TimeAxisView* tvp = clicked_axisview;
4716 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4718 if (tv && tv->is_track()) {
4719 speed = tv->get_diskstream()->speed();
4722 nframes64_t region_start = (nframes64_t) (clicked_regionview->region()->position() / speed);
4723 nframes64_t region_end = (nframes64_t) (clicked_regionview->region()->last_frame() / speed);
4724 nframes64_t region_length = (nframes64_t) (clicked_regionview->region()->length() / speed);
4726 //drag_info.item = clicked_regionview->get_name_highlight();
4727 drag_info.item = item;
4728 drag_info.motion_callback = &Editor::trim_motion_callback;
4729 drag_info.finished_callback = &Editor::trim_finished_callback;
4731 start_grab (event, trimmer_cursor);
4733 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
4734 trim_op = ContentsTrim;
4736 /* These will get overridden for a point trim.*/
4737 if (drag_info.current_pointer_frame < (region_start + region_length/2)) {
4738 /* closer to start */
4739 trim_op = StartTrim;
4740 } else if (drag_info.current_pointer_frame > (region_end - region_length/2)) {
4748 show_verbose_time_cursor(region_start, 10);
4751 show_verbose_time_cursor(region_end, 10);
4754 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4760 Editor::trim_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
4762 RegionView* rv = clicked_regionview;
4763 nframes64_t frame_delta = 0;
4764 bool left_direction;
4765 bool obey_snap = !Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier());
4767 /* snap modifier works differently here..
4768 its' current state has to be passed to the
4769 various trim functions in order to work properly
4773 TimeAxisView* tvp = clicked_axisview;
4774 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4775 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
4777 if (tv && tv->is_track()) {
4778 speed = tv->get_diskstream()->speed();
4781 if (drag_info.last_pointer_frame > drag_info.current_pointer_frame) {
4782 left_direction = true;
4784 left_direction = false;
4788 snap_to (drag_info.current_pointer_frame);
4791 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
4795 if (drag_info.first_move) {
4801 trim_type = "Region start trim";
4804 trim_type = "Region end trim";
4807 trim_type = "Region content trim";
4811 begin_reversible_command (trim_type);
4813 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4814 (*i)->fake_set_opaque(false);
4815 (*i)->region()->freeze ();
4817 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
4819 arv->temporarily_hide_envelope ();
4821 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4822 insert_result = motion_frozen_playlists.insert (pl);
4823 if (insert_result.second) {
4824 session->add_command(new MementoCommand<Playlist>(*pl, &pl->get_state(), 0));
4830 if (left_direction) {
4831 frame_delta = (drag_info.last_pointer_frame - drag_info.current_pointer_frame);
4833 frame_delta = (drag_info.current_pointer_frame - drag_info.last_pointer_frame);
4838 if ((left_direction == false) && (drag_info.current_pointer_frame <= rv->region()->first_frame()/speed)) {
4841 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4842 single_start_trim (**i, frame_delta, left_direction, obey_snap);
4848 if ((left_direction == true) && (drag_info.current_pointer_frame > (nframes64_t) (rv->region()->last_frame()/speed))) {
4851 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4852 single_end_trim (**i, frame_delta, left_direction, obey_snap);
4859 bool swap_direction = false;
4861 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
4862 swap_direction = true;
4865 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4866 i != selection->regions.by_layer().end(); ++i)
4868 single_contents_trim (**i, frame_delta, left_direction, swap_direction, obey_snap);
4876 show_verbose_time_cursor((nframes64_t) (rv->region()->position()/speed), 10);
4879 show_verbose_time_cursor((nframes64_t) (rv->region()->last_frame()/speed), 10);
4882 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4886 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4887 drag_info.first_move = false;
4891 Editor::single_contents_trim (RegionView& rv, nframes64_t frame_delta, bool left_direction, bool swap_direction, bool obey_snap)
4893 boost::shared_ptr<Region> region (rv.region());
4895 if (region->locked()) {
4899 nframes64_t new_bound;
4902 TimeAxisView* tvp = clicked_axisview;
4903 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4905 if (tv && tv->is_track()) {
4906 speed = tv->get_diskstream()->speed();
4909 if (left_direction) {
4910 if (swap_direction) {
4911 new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
4913 new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
4916 if (swap_direction) {
4917 new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
4919 new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
4924 snap_to (new_bound);
4926 region->trim_start ((nframes64_t) (new_bound * speed), this);
4927 rv.region_changed (StartChanged);
4931 Editor::single_start_trim (RegionView& rv, nframes64_t frame_delta, bool left_direction, bool obey_snap)
4933 boost::shared_ptr<Region> region (rv.region());
4935 if (region->locked()) {
4939 nframes64_t new_bound;
4942 TimeAxisView* tvp = clicked_axisview;
4943 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4945 if (tv && tv->is_track()) {
4946 speed = tv->get_diskstream()->speed();
4949 if (left_direction) {
4950 new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
4952 new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
4956 snap_to (new_bound, (left_direction ? 0 : 1));
4959 region->trim_front ((nframes64_t) (new_bound * speed), this);
4961 rv.region_changed (Change (LengthChanged|PositionChanged|StartChanged));
4965 Editor::single_end_trim (RegionView& rv, nframes64_t frame_delta, bool left_direction, bool obey_snap)
4967 boost::shared_ptr<Region> region (rv.region());
4969 if (region->locked()) {
4973 nframes64_t new_bound;
4976 TimeAxisView* tvp = clicked_axisview;
4977 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4979 if (tv && tv->is_track()) {
4980 speed = tv->get_diskstream()->speed();
4983 if (left_direction) {
4984 new_bound = (nframes64_t) ((region->last_frame() + 1)/speed) - frame_delta;
4986 new_bound = (nframes64_t) ((region->last_frame() + 1)/speed) + frame_delta;
4990 snap_to (new_bound);
4992 region->trim_end ((nframes64_t) (new_bound * speed), this);
4993 rv.region_changed (LengthChanged);
4997 Editor::trim_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
4999 if (!drag_info.first_move) {
5000 trim_motion_callback (item, event);
5002 if (!selection->selected (clicked_regionview)) {
5003 thaw_region_after_trim (*clicked_regionview);
5006 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
5007 i != selection->regions.by_layer().end(); ++i)
5009 thaw_region_after_trim (**i);
5010 (*i)->fake_set_opaque (true);
5014 for (set<boost::shared_ptr<Playlist> >::iterator p = motion_frozen_playlists.begin(); p != motion_frozen_playlists.end(); ++p) {
5016 session->add_command (new MementoCommand<Playlist>(*(*p).get(), 0, &(*p)->get_state()));
5019 motion_frozen_playlists.clear ();
5021 commit_reversible_command();
5023 /* no mouse movement */
5029 Editor::point_trim (GdkEvent* event)
5031 RegionView* rv = clicked_regionview;
5032 nframes64_t new_bound = drag_info.current_pointer_frame;
5034 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
5035 snap_to (new_bound);
5038 /* Choose action dependant on which button was pressed */
5039 switch (event->button.button) {
5041 trim_op = StartTrim;
5042 begin_reversible_command (_("Start point trim"));
5044 if (selection->selected (rv)) {
5046 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
5047 i != selection->regions.by_layer().end(); ++i)
5049 if (!(*i)->region()->locked()) {
5050 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
5051 XMLNode &before = pl->get_state();
5052 (*i)->region()->trim_front (new_bound, this);
5053 XMLNode &after = pl->get_state();
5054 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
5060 if (!rv->region()->locked()) {
5061 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
5062 XMLNode &before = pl->get_state();
5063 rv->region()->trim_front (new_bound, this);
5064 XMLNode &after = pl->get_state();
5065 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
5069 commit_reversible_command();
5074 begin_reversible_command (_("End point trim"));
5076 if (selection->selected (rv)) {
5078 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
5080 if (!(*i)->region()->locked()) {
5081 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
5082 XMLNode &before = pl->get_state();
5083 (*i)->region()->trim_end (new_bound, this);
5084 XMLNode &after = pl->get_state();
5085 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
5091 if (!rv->region()->locked()) {
5092 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
5093 XMLNode &before = pl->get_state();
5094 rv->region()->trim_end (new_bound, this);
5095 XMLNode &after = pl->get_state();
5096 session->add_command (new MementoCommand<Playlist>(*pl.get(), &before, &after));
5100 commit_reversible_command();
5109 Editor::thaw_region_after_trim (RegionView& rv)
5111 boost::shared_ptr<Region> region (rv.region());
5113 if (region->locked()) {
5117 region->thaw (_("trimmed region"));
5118 XMLNode &after = region->playlist()->get_state();
5119 session->add_command (new MementoCommand<Playlist>(*(region->playlist()), 0, &after));
5121 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(&rv);
5123 arv->unhide_envelope ();
5127 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* event)
5132 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
5133 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
5137 Location* location = find_location_from_marker (marker, is_start);
5138 location->set_hidden (true, this);
5143 Editor::start_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event, RangeMarkerOp op)
5149 drag_info.item = item;
5150 drag_info.motion_callback = &Editor::drag_range_markerbar_op;
5151 drag_info.finished_callback = &Editor::end_range_markerbar_op;
5153 range_marker_op = op;
5155 if (!temp_location) {
5156 temp_location = new Location;
5160 case CreateRangeMarker:
5161 case CreateTransportMarker:
5162 case CreateCDMarker:
5164 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
5165 drag_info.copy = true;
5167 drag_info.copy = false;
5169 start_grab (event, selector_cursor);
5173 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
5178 Editor::drag_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
5180 nframes64_t start = 0;
5181 nframes64_t end = 0;
5182 ArdourCanvas::SimpleRect *crect;
5184 switch (range_marker_op) {
5185 case CreateRangeMarker:
5186 crect = range_bar_drag_rect;
5188 case CreateTransportMarker:
5189 crect = transport_bar_drag_rect;
5191 case CreateCDMarker:
5192 crect = cd_marker_bar_drag_rect;
5195 cerr << "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()" << endl;
5200 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
5201 snap_to (drag_info.current_pointer_frame);
5204 /* only alter selection if the current frame is
5205 different from the last frame position.
5208 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
5210 switch (range_marker_op) {
5211 case CreateRangeMarker:
5212 case CreateTransportMarker:
5213 case CreateCDMarker:
5214 if (drag_info.first_move) {
5215 snap_to (drag_info.grab_frame);
5218 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
5219 start = drag_info.current_pointer_frame;
5220 end = drag_info.grab_frame;
5222 end = drag_info.current_pointer_frame;
5223 start = drag_info.grab_frame;
5226 /* first drag: Either add to the selection
5227 or create a new selection.
5230 if (drag_info.first_move) {
5232 temp_location->set (start, end);
5236 update_marker_drag_item (temp_location);
5237 range_marker_drag_rect->show();
5238 //range_marker_drag_rect->raise_to_top();
5244 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
5245 start_canvas_autoscroll (1, 0);
5249 temp_location->set (start, end);
5251 double x1 = frame_to_pixel (start);
5252 double x2 = frame_to_pixel (end);
5253 crect->property_x1() = x1;
5254 crect->property_x2() = x2;
5256 update_marker_drag_item (temp_location);
5259 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
5260 drag_info.first_move = false;
5262 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
5267 Editor::end_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
5269 Location * newloc = 0;
5273 if (!drag_info.first_move) {
5274 drag_range_markerbar_op (item, event);
5276 switch (range_marker_op) {
5277 case CreateRangeMarker:
5278 case CreateCDMarker:
5280 begin_reversible_command (_("new range marker"));
5281 XMLNode &before = session->locations()->get_state();
5282 session->locations()->next_available_name(rangename,"unnamed");
5283 if (range_marker_op == CreateCDMarker) {
5284 flags = Location::IsRangeMarker|Location::IsCDMarker;
5285 cd_marker_bar_drag_rect->hide();
5288 flags = Location::IsRangeMarker;
5289 range_bar_drag_rect->hide();
5291 newloc = new Location(temp_location->start(), temp_location->end(), rangename, (Location::Flags) flags);
5292 session->locations()->add (newloc, true);
5293 XMLNode &after = session->locations()->get_state();
5294 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
5295 commit_reversible_command ();
5297 range_marker_drag_rect->hide();
5301 case CreateTransportMarker:
5302 // popup menu to pick loop or punch
5303 new_transport_marker_context_menu (&event->button, item);
5308 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
5310 if (Keyboard::no_modifier_keys_pressed (&event->button) && range_marker_op != CreateCDMarker) {
5315 start = session->locations()->first_mark_before (drag_info.grab_frame);
5316 end = session->locations()->first_mark_after (drag_info.grab_frame);
5318 if (end == max_frames) {
5319 end = session->current_end_frame ();
5323 start = session->current_start_frame ();
5326 switch (mouse_mode) {
5328 /* find the two markers on either side and then make the selection from it */
5329 select_all_within (start, end, 0.0f, FLT_MAX, track_views, Selection::Set);
5333 /* find the two markers on either side of the click and make the range out of it */
5334 selection->set (0, start, end);
5343 stop_canvas_autoscroll ();
5349 Editor::start_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
5351 drag_info.item = item;
5352 drag_info.motion_callback = &Editor::drag_mouse_zoom;
5353 drag_info.finished_callback = &Editor::end_mouse_zoom;
5355 start_grab (event, zoom_cursor);
5357 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5361 Editor::drag_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
5366 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
5367 snap_to (drag_info.current_pointer_frame);
5369 if (drag_info.first_move) {
5370 snap_to (drag_info.grab_frame);
5374 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
5376 /* base start and end on initial click position */
5377 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
5378 start = drag_info.current_pointer_frame;
5379 end = drag_info.grab_frame;
5381 end = drag_info.current_pointer_frame;
5382 start = drag_info.grab_frame;
5387 if (drag_info.first_move) {
5389 zoom_rect->raise_to_top();
5392 reposition_zoom_rect(start, end);
5394 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
5395 drag_info.first_move = false;
5397 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5402 Editor::end_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
5404 if (!drag_info.first_move) {
5405 drag_mouse_zoom (item, event);
5407 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
5408 temporal_zoom_by_frame (drag_info.grab_frame, drag_info.last_pointer_frame, "mouse zoom");
5410 temporal_zoom_by_frame (drag_info.last_pointer_frame, drag_info.grab_frame, "mouse zoom");
5413 temporal_zoom_to_frame (false, drag_info.grab_frame);
5415 temporal_zoom_step (false);
5416 center_screen (drag_info.grab_frame);
5424 Editor::reposition_zoom_rect (nframes64_t start, nframes64_t end)
5426 double x1 = frame_to_pixel (start);
5427 double x2 = frame_to_pixel (end);
5428 double y2 = full_canvas_height - 1.0;
5430 zoom_rect->property_x1() = x1;
5431 zoom_rect->property_y1() = 1.0;
5432 zoom_rect->property_x2() = x2;
5433 zoom_rect->property_y2() = y2;
5437 Editor::start_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
5439 drag_info.item = item;
5440 drag_info.motion_callback = &Editor::drag_rubberband_select;
5441 drag_info.finished_callback = &Editor::end_rubberband_select;
5443 start_grab (event, cross_hair_cursor);
5445 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5449 Editor::drag_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
5456 /* use a bigger drag threshold than the default */
5458 if (abs ((int) (drag_info.current_pointer_frame - drag_info.grab_frame)) < 8) {
5462 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier()) && Config->get_rubberbanding_snaps_to_grid()) {
5463 if (drag_info.first_move) {
5464 snap_to (drag_info.grab_frame);
5466 snap_to (drag_info.current_pointer_frame);
5469 /* base start and end on initial click position */
5471 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
5472 start = drag_info.current_pointer_frame;
5473 end = drag_info.grab_frame;
5475 end = drag_info.current_pointer_frame;
5476 start = drag_info.grab_frame;
5479 if (drag_info.current_pointer_y < drag_info.grab_y) {
5480 y1 = drag_info.current_pointer_y;
5481 y2 = drag_info.grab_y;
5483 y2 = drag_info.current_pointer_y;
5484 y1 = drag_info.grab_y;
5488 if (start != end || y1 != y2) {
5490 double x1 = frame_to_pixel (start);
5491 double x2 = frame_to_pixel (end);
5493 rubberband_rect->property_x1() = x1;
5494 rubberband_rect->property_y1() = y1;
5495 rubberband_rect->property_x2() = x2;
5496 rubberband_rect->property_y2() = y2;
5498 rubberband_rect->show();
5499 rubberband_rect->raise_to_top();
5501 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
5502 drag_info.first_move = false;
5504 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5509 Editor::end_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
5511 if (!drag_info.first_move) {
5513 drag_rubberband_select (item, event);
5516 if (drag_info.current_pointer_y < drag_info.grab_y) {
5517 y1 = drag_info.current_pointer_y;
5518 y2 = drag_info.grab_y;
5520 y2 = drag_info.current_pointer_y;
5521 y1 = drag_info.grab_y;
5525 Selection::Operation op = Keyboard::selection_type (event->button.state);
5528 begin_reversible_command (_("rubberband selection"));
5530 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
5531 commit = select_all_within (drag_info.grab_frame, drag_info.last_pointer_frame, y1, y2, track_views, op);
5533 commit = select_all_within (drag_info.last_pointer_frame, drag_info.grab_frame, y1, y2, track_views, op);
5537 commit_reversible_command ();
5541 selection->clear_tracks();
5542 selection->clear_regions();
5543 selection->clear_points ();
5544 selection->clear_lines ();
5547 rubberband_rect->hide();
5552 Editor::mouse_rename_region (ArdourCanvas::Item* item, GdkEvent* event)
5554 using namespace Gtkmm2ext;
5556 ArdourPrompter prompter (false);
5558 prompter.set_prompt (_("Name for region:"));
5559 prompter.set_initial_text (clicked_regionview->region()->name());
5560 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
5561 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
5562 prompter.show_all ();
5563 switch (prompter.run ()) {
5564 case Gtk::RESPONSE_ACCEPT:
5566 prompter.get_result(str);
5568 clicked_regionview->region()->set_name (str);
5576 Editor::start_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
5578 drag_info.item = item;
5579 drag_info.motion_callback = &Editor::time_fx_motion;
5580 drag_info.finished_callback = &Editor::end_time_fx;
5584 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5588 Editor::time_fx_motion (ArdourCanvas::Item *item, GdkEvent* event)
5590 RegionView* rv = clicked_regionview;
5592 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
5593 snap_to (drag_info.current_pointer_frame);
5596 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
5600 if (drag_info.current_pointer_frame > rv->region()->position()) {
5601 rv->get_time_axis_view().show_timestretch (rv->region()->position(), drag_info.current_pointer_frame);
5604 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
5605 drag_info.first_move = false;
5607 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5611 Editor::end_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
5613 clicked_regionview->get_time_axis_view().hide_timestretch ();
5615 if (drag_info.first_move) {
5619 if (drag_info.last_pointer_frame < clicked_regionview->region()->position()) {
5620 /* backwards drag of the left edge - not usable */
5624 nframes64_t newlen = drag_info.last_pointer_frame - clicked_regionview->region()->position();
5626 float percentage = (double) newlen / (double) clicked_regionview->region()->length();
5628 #ifndef USE_RUBBERBAND
5629 // Soundtouch uses percentage / 100 instead of normal (/ 1)
5630 if (clicked_regionview->region()->data_type() == DataType::AUDIO) {
5631 percentage = (float) ((double) newlen - (double) clicked_regionview->region()->length()) / ((double) newlen) * 100.0f;
5635 begin_reversible_command (_("timestretch"));
5637 // XXX how do timeFX on multiple regions ?
5640 rs.add (clicked_regionview);
5642 if (time_stretch (rs, percentage) == 0) {
5643 session->commit_reversible_command ();
5648 Editor::mouse_brush_insert_region (RegionView* rv, nframes64_t pos)
5650 /* no brushing without a useful snap setting */
5652 switch (snap_mode) {
5654 return; /* can't work because it allows region to be placed anywhere */
5659 switch (snap_type) {
5667 /* don't brush a copy over the original */
5669 if (pos == rv->region()->position()) {
5673 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
5675 if (rtv == 0 || !rtv->is_track()) {
5679 boost::shared_ptr<Playlist> playlist = rtv->playlist();
5680 double speed = rtv->get_diskstream()->speed();
5682 XMLNode &before = playlist->get_state();
5683 playlist->add_region (RegionFactory::create (rv->region()), (nframes64_t) (pos * speed));
5684 XMLNode &after = playlist->get_state();
5685 session->add_command(new MementoCommand<Playlist>(*playlist.get(), &before, &after));
5687 // playlist is frozen, so we have to update manually
5689 playlist->Modified(); /* EMIT SIGNAL */
5693 Editor::track_height_step_timeout ()
5695 if (get_microseconds() - last_track_height_step_timestamp < 250000) {
5696 current_stepping_trackview = 0;