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 cx = drag_info.current_pointer_x;
3145 double cy = drag_info.grab_y + drag_info.cumulative_y_drag + dy;
3147 // calculate zero crossing point. back off by .01 to stay on the
3148 // positive side of zero
3150 double zero_gain_y = (1.0 - ZERO_GAIN_FRACTION) * line->height() - .01;
3151 line->parent_group().i2w(_unused, zero_gain_y);
3153 // make sure we hit zero when passing through
3154 if ((cy < zero_gain_y and (cy - dy) > zero_gain_y)
3155 or (cy > zero_gain_y and (cy - dy) < zero_gain_y)) {
3159 drag_info.cumulative_y_drag = cy - drag_info.grab_y;
3162 cy = min ((double) line->height(), cy);
3164 double fraction = 1.0 - (cy / line->height());
3168 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) {
3174 line->line_drag (current_line_drag_info.before, current_line_drag_info.after, fraction, push);
3176 set_verbose_canvas_cursor_text (line->get_verbose_cursor_string (fraction));
3180 Editor::line_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3182 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
3183 line_drag_motion_callback (item, event);
3188 Editor::start_region_grab (ArdourCanvas::Item* item, GdkEvent* event)
3190 if (selection->regions.empty() || clicked_regionview == 0) {
3193 _region_motion_group->raise_to_top ();
3194 drag_info.copy = false;
3195 drag_info.item = item;
3196 drag_info.data = clicked_regionview;
3198 if (Config->get_edit_mode() == Splice) {
3199 drag_info.motion_callback = &Editor::region_drag_splice_motion_callback;
3200 drag_info.finished_callback = &Editor::region_drag_splice_finished_callback;
3202 drag_info.motion_callback = &Editor::region_drag_motion_callback;
3203 drag_info.finished_callback = &Editor::region_drag_finished_callback;
3209 TimeAxisView* tvp = clicked_axisview;
3210 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
3212 if (tv && tv->is_track()) {
3213 speed = tv->get_diskstream()->speed();
3216 drag_info.last_frame_position = (nframes64_t) (clicked_regionview->region()->position() / speed);
3217 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
3218 drag_info.source_trackview = &clicked_regionview->get_time_axis_view();
3219 drag_info.dest_trackview = drag_info.source_trackview;
3220 // we want a move threshold
3221 drag_info.want_move_threshold = true;
3222 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3224 begin_reversible_command (_("move region(s)"));
3226 /* sync the canvas to what we think is its current state */
3227 track_canvas->update_now();
3231 Editor::start_create_region_grab (ArdourCanvas::Item* item, GdkEvent* event)
3233 drag_info.copy = false;
3234 drag_info.item = item;
3235 drag_info.data = clicked_axisview;
3236 drag_info.source_trackview = clicked_axisview;
3237 drag_info.dest_trackview = drag_info.source_trackview;
3238 drag_info.motion_callback = &Editor::create_region_drag_motion_callback;
3239 drag_info.finished_callback = &Editor::create_region_drag_finished_callback;
3245 Editor::start_region_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
3247 if (selection->regions.empty() || clicked_regionview == 0) {
3250 _region_motion_group->raise_to_top ();
3251 drag_info.copy = true;
3252 drag_info.item = item;
3253 drag_info.data = clicked_regionview;
3257 TimeAxisView* tv = &clicked_regionview->get_time_axis_view();
3258 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
3261 if (rtv && rtv->is_track()) {
3262 speed = rtv->get_diskstream()->speed();
3265 drag_info.source_trackview = &clicked_regionview->get_time_axis_view();
3266 drag_info.dest_trackview = drag_info.source_trackview;
3267 drag_info.last_frame_position = (nframes64_t) (clicked_regionview->region()->position() / speed);
3268 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
3269 // we want a move threshold
3270 drag_info.want_move_threshold = true;
3271 drag_info.motion_callback = &Editor::region_drag_motion_callback;
3272 drag_info.finished_callback = &Editor::region_drag_finished_callback;
3273 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3277 Editor::start_region_brush_grab (ArdourCanvas::Item* item, GdkEvent* event)
3279 if (selection->regions.empty() || clicked_regionview == 0 || Config->get_edit_mode() == Splice) {
3283 drag_info.copy = false;
3284 drag_info.item = item;
3285 drag_info.data = clicked_regionview;
3286 drag_info.motion_callback = &Editor::region_drag_motion_callback;
3287 drag_info.finished_callback = &Editor::region_drag_finished_callback;
3292 TimeAxisView* tvp = clicked_axisview;
3293 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
3295 if (tv && tv->is_track()) {
3296 speed = tv->get_diskstream()->speed();
3299 drag_info.last_frame_position = (nframes64_t) (clicked_regionview->region()->position() / speed);
3300 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
3301 drag_info.source_trackview = &clicked_regionview->get_time_axis_view();
3302 drag_info.dest_trackview = drag_info.source_trackview;
3303 // we want a move threshold
3304 drag_info.want_move_threshold = true;
3305 drag_info.brushing = true;
3307 begin_reversible_command (_("Drag region brush"));
3311 Editor::possibly_copy_regions_during_grab (GdkEvent* event)
3313 if (drag_info.copy && drag_info.move_threshold_passed && drag_info.want_move_threshold) {
3315 drag_info.want_move_threshold = false; // don't copy again
3317 /* duplicate the regionview(s) and region(s) */
3319 vector<RegionView*> new_regionviews;
3321 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3326 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
3327 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
3329 const boost::shared_ptr<const Region> original = rv->region();
3330 boost::shared_ptr<Region> region_copy = RegionFactory::create (original);
3333 boost::shared_ptr<AudioRegion> audioregion_copy
3334 = boost::dynamic_pointer_cast<AudioRegion>(region_copy);
3335 nrv = new AudioRegionView (*arv, audioregion_copy);
3337 boost::shared_ptr<MidiRegion> midiregion_copy
3338 = boost::dynamic_pointer_cast<MidiRegion>(region_copy);
3339 nrv = new MidiRegionView (*mrv, midiregion_copy);
3344 nrv->get_canvas_group()->show ();
3345 new_regionviews.push_back (nrv);
3348 if (new_regionviews.empty()) {
3352 /* reset selection to new regionviews. This will not set selection visual status for
3353 these regionviews since they don't belong to a track, so do that by hand too.
3356 selection->set (new_regionviews);
3358 for (vector<RegionView*>::iterator i = new_regionviews.begin(); i != new_regionviews.end(); ++i) {
3359 (*i)->set_selected (true);
3362 /* reset drag_info data to reflect the fact that we are dragging the copies */
3364 drag_info.data = new_regionviews.front();
3366 swap_grab (new_regionviews.front()->get_canvas_group (), 0, event->motion.time);
3368 sync the canvas to what we think is its current state
3369 without it, the canvas seems to
3370 "forget" to update properly after the upcoming reparent()
3371 ..only if the mouse is in rapid motion at the time of the grab.
3372 something to do with regionview creation raking so long?
3374 track_canvas->update_now();
3379 Editor::check_region_drag_possible (RouteTimeAxisView** tv)
3381 /* Which trackview is this ? */
3383 TimeAxisView* tvp = trackview_by_y_position (drag_info.current_pointer_y);
3384 (*tv) = dynamic_cast<RouteTimeAxisView*>(tvp);
3386 /* The region motion is only processed if the pointer is over
3390 if (!(*tv) || !(*tv)->is_track()) {
3391 /* To make sure we hide the verbose canvas cursor when the mouse is
3392 not held over and audiotrack.
3394 hide_verbose_canvas_cursor ();
3401 struct RegionSelectionByPosition {
3402 bool operator() (RegionView*a, RegionView* b) {
3403 return a->region()->position () < b->region()->position();
3408 Editor::region_drag_splice_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
3410 RouteTimeAxisView* tv;
3412 if (!check_region_drag_possible (&tv)) {
3416 if (!drag_info.move_threshold_passed) {
3422 if (drag_info.current_pointer_x - drag_info.grab_x > 0) {
3428 RegionSelection copy (selection->regions);
3430 RegionSelectionByPosition cmp;
3433 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
3435 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
3441 boost::shared_ptr<Playlist> playlist;
3443 if ((playlist = atv->playlist()) == 0) {
3447 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
3452 if (drag_info.current_pointer_frame < (*i)->region()->last_frame() + 1) {
3456 if (drag_info.current_pointer_frame > (*i)->region()->first_frame()) {
3462 playlist->shuffle ((*i)->region(), dir);
3464 drag_info.grab_x = drag_info.current_pointer_x;
3469 Editor::region_drag_splice_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3474 Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
3478 RegionView* rv = reinterpret_cast<RegionView*> (drag_info.data);
3479 nframes64_t pending_region_position = 0;
3480 int32_t pointer_y_span = 0, canvas_pointer_y_span = 0, original_pointer_order;
3481 int32_t visible_y_high = 0, visible_y_low = 512; //high meaning higher numbered.. not the height on the screen
3482 bool clamp_y_axis = false;
3483 vector<int32_t> height_list(512) ;
3484 vector<int32_t>::iterator j;
3485 RouteTimeAxisView* tv;
3487 possibly_copy_regions_during_grab (event);
3489 if (!check_region_drag_possible (&tv)) {
3493 original_pointer_order = drag_info.dest_trackview->order;
3495 /************************************************************
3497 ************************************************************/
3499 if (drag_info.brushing) {
3500 clamp_y_axis = true;
3505 if ((pointer_y_span = (drag_info.dest_trackview->order - tv->order)) != 0) {
3507 int32_t children = 0, numtracks = 0;
3508 // XXX hard coding track limit, oh my, so very very bad
3509 bitset <1024> tracks (0x00);
3510 /* get a bitmask representing the visible tracks */
3512 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
3513 TimeAxisView *tracklist_timeview;
3514 tracklist_timeview = (*i);
3515 RouteTimeAxisView* rtv2 = dynamic_cast<RouteTimeAxisView*>(tracklist_timeview);
3516 TimeAxisView::Children children_list;
3518 /* zeroes are audio tracks. ones are other types. */
3520 if (!rtv2->hidden()) {
3522 if (visible_y_high < rtv2->order) {
3523 visible_y_high = rtv2->order;
3525 if (visible_y_low > rtv2->order) {
3526 visible_y_low = rtv2->order;
3529 if (!rtv2->is_track()) {
3530 tracks = tracks |= (0x01 << rtv2->order);
3533 height_list[rtv2->order] = (*i)->current_height();
3536 if ((children_list = rtv2->get_child_list()).size() > 0) {
3537 for (TimeAxisView::Children::iterator j = children_list.begin(); j != children_list.end(); ++j) {
3538 tracks = tracks |= (0x01 << (rtv2->order + children));
3539 height_list[rtv2->order + children] = (*j)->current_height();
3547 /* find the actual span according to the canvas */
3549 canvas_pointer_y_span = pointer_y_span;
3550 if (drag_info.dest_trackview->order >= tv->order) {
3552 for (y = tv->order; y < drag_info.dest_trackview->order; y++) {
3553 if (height_list[y] == 0 ) {
3554 canvas_pointer_y_span--;
3559 for (y = drag_info.dest_trackview->order;y <= tv->order; y++) {
3560 if ( height_list[y] == 0 ) {
3561 canvas_pointer_y_span++;
3566 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3567 RegionView* rv2 = (*i);
3568 double ix1, ix2, iy1, iy2;
3571 if (rv2->region()->locked()) {
3575 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3576 rv2->get_canvas_frame()->i2w (ix1, iy1);
3577 iy1 += vertical_adjustment.get_value() - canvas_timebars_vsize;
3579 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3580 RouteTimeAxisView* rtv2 = dynamic_cast<RouteTimeAxisView*>(tvp2);
3582 if (rtv2->order != original_pointer_order) {
3583 /* this isn't the pointer track */
3585 if (canvas_pointer_y_span > 0) {
3587 /* moving up the canvas */
3588 if ((rtv2->order - canvas_pointer_y_span) >= visible_y_low) {
3590 int32_t visible_tracks = 0;
3591 while (visible_tracks < canvas_pointer_y_span ) {
3594 while (height_list[rtv2->order - (visible_tracks - n)] == 0) {
3595 /* we're passing through a hidden track */
3600 if (tracks[rtv2->order - (canvas_pointer_y_span - n)] != 0x00) {
3601 clamp_y_axis = true;
3605 clamp_y_axis = true;
3608 } else if (canvas_pointer_y_span < 0) {
3610 /*moving down the canvas*/
3612 if ((rtv2->order - (canvas_pointer_y_span - n)) <= visible_y_high) { // we will overflow
3615 int32_t visible_tracks = 0;
3617 while (visible_tracks > canvas_pointer_y_span ) {
3620 while (height_list[rtv2->order - (visible_tracks - n)] == 0) {
3624 if ( tracks[rtv2->order - ( canvas_pointer_y_span - n)] != 0x00) {
3625 clamp_y_axis = true;
3630 clamp_y_axis = true;
3636 /* this is the pointer's track */
3637 if ((rtv2->order - pointer_y_span) > visible_y_high) { // we will overflow
3638 clamp_y_axis = true;
3639 } else if ((rtv2->order - pointer_y_span) < visible_y_low) { // we will underflow
3640 clamp_y_axis = true;
3648 } else if (drag_info.dest_trackview == tv) {
3649 clamp_y_axis = true;
3653 if (!clamp_y_axis) {
3654 drag_info.dest_trackview = tv;
3657 /************************************************************
3659 ************************************************************/
3661 /* compute the amount of pointer motion in frames, and where
3662 the region would be if we moved it by that much.
3664 if ( drag_info.move_threshold_passed ) {
3666 if (drag_info.current_pointer_frame >= drag_info.pointer_frame_offset) {
3668 nframes64_t sync_frame;
3669 nframes64_t sync_offset;
3672 pending_region_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
3674 sync_offset = rv->region()->sync_offset (sync_dir);
3676 /* we don't handle a sync point that lies before zero.
3678 if (sync_dir >= 0 || (sync_dir < 0 && pending_region_position >= sync_offset)) {
3679 sync_frame = pending_region_position + (sync_dir*sync_offset);
3681 /* we snap if the snap modifier is not enabled.
3684 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3685 snap_to (sync_frame);
3688 pending_region_position = rv->region()->adjust_to_sync (sync_frame);
3691 pending_region_position = drag_info.last_frame_position;
3695 pending_region_position = 0;
3698 if (pending_region_position > max_frames - rv->region()->length()) {
3699 pending_region_position = drag_info.last_frame_position;
3702 // printf ("3: pending_region_position= %lu %lu\n", pending_region_position, drag_info.last_frame_position );
3704 bool x_move_allowed;
3706 if (Config->get_edit_mode() == Lock) {
3707 if (drag_info.copy) {
3708 x_move_allowed = !drag_info.x_constrained;
3710 /* in locked edit mode, reverse the usual meaning of x_constrained */
3711 x_move_allowed = drag_info.x_constrained;
3714 x_move_allowed = !drag_info.x_constrained;
3717 if (( pending_region_position != drag_info.last_frame_position) && x_move_allowed ) {
3719 /* now compute the canvas unit distance we need to move the regionview
3720 to make it appear at the new location.
3723 if (pending_region_position > drag_info.last_frame_position) {
3724 x_delta = ((double) (pending_region_position - drag_info.last_frame_position) / frames_per_unit);
3726 x_delta = -((double) (drag_info.last_frame_position - pending_region_position) / frames_per_unit);
3727 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3729 RegionView* rv2 = (*i);
3731 // If any regionview is at zero, we need to know so we can stop further leftward motion.
3733 double ix1, ix2, iy1, iy2;
3734 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3735 rv2->get_canvas_frame()->i2w (ix1, iy1);
3737 if (-x_delta > ix1 + horizontal_adjustment.get_value()) {
3740 pending_region_position = drag_info.last_frame_position;
3747 drag_info.last_frame_position = pending_region_position;
3754 /* threshold not passed */
3759 /*************************************************************
3761 ************************************************************/
3763 if (x_delta == 0 && (pointer_y_span == 0)) {
3764 /* haven't reached next snap point, and we're not switching
3765 trackviews. nothing to do.
3770 /*************************************************************
3772 ************************************************************/
3773 bool do_move = true;
3774 if (drag_info.first_move) {
3775 if (!drag_info.move_threshold_passed) {
3782 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
3783 const list<RegionView*>& layered_regions = selection->regions.by_layer();
3785 for (list<RegionView*>::const_iterator i = layered_regions.begin(); i != layered_regions.end(); ++i) {
3787 RegionView* rv = (*i);
3788 double ix1, ix2, iy1, iy2;
3789 int32_t temp_pointer_y_span = pointer_y_span;
3791 if (rv->region()->locked()) {
3795 /* get item BBox, which will be relative to parent. so we have
3796 to query on a child, then convert to world coordinates using
3800 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3801 rv->get_canvas_frame()->i2w (ix1, iy1);
3803 cerr << "adjust y from " << iy1 << " using "
3804 << vertical_adjustment.get_value() << " - "
3805 << canvas_timebars_vsize
3808 iy1 += get_trackview_group_vertical_offset ();;
3810 if (drag_info.first_move) {
3812 // hide any dependent views
3814 rv->get_time_axis_view().hide_dependent_views (*rv);
3817 reparent to a non scrolling group so that we can keep the
3818 region selection above all time axis views.
3819 reparenting means we have to move the rv as the two
3820 parent groups have different coordinates.
3823 rv->get_canvas_group()->property_y() = iy1 - 1;
3824 rv->get_canvas_group()->reparent(*_region_motion_group);
3826 rv->fake_set_opaque (true);
3829 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3830 RouteTimeAxisView* canvas_rtv = dynamic_cast<RouteTimeAxisView*>(tvp2);
3831 RouteTimeAxisView* temp_rtv;
3833 if ((pointer_y_span != 0) && !clamp_y_axis) {
3836 for (j = height_list.begin(); j!= height_list.end(); j++) {
3837 if (x == canvas_rtv->order) {
3838 /* we found the track the region is on */
3839 if (x != original_pointer_order) {
3840 /*this isn't from the same track we're dragging from */
3841 temp_pointer_y_span = canvas_pointer_y_span;
3843 while (temp_pointer_y_span > 0) {
3844 /* we're moving up canvas-wise,
3845 so we need to find the next track height
3847 if (j != height_list.begin()) {
3850 if (x != original_pointer_order) {
3851 /* we're not from the dragged track, so ignore hidden tracks. */
3853 temp_pointer_y_span++;
3857 temp_pointer_y_span--;
3860 while (temp_pointer_y_span < 0) {
3862 if (x != original_pointer_order) {
3864 temp_pointer_y_span--;
3868 if (j != height_list.end()) {
3871 temp_pointer_y_span++;
3873 /* find out where we'll be when we move and set height accordingly */
3875 tvp2 = trackview_by_y_position (iy1 + y_delta);
3876 temp_rtv = dynamic_cast<RouteTimeAxisView*>(tvp2);
3877 rv->set_height (temp_rtv->current_height());
3879 /* if you un-comment the following, the region colours will follow the track colours whilst dragging,
3880 personally, i think this can confuse things, but never mind.
3883 //const GdkColor& col (temp_rtv->view->get_region_color());
3884 //rv->set_color (const_cast<GdkColor&>(col));
3891 /* prevent the regionview from being moved to before
3892 the zero position on the canvas.
3897 if (-x_delta > ix1) {
3900 } else if ((x_delta > 0) && (rv->region()->last_frame() > max_frames - x_delta)) {
3901 x_delta = max_frames - rv->region()->last_frame();
3904 if (drag_info.brushing) {
3905 mouse_brush_insert_region (rv, pending_region_position);
3907 rv->move (x_delta, y_delta);
3910 } /* foreach region */
3914 if (drag_info.first_move && drag_info.move_threshold_passed) {
3915 cursor_group->raise_to_top();
3916 drag_info.first_move = false;
3919 if (x_delta != 0 && !drag_info.brushing) {
3920 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3925 Editor::region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3927 bool nocommit = true;
3928 vector<RegionView*> copies;
3929 RouteTimeAxisView* source_tv;
3930 boost::shared_ptr<Diskstream> ds;
3931 boost::shared_ptr<Playlist> from_playlist;
3932 vector<RegionView*> new_selection;
3933 typedef set<boost::shared_ptr<Playlist> > PlaylistSet;
3934 PlaylistSet modified_playlists;
3935 PlaylistSet frozen_playlists;
3936 list <sigc::connection> modified_playlist_connections;
3937 pair<PlaylistSet::iterator,bool> insert_result, frozen_insert_result;
3939 /* first_move is set to false if the regionview has been moved in the
3943 if (drag_info.first_move) {
3950 if (Config->get_edit_mode() == Splice && !pre_drag_region_selection.empty()) {
3951 selection->set (pre_drag_region_selection);
3952 pre_drag_region_selection.clear ();
3955 if (drag_info.brushing) {
3956 /* all changes were made during motion event handlers */
3958 if (drag_info.copy) {
3959 for (list<RegionView*>::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
3960 copies.push_back (*i);
3969 /* reverse this here so that we have the correct logic to finalize
3973 if (Config->get_edit_mode() == Lock && !drag_info.copy) {
3974 drag_info.x_constrained = !drag_info.x_constrained;
3977 if (drag_info.copy) {
3978 if (drag_info.x_constrained) {
3979 op_string = _("fixed time region copy");
3981 op_string = _("region copy");
3984 if (drag_info.x_constrained) {
3985 op_string = _("fixed time region drag");
3987 op_string = _("region drag");
3991 begin_reversible_command (op_string);
3993 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ) {
3995 RegionView* rv = (*i);
3996 double ix1, ix2, iy1, iy2;
3997 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3998 rv->get_canvas_frame()->i2w (ix1, iy1);
3999 iy1 += vertical_adjustment.get_value() - canvas_timebars_vsize;
4001 TimeAxisView* dest_tv = trackview_by_y_position (iy1);
4002 RouteTimeAxisView* dest_rtv = dynamic_cast<RouteTimeAxisView*>(dest_tv);
4004 bool changed_tracks, changed_position;
4007 if (rv->region()->locked()) {
4012 /* adjust for track speed */
4016 if (dest_rtv && dest_rtv->get_diskstream()) {
4017 speed = dest_rtv->get_diskstream()->speed();
4020 changed_position = (drag_info.last_frame_position != (nframes64_t) (rv->region()->position()/speed));
4021 changed_tracks = (dest_tv != &rv->get_time_axis_view());
4023 if (changed_position && !drag_info.x_constrained) {
4024 _master_group->w2i(ix1, iy1);
4025 where = (nframes64_t) (unit_to_frame (ix1) * speed);
4027 where = rv->region()->position();
4030 boost::shared_ptr<Region> new_region;
4033 if (drag_info.copy) {
4034 /* we already made a copy */
4035 new_region = rv->region();
4037 /* undo the previous hide_dependent_views so that xfades don't
4038 disappear on copying regions
4041 //rv->get_time_axis_view().reveal_dependent_views (*rv);
4043 } else if (changed_tracks && dest_rtv->playlist()) {
4044 new_region = RegionFactory::create (rv->region());
4047 if (changed_tracks || drag_info.copy) {
4049 boost::shared_ptr<Playlist> to_playlist = dest_rtv->playlist();
4055 latest_regionviews.clear ();
4057 sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
4059 insert_result = modified_playlists.insert (to_playlist);
4060 if (insert_result.second) {
4061 session->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
4064 to_playlist->add_region (new_region, where);
4068 if (!latest_regionviews.empty()) {
4069 // XXX why just the first one ? we only expect one
4070 // commented out in nick_m's canvas reworking. is that intended?
4071 //dest_atv->reveal_dependent_views (*latest_regionviews.front());
4072 new_selection.push_back (latest_regionviews.front());
4077 motion on the same track. plonk the previously reparented region
4078 back to its original canvas group (its streamview).
4079 No need to do anything for copies as they are fake regions which will be deleted.
4082 rv->get_canvas_group()->reparent (*dest_rtv->view()->canvas_item());
4083 rv->get_canvas_group()->property_y() = 0;
4085 /* just change the model */
4087 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
4089 insert_result = modified_playlists.insert (playlist);
4090 if (insert_result.second) {
4091 session->add_command (new MementoCommand<Playlist>(*playlist, &playlist->get_state(), 0));
4093 /* freeze to avoid lots of relayering in the case of a multi-region drag */
4094 frozen_insert_result = frozen_playlists.insert(playlist);
4095 if (frozen_insert_result.second) {
4099 rv->region()->set_position (where, (void*) this);
4102 if (changed_tracks && !drag_info.copy) {
4104 /* get the playlist where this drag started. we can't use rv->region()->playlist()
4105 because we may have copied the region and it has not been attached to a playlist.
4108 assert ((source_tv = dynamic_cast<RouteTimeAxisView*> (&rv->get_time_axis_view())));
4109 assert ((ds = source_tv->get_diskstream()));
4110 assert ((from_playlist = ds->playlist()));
4112 /* moved to a different audio track, without copying */
4114 /* the region that used to be in the old playlist is not
4115 moved to the new one - we use a copy of it. as a result,
4116 any existing editor for the region should no longer be
4120 rv->hide_region_editor();
4121 rv->fake_set_opaque (false);
4123 /* remove the region from the old playlist */
4125 insert_result = modified_playlists.insert (from_playlist);
4126 if (insert_result.second) {
4127 session->add_command (new MementoCommand<Playlist>(*from_playlist, &from_playlist->get_state(), 0));
4130 from_playlist->remove_region ((rv->region()));
4132 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
4133 was selected in all of them, then removing it from a playlist will have removed all
4134 trace of it from the selection (i.e. there were N regions selected, we removed 1,
4135 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
4136 corresponding regionview, and the selection is now empty).
4138 this could have invalidated any and all iterators into the region selection.
4140 the heuristic we use here is: if the region selection is empty, break out of the loop
4141 here. if the region selection is not empty, then restart the loop because we know that
4142 we must have removed at least the region(view) we've just been working on as well as any
4143 that we processed on previous iterations.
4145 EXCEPT .... if we are doing a copy drag, then the selection hasn't been modified and
4146 we can just iterate.
4149 if (selection->regions.empty()) {
4152 i = selection->regions.by_layer().begin();
4159 if (drag_info.copy) {
4160 copies.push_back (rv);
4164 if (new_selection.empty()) {
4165 if (drag_info.copy) {
4166 /* the region(view)s that are selected and being dragged around
4167 are copies and do not belong to any track. remove them
4168 from the selection right here.
4170 selection->clear_regions();
4173 /* this will clear any existing selection that would have been
4174 cleared in the other clause above
4176 selection->set (new_selection);
4179 for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
4185 for (set<boost::shared_ptr<Playlist> >::iterator p = modified_playlists.begin(); p != modified_playlists.end(); ++p) {
4186 session->add_command (new MementoCommand<Playlist>(*(*p), 0, &(*p)->get_state()));
4188 commit_reversible_command ();
4191 for (vector<RegionView*>::iterator x = copies.begin(); x != copies.end(); ++x) {
4198 Editor::create_region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
4200 if (drag_info.move_threshold_passed) {
4201 if (drag_info.first_move) {
4202 // TODO: create region-create-drag region view here
4203 drag_info.first_move = false;
4206 // TODO: resize region-create-drag region view here
4211 Editor::create_region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
4213 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (drag_info.dest_trackview);
4217 const boost::shared_ptr<MidiDiskstream> diskstream =
4218 boost::dynamic_pointer_cast<MidiDiskstream>(mtv->view()->trackview().track()->diskstream());
4221 warning << "Cannot create non-MIDI region" << endl;
4225 if (drag_info.first_move) {
4226 begin_reversible_command (_("create region"));
4227 XMLNode &before = mtv->playlist()->get_state();
4229 nframes64_t start = drag_info.grab_frame;
4230 snap_to (start, -1);
4231 const Meter& m = session->tempo_map().meter_at(start);
4232 const Tempo& t = session->tempo_map().tempo_at(start);
4233 double length = floor (m.frames_per_bar(t, session->frame_rate()));
4235 boost::shared_ptr<Source> src = session->create_midi_source_for_session(*diskstream.get());
4237 mtv->playlist()->add_region (boost::dynamic_pointer_cast<MidiRegion>
4238 (RegionFactory::create(src, 0, (nframes_t) length,
4239 PBD::basename_nosuffix(src->name()))), start);
4240 XMLNode &after = mtv->playlist()->get_state();
4241 session->add_command(new MementoCommand<Playlist>(*mtv->playlist().get(), &before, &after));
4242 commit_reversible_command();
4245 create_region_drag_motion_callback (item, event);
4246 // TODO: create region-create-drag region here
4251 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
4253 /* Either add to or set the set the region selection, unless
4254 this is an alignment click (control used)
4257 if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
4258 TimeAxisView* tv = &rv.get_time_axis_view();
4259 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
4261 if (rtv && rtv->is_track()) {
4262 speed = rtv->get_diskstream()->speed();
4265 nframes64_t where = get_preferred_edit_position();
4269 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
4271 align_region (rv.region(), SyncPoint, (nframes64_t) (where * speed));
4273 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
4275 align_region (rv.region(), End, (nframes64_t) (where * speed));
4279 align_region (rv.region(), Start, (nframes64_t) (where * speed));
4286 Editor::show_verbose_time_cursor (nframes64_t frame, double offset, double xpos, double ypos)
4292 nframes64_t frame_rate;
4301 if (Profile->get_sae() || Profile->get_small_screen()) {
4302 m = ARDOUR_UI::instance()->primary_clock.mode();
4304 m = ARDOUR_UI::instance()->secondary_clock.mode();
4308 case AudioClock::BBT:
4309 session->bbt_time (frame, bbt);
4310 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, bbt.bars, bbt.beats, bbt.ticks);
4313 case AudioClock::SMPTE:
4314 session->smpte_time (frame, smpte);
4315 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
4318 case AudioClock::MinSec:
4319 /* XXX this is copied from show_verbose_duration_cursor() */
4320 frame_rate = session->frame_rate();
4321 hours = frame / (frame_rate * 3600);
4322 frame = frame % (frame_rate * 3600);
4323 mins = frame / (frame_rate * 60);
4324 frame = frame % (frame_rate * 60);
4325 secs = (float) frame / (float) frame_rate;
4326 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
4330 snprintf (buf, sizeof(buf), "%" PRIi64, frame);
4334 if (xpos >= 0 && ypos >=0) {
4335 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
4338 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);
4340 show_verbose_canvas_cursor ();
4344 Editor::show_verbose_duration_cursor (nframes64_t start, nframes64_t end, double offset, double xpos, double ypos)
4351 nframes64_t distance, frame_rate;
4353 Meter meter_at_start(session->tempo_map().meter_at(start));
4361 if (Profile->get_sae() || Profile->get_small_screen()) {
4362 m = ARDOUR_UI::instance()->primary_clock.mode ();
4364 m = ARDOUR_UI::instance()->secondary_clock.mode ();
4368 case AudioClock::BBT:
4369 session->bbt_time (start, sbbt);
4370 session->bbt_time (end, ebbt);
4373 /* XXX this computation won't work well if the
4374 user makes a selection that spans any meter changes.
4377 ebbt.bars -= sbbt.bars;
4378 if (ebbt.beats >= sbbt.beats) {
4379 ebbt.beats -= sbbt.beats;
4382 ebbt.beats = int(meter_at_start.beats_per_bar()) + ebbt.beats - sbbt.beats;
4384 if (ebbt.ticks >= sbbt.ticks) {
4385 ebbt.ticks -= sbbt.ticks;
4388 ebbt.ticks = int(Meter::ticks_per_beat) + ebbt.ticks - sbbt.ticks;
4391 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, ebbt.bars, ebbt.beats, ebbt.ticks);
4394 case AudioClock::SMPTE:
4395 session->smpte_duration (end - start, smpte);
4396 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
4399 case AudioClock::MinSec:
4400 /* XXX this stuff should be elsewhere.. */
4401 distance = end - start;
4402 frame_rate = session->frame_rate();
4403 hours = distance / (frame_rate * 3600);
4404 distance = distance % (frame_rate * 3600);
4405 mins = distance / (frame_rate * 60);
4406 distance = distance % (frame_rate * 60);
4407 secs = (float) distance / (float) frame_rate;
4408 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
4412 snprintf (buf, sizeof(buf), "%" PRIi64, end - start);
4416 if (xpos >= 0 && ypos >=0) {
4417 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
4420 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
4423 show_verbose_canvas_cursor ();
4427 Editor::collect_new_region_view (RegionView* rv)
4429 latest_regionviews.push_back (rv);
4433 Editor::collect_and_select_new_region_view (RegionView* rv)
4436 latest_regionviews.push_back (rv);
4440 Editor::start_selection_grab (ArdourCanvas::Item* item, GdkEvent* event)
4442 if (clicked_regionview == 0) {
4446 /* lets try to create new Region for the selection */
4448 vector<boost::shared_ptr<Region> > new_regions;
4449 create_region_from_selection (new_regions);
4451 if (new_regions.empty()) {
4455 /* XXX fix me one day to use all new regions */
4457 boost::shared_ptr<Region> region (new_regions.front());
4459 /* add it to the current stream/playlist.
4461 tricky: the streamview for the track will add a new regionview. we will
4462 catch the signal it sends when it creates the regionview to
4463 set the regionview we want to then drag.
4466 latest_regionviews.clear();
4467 sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
4469 /* A selection grab currently creates two undo/redo operations, one for
4470 creating the new region and another for moving it.
4473 begin_reversible_command (_("selection grab"));
4475 boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
4477 XMLNode *before = &(playlist->get_state());
4478 clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
4479 XMLNode *after = &(playlist->get_state());
4480 session->add_command(new MementoCommand<Playlist>(*playlist, before, after));
4482 commit_reversible_command ();
4486 if (latest_regionviews.empty()) {
4487 /* something went wrong */
4491 /* we need to deselect all other regionviews, and select this one
4492 i'm ignoring undo stuff, because the region creation will take care of it
4494 selection->set (latest_regionviews);
4496 drag_info.item = latest_regionviews.front()->get_canvas_group();
4497 drag_info.data = latest_regionviews.front();
4498 drag_info.motion_callback = &Editor::region_drag_motion_callback;
4499 drag_info.finished_callback = &Editor::region_drag_finished_callback;
4503 drag_info.source_trackview = clicked_routeview;
4504 drag_info.dest_trackview = drag_info.source_trackview;
4505 drag_info.last_frame_position = latest_regionviews.front()->region()->position();
4506 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
4508 show_verbose_time_cursor (drag_info.last_frame_position, 10);
4512 Editor::cancel_selection ()
4514 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4515 (*i)->hide_selection ();
4517 selection->clear ();
4518 clicked_selection = 0;
4522 Editor::start_selection_op (ArdourCanvas::Item* item, GdkEvent* event, SelectionOp op)
4524 nframes64_t start = 0;
4525 nframes64_t end = 0;
4531 drag_info.item = item;
4532 drag_info.motion_callback = &Editor::drag_selection;
4533 drag_info.finished_callback = &Editor::end_selection_op;
4538 case CreateSelection:
4539 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
4540 drag_info.copy = true;
4542 drag_info.copy = false;
4544 start_grab (event, selector_cursor);
4547 case SelectionStartTrim:
4548 if (clicked_axisview) {
4549 clicked_axisview->order_selection_trims (item, true);
4551 start_grab (event, trimmer_cursor);
4552 start = selection->time[clicked_selection].start;
4553 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
4556 case SelectionEndTrim:
4557 if (clicked_axisview) {
4558 clicked_axisview->order_selection_trims (item, false);
4560 start_grab (event, trimmer_cursor);
4561 end = selection->time[clicked_selection].end;
4562 drag_info.pointer_frame_offset = drag_info.grab_frame - end;
4566 start = selection->time[clicked_selection].start;
4568 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
4572 if (selection_op == SelectionMove) {
4573 show_verbose_time_cursor(start, 10);
4575 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4580 Editor::drag_selection (ArdourCanvas::Item* item, GdkEvent* event)
4582 nframes64_t start = 0;
4583 nframes64_t end = 0;
4585 nframes64_t pending_position;
4587 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
4588 pending_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
4590 pending_position = 0;
4593 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4594 snap_to (pending_position);
4597 /* only alter selection if the current frame is
4598 different from the last frame position (adjusted)
4601 if (pending_position == drag_info.last_pointer_frame) return;
4603 switch (selection_op) {
4604 case CreateSelection:
4606 if (drag_info.first_move) {
4607 snap_to (drag_info.grab_frame);
4610 if (pending_position < drag_info.grab_frame) {
4611 start = pending_position;
4612 end = drag_info.grab_frame;
4614 end = pending_position;
4615 start = drag_info.grab_frame;
4618 /* first drag: Either add to the selection
4619 or create a new selection->
4622 if (drag_info.first_move) {
4624 begin_reversible_command (_("range selection"));
4626 if (drag_info.copy) {
4627 /* adding to the selection */
4628 clicked_selection = selection->add (start, end);
4629 drag_info.copy = false;
4631 /* new selection-> */
4632 clicked_selection = selection->set (clicked_axisview, start, end);
4637 case SelectionStartTrim:
4639 if (drag_info.first_move) {
4640 begin_reversible_command (_("trim selection start"));
4643 start = selection->time[clicked_selection].start;
4644 end = selection->time[clicked_selection].end;
4646 if (pending_position > end) {
4649 start = pending_position;
4653 case SelectionEndTrim:
4655 if (drag_info.first_move) {
4656 begin_reversible_command (_("trim selection end"));
4659 start = selection->time[clicked_selection].start;
4660 end = selection->time[clicked_selection].end;
4662 if (pending_position < start) {
4665 end = pending_position;
4672 if (drag_info.first_move) {
4673 begin_reversible_command (_("move selection"));
4676 start = selection->time[clicked_selection].start;
4677 end = selection->time[clicked_selection].end;
4679 length = end - start;
4681 start = pending_position;
4684 end = start + length;
4689 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
4690 start_canvas_autoscroll (1, 0);
4694 selection->replace (clicked_selection, start, end);
4697 drag_info.last_pointer_frame = pending_position;
4698 drag_info.first_move = false;
4700 if (selection_op == SelectionMove) {
4701 show_verbose_time_cursor(start, 10);
4703 show_verbose_time_cursor(pending_position, 10);
4708 Editor::end_selection_op (ArdourCanvas::Item* item, GdkEvent* event)
4710 if (!drag_info.first_move) {
4711 drag_selection (item, event);
4712 /* XXX this is not object-oriented programming at all. ick */
4713 if (selection->time.consolidate()) {
4714 selection->TimeChanged ();
4716 commit_reversible_command ();
4718 /* just a click, no pointer movement.*/
4720 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
4722 selection->clear_time();
4727 /* XXX what happens if its a music selection? */
4728 session->set_audio_range (selection->time);
4729 stop_canvas_autoscroll ();
4733 Editor::start_trim (ArdourCanvas::Item* item, GdkEvent* event)
4736 TimeAxisView* tvp = clicked_axisview;
4737 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4739 if (tv && tv->is_track()) {
4740 speed = tv->get_diskstream()->speed();
4743 nframes64_t region_start = (nframes64_t) (clicked_regionview->region()->position() / speed);
4744 nframes64_t region_end = (nframes64_t) (clicked_regionview->region()->last_frame() / speed);
4745 nframes64_t region_length = (nframes64_t) (clicked_regionview->region()->length() / speed);
4747 //drag_info.item = clicked_regionview->get_name_highlight();
4748 drag_info.item = item;
4749 drag_info.motion_callback = &Editor::trim_motion_callback;
4750 drag_info.finished_callback = &Editor::trim_finished_callback;
4752 start_grab (event, trimmer_cursor);
4754 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
4755 trim_op = ContentsTrim;
4757 /* These will get overridden for a point trim.*/
4758 if (drag_info.current_pointer_frame < (region_start + region_length/2)) {
4759 /* closer to start */
4760 trim_op = StartTrim;
4761 } else if (drag_info.current_pointer_frame > (region_end - region_length/2)) {
4769 show_verbose_time_cursor(region_start, 10);
4772 show_verbose_time_cursor(region_end, 10);
4775 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4781 Editor::trim_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
4783 RegionView* rv = clicked_regionview;
4784 nframes64_t frame_delta = 0;
4785 bool left_direction;
4786 bool obey_snap = !Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier());
4788 /* snap modifier works differently here..
4789 its' current state has to be passed to the
4790 various trim functions in order to work properly
4794 TimeAxisView* tvp = clicked_axisview;
4795 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4796 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
4798 if (tv && tv->is_track()) {
4799 speed = tv->get_diskstream()->speed();
4802 if (drag_info.last_pointer_frame > drag_info.current_pointer_frame) {
4803 left_direction = true;
4805 left_direction = false;
4809 snap_to (drag_info.current_pointer_frame);
4812 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
4816 if (drag_info.first_move) {
4822 trim_type = "Region start trim";
4825 trim_type = "Region end trim";
4828 trim_type = "Region content trim";
4832 begin_reversible_command (trim_type);
4834 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4835 (*i)->fake_set_opaque(false);
4836 (*i)->region()->freeze ();
4838 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
4840 arv->temporarily_hide_envelope ();
4842 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4843 insert_result = motion_frozen_playlists.insert (pl);
4844 if (insert_result.second) {
4845 session->add_command(new MementoCommand<Playlist>(*pl, &pl->get_state(), 0));
4851 if (left_direction) {
4852 frame_delta = (drag_info.last_pointer_frame - drag_info.current_pointer_frame);
4854 frame_delta = (drag_info.current_pointer_frame - drag_info.last_pointer_frame);
4859 if ((left_direction == false) && (drag_info.current_pointer_frame <= rv->region()->first_frame()/speed)) {
4862 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4863 single_start_trim (**i, frame_delta, left_direction, obey_snap);
4869 if ((left_direction == true) && (drag_info.current_pointer_frame > (nframes64_t) (rv->region()->last_frame()/speed))) {
4872 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4873 single_end_trim (**i, frame_delta, left_direction, obey_snap);
4880 bool swap_direction = false;
4882 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
4883 swap_direction = true;
4886 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4887 i != selection->regions.by_layer().end(); ++i)
4889 single_contents_trim (**i, frame_delta, left_direction, swap_direction, obey_snap);
4897 show_verbose_time_cursor((nframes64_t) (rv->region()->position()/speed), 10);
4900 show_verbose_time_cursor((nframes64_t) (rv->region()->last_frame()/speed), 10);
4903 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4907 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4908 drag_info.first_move = false;
4912 Editor::single_contents_trim (RegionView& rv, nframes64_t frame_delta, bool left_direction, bool swap_direction, bool obey_snap)
4914 boost::shared_ptr<Region> region (rv.region());
4916 if (region->locked()) {
4920 nframes64_t new_bound;
4923 TimeAxisView* tvp = clicked_axisview;
4924 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4926 if (tv && tv->is_track()) {
4927 speed = tv->get_diskstream()->speed();
4930 if (left_direction) {
4931 if (swap_direction) {
4932 new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
4934 new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
4937 if (swap_direction) {
4938 new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
4940 new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
4945 snap_to (new_bound);
4947 region->trim_start ((nframes64_t) (new_bound * speed), this);
4948 rv.region_changed (StartChanged);
4952 Editor::single_start_trim (RegionView& rv, nframes64_t frame_delta, bool left_direction, bool obey_snap)
4954 boost::shared_ptr<Region> region (rv.region());
4956 if (region->locked()) {
4960 nframes64_t new_bound;
4963 TimeAxisView* tvp = clicked_axisview;
4964 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4966 if (tv && tv->is_track()) {
4967 speed = tv->get_diskstream()->speed();
4970 if (left_direction) {
4971 new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
4973 new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
4977 snap_to (new_bound, (left_direction ? 0 : 1));
4980 region->trim_front ((nframes64_t) (new_bound * speed), this);
4982 rv.region_changed (Change (LengthChanged|PositionChanged|StartChanged));
4986 Editor::single_end_trim (RegionView& rv, nframes64_t frame_delta, bool left_direction, bool obey_snap)
4988 boost::shared_ptr<Region> region (rv.region());
4990 if (region->locked()) {
4994 nframes64_t new_bound;
4997 TimeAxisView* tvp = clicked_axisview;
4998 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
5000 if (tv && tv->is_track()) {
5001 speed = tv->get_diskstream()->speed();
5004 if (left_direction) {
5005 new_bound = (nframes64_t) ((region->last_frame() + 1)/speed) - frame_delta;
5007 new_bound = (nframes64_t) ((region->last_frame() + 1)/speed) + frame_delta;
5011 snap_to (new_bound);
5013 region->trim_end ((nframes64_t) (new_bound * speed), this);
5014 rv.region_changed (LengthChanged);
5018 Editor::trim_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
5020 if (!drag_info.first_move) {
5021 trim_motion_callback (item, event);
5023 if (!selection->selected (clicked_regionview)) {
5024 thaw_region_after_trim (*clicked_regionview);
5027 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
5028 i != selection->regions.by_layer().end(); ++i)
5030 thaw_region_after_trim (**i);
5031 (*i)->fake_set_opaque (true);
5035 for (set<boost::shared_ptr<Playlist> >::iterator p = motion_frozen_playlists.begin(); p != motion_frozen_playlists.end(); ++p) {
5037 session->add_command (new MementoCommand<Playlist>(*(*p).get(), 0, &(*p)->get_state()));
5040 motion_frozen_playlists.clear ();
5042 commit_reversible_command();
5044 /* no mouse movement */
5050 Editor::point_trim (GdkEvent* event)
5052 RegionView* rv = clicked_regionview;
5053 nframes64_t new_bound = drag_info.current_pointer_frame;
5055 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
5056 snap_to (new_bound);
5059 /* Choose action dependant on which button was pressed */
5060 switch (event->button.button) {
5062 trim_op = StartTrim;
5063 begin_reversible_command (_("Start point trim"));
5065 if (selection->selected (rv)) {
5067 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
5068 i != selection->regions.by_layer().end(); ++i)
5070 if (!(*i)->region()->locked()) {
5071 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
5072 XMLNode &before = pl->get_state();
5073 (*i)->region()->trim_front (new_bound, this);
5074 XMLNode &after = pl->get_state();
5075 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
5081 if (!rv->region()->locked()) {
5082 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
5083 XMLNode &before = pl->get_state();
5084 rv->region()->trim_front (new_bound, this);
5085 XMLNode &after = pl->get_state();
5086 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
5090 commit_reversible_command();
5095 begin_reversible_command (_("End point trim"));
5097 if (selection->selected (rv)) {
5099 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
5101 if (!(*i)->region()->locked()) {
5102 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
5103 XMLNode &before = pl->get_state();
5104 (*i)->region()->trim_end (new_bound, this);
5105 XMLNode &after = pl->get_state();
5106 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
5112 if (!rv->region()->locked()) {
5113 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
5114 XMLNode &before = pl->get_state();
5115 rv->region()->trim_end (new_bound, this);
5116 XMLNode &after = pl->get_state();
5117 session->add_command (new MementoCommand<Playlist>(*pl.get(), &before, &after));
5121 commit_reversible_command();
5130 Editor::thaw_region_after_trim (RegionView& rv)
5132 boost::shared_ptr<Region> region (rv.region());
5134 if (region->locked()) {
5138 region->thaw (_("trimmed region"));
5139 XMLNode &after = region->playlist()->get_state();
5140 session->add_command (new MementoCommand<Playlist>(*(region->playlist()), 0, &after));
5142 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(&rv);
5144 arv->unhide_envelope ();
5148 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* event)
5153 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
5154 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
5158 Location* location = find_location_from_marker (marker, is_start);
5159 location->set_hidden (true, this);
5164 Editor::start_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event, RangeMarkerOp op)
5170 drag_info.item = item;
5171 drag_info.motion_callback = &Editor::drag_range_markerbar_op;
5172 drag_info.finished_callback = &Editor::end_range_markerbar_op;
5174 range_marker_op = op;
5176 if (!temp_location) {
5177 temp_location = new Location;
5181 case CreateRangeMarker:
5182 case CreateTransportMarker:
5183 case CreateCDMarker:
5185 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
5186 drag_info.copy = true;
5188 drag_info.copy = false;
5190 start_grab (event, selector_cursor);
5194 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
5199 Editor::drag_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
5201 nframes64_t start = 0;
5202 nframes64_t end = 0;
5203 ArdourCanvas::SimpleRect *crect;
5205 switch (range_marker_op) {
5206 case CreateRangeMarker:
5207 crect = range_bar_drag_rect;
5209 case CreateTransportMarker:
5210 crect = transport_bar_drag_rect;
5212 case CreateCDMarker:
5213 crect = cd_marker_bar_drag_rect;
5216 cerr << "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()" << endl;
5221 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
5222 snap_to (drag_info.current_pointer_frame);
5225 /* only alter selection if the current frame is
5226 different from the last frame position.
5229 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
5231 switch (range_marker_op) {
5232 case CreateRangeMarker:
5233 case CreateTransportMarker:
5234 case CreateCDMarker:
5235 if (drag_info.first_move) {
5236 snap_to (drag_info.grab_frame);
5239 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
5240 start = drag_info.current_pointer_frame;
5241 end = drag_info.grab_frame;
5243 end = drag_info.current_pointer_frame;
5244 start = drag_info.grab_frame;
5247 /* first drag: Either add to the selection
5248 or create a new selection.
5251 if (drag_info.first_move) {
5253 temp_location->set (start, end);
5257 update_marker_drag_item (temp_location);
5258 range_marker_drag_rect->show();
5259 //range_marker_drag_rect->raise_to_top();
5265 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
5266 start_canvas_autoscroll (1, 0);
5270 temp_location->set (start, end);
5272 double x1 = frame_to_pixel (start);
5273 double x2 = frame_to_pixel (end);
5274 crect->property_x1() = x1;
5275 crect->property_x2() = x2;
5277 update_marker_drag_item (temp_location);
5280 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
5281 drag_info.first_move = false;
5283 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
5288 Editor::end_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
5290 Location * newloc = 0;
5294 if (!drag_info.first_move) {
5295 drag_range_markerbar_op (item, event);
5297 switch (range_marker_op) {
5298 case CreateRangeMarker:
5299 case CreateCDMarker:
5301 begin_reversible_command (_("new range marker"));
5302 XMLNode &before = session->locations()->get_state();
5303 session->locations()->next_available_name(rangename,"unnamed");
5304 if (range_marker_op == CreateCDMarker) {
5305 flags = Location::IsRangeMarker|Location::IsCDMarker;
5306 cd_marker_bar_drag_rect->hide();
5309 flags = Location::IsRangeMarker;
5310 range_bar_drag_rect->hide();
5312 newloc = new Location(temp_location->start(), temp_location->end(), rangename, (Location::Flags) flags);
5313 session->locations()->add (newloc, true);
5314 XMLNode &after = session->locations()->get_state();
5315 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
5316 commit_reversible_command ();
5318 range_marker_drag_rect->hide();
5322 case CreateTransportMarker:
5323 // popup menu to pick loop or punch
5324 new_transport_marker_context_menu (&event->button, item);
5329 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
5331 if (Keyboard::no_modifier_keys_pressed (&event->button) && range_marker_op != CreateCDMarker) {
5336 start = session->locations()->first_mark_before (drag_info.grab_frame);
5337 end = session->locations()->first_mark_after (drag_info.grab_frame);
5339 if (end == max_frames) {
5340 end = session->current_end_frame ();
5344 start = session->current_start_frame ();
5347 switch (mouse_mode) {
5349 /* find the two markers on either side and then make the selection from it */
5350 select_all_within (start, end, 0.0f, FLT_MAX, track_views, Selection::Set);
5354 /* find the two markers on either side of the click and make the range out of it */
5355 selection->set (0, start, end);
5364 stop_canvas_autoscroll ();
5370 Editor::start_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
5372 drag_info.item = item;
5373 drag_info.motion_callback = &Editor::drag_mouse_zoom;
5374 drag_info.finished_callback = &Editor::end_mouse_zoom;
5376 start_grab (event, zoom_cursor);
5378 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5382 Editor::drag_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
5387 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
5388 snap_to (drag_info.current_pointer_frame);
5390 if (drag_info.first_move) {
5391 snap_to (drag_info.grab_frame);
5395 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
5397 /* base start and end on initial click position */
5398 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
5399 start = drag_info.current_pointer_frame;
5400 end = drag_info.grab_frame;
5402 end = drag_info.current_pointer_frame;
5403 start = drag_info.grab_frame;
5408 if (drag_info.first_move) {
5410 zoom_rect->raise_to_top();
5413 reposition_zoom_rect(start, end);
5415 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
5416 drag_info.first_move = false;
5418 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5423 Editor::end_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
5425 if (!drag_info.first_move) {
5426 drag_mouse_zoom (item, event);
5428 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
5429 temporal_zoom_by_frame (drag_info.grab_frame, drag_info.last_pointer_frame, "mouse zoom");
5431 temporal_zoom_by_frame (drag_info.last_pointer_frame, drag_info.grab_frame, "mouse zoom");
5434 temporal_zoom_to_frame (false, drag_info.grab_frame);
5436 temporal_zoom_step (false);
5437 center_screen (drag_info.grab_frame);
5445 Editor::reposition_zoom_rect (nframes64_t start, nframes64_t end)
5447 double x1 = frame_to_pixel (start);
5448 double x2 = frame_to_pixel (end);
5449 double y2 = full_canvas_height - 1.0;
5451 zoom_rect->property_x1() = x1;
5452 zoom_rect->property_y1() = 1.0;
5453 zoom_rect->property_x2() = x2;
5454 zoom_rect->property_y2() = y2;
5458 Editor::start_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
5460 drag_info.item = item;
5461 drag_info.motion_callback = &Editor::drag_rubberband_select;
5462 drag_info.finished_callback = &Editor::end_rubberband_select;
5464 start_grab (event, cross_hair_cursor);
5466 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5470 Editor::drag_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
5477 /* use a bigger drag threshold than the default */
5479 if (abs ((int) (drag_info.current_pointer_frame - drag_info.grab_frame)) < 8) {
5483 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier()) && Config->get_rubberbanding_snaps_to_grid()) {
5484 if (drag_info.first_move) {
5485 snap_to (drag_info.grab_frame);
5487 snap_to (drag_info.current_pointer_frame);
5490 /* base start and end on initial click position */
5492 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
5493 start = drag_info.current_pointer_frame;
5494 end = drag_info.grab_frame;
5496 end = drag_info.current_pointer_frame;
5497 start = drag_info.grab_frame;
5500 if (drag_info.current_pointer_y < drag_info.grab_y) {
5501 y1 = drag_info.current_pointer_y;
5502 y2 = drag_info.grab_y;
5504 y2 = drag_info.current_pointer_y;
5505 y1 = drag_info.grab_y;
5509 if (start != end || y1 != y2) {
5511 double x1 = frame_to_pixel (start);
5512 double x2 = frame_to_pixel (end);
5514 rubberband_rect->property_x1() = x1;
5515 rubberband_rect->property_y1() = y1;
5516 rubberband_rect->property_x2() = x2;
5517 rubberband_rect->property_y2() = y2;
5519 rubberband_rect->show();
5520 rubberband_rect->raise_to_top();
5522 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
5523 drag_info.first_move = false;
5525 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5530 Editor::end_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
5532 if (!drag_info.first_move) {
5534 drag_rubberband_select (item, event);
5537 if (drag_info.current_pointer_y < drag_info.grab_y) {
5538 y1 = drag_info.current_pointer_y;
5539 y2 = drag_info.grab_y;
5541 y2 = drag_info.current_pointer_y;
5542 y1 = drag_info.grab_y;
5546 Selection::Operation op = Keyboard::selection_type (event->button.state);
5549 begin_reversible_command (_("rubberband selection"));
5551 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
5552 commit = select_all_within (drag_info.grab_frame, drag_info.last_pointer_frame, y1, y2, track_views, op);
5554 commit = select_all_within (drag_info.last_pointer_frame, drag_info.grab_frame, y1, y2, track_views, op);
5558 commit_reversible_command ();
5562 selection->clear_tracks();
5563 selection->clear_regions();
5564 selection->clear_points ();
5565 selection->clear_lines ();
5568 rubberband_rect->hide();
5573 Editor::mouse_rename_region (ArdourCanvas::Item* item, GdkEvent* event)
5575 using namespace Gtkmm2ext;
5577 ArdourPrompter prompter (false);
5579 prompter.set_prompt (_("Name for region:"));
5580 prompter.set_initial_text (clicked_regionview->region()->name());
5581 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
5582 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
5583 prompter.show_all ();
5584 switch (prompter.run ()) {
5585 case Gtk::RESPONSE_ACCEPT:
5587 prompter.get_result(str);
5589 clicked_regionview->region()->set_name (str);
5597 Editor::start_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
5599 drag_info.item = item;
5600 drag_info.motion_callback = &Editor::time_fx_motion;
5601 drag_info.finished_callback = &Editor::end_time_fx;
5605 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5609 Editor::time_fx_motion (ArdourCanvas::Item *item, GdkEvent* event)
5611 RegionView* rv = clicked_regionview;
5613 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
5614 snap_to (drag_info.current_pointer_frame);
5617 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
5621 if (drag_info.current_pointer_frame > rv->region()->position()) {
5622 rv->get_time_axis_view().show_timestretch (rv->region()->position(), drag_info.current_pointer_frame);
5625 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
5626 drag_info.first_move = false;
5628 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5632 Editor::end_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
5634 clicked_regionview->get_time_axis_view().hide_timestretch ();
5636 if (drag_info.first_move) {
5640 if (drag_info.last_pointer_frame < clicked_regionview->region()->position()) {
5641 /* backwards drag of the left edge - not usable */
5645 nframes64_t newlen = drag_info.last_pointer_frame - clicked_regionview->region()->position();
5647 float percentage = (double) newlen / (double) clicked_regionview->region()->length();
5649 #ifndef USE_RUBBERBAND
5650 // Soundtouch uses percentage / 100 instead of normal (/ 1)
5651 if (clicked_regionview->region()->data_type() == DataType::AUDIO) {
5652 percentage = (float) ((double) newlen - (double) clicked_regionview->region()->length()) / ((double) newlen) * 100.0f;
5656 begin_reversible_command (_("timestretch"));
5658 // XXX how do timeFX on multiple regions ?
5661 rs.add (clicked_regionview);
5663 if (time_stretch (rs, percentage) == 0) {
5664 session->commit_reversible_command ();
5669 Editor::mouse_brush_insert_region (RegionView* rv, nframes64_t pos)
5671 /* no brushing without a useful snap setting */
5673 switch (snap_mode) {
5675 return; /* can't work because it allows region to be placed anywhere */
5680 switch (snap_type) {
5688 /* don't brush a copy over the original */
5690 if (pos == rv->region()->position()) {
5694 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
5696 if (rtv == 0 || !rtv->is_track()) {
5700 boost::shared_ptr<Playlist> playlist = rtv->playlist();
5701 double speed = rtv->get_diskstream()->speed();
5703 XMLNode &before = playlist->get_state();
5704 playlist->add_region (RegionFactory::create (rv->region()), (nframes64_t) (pos * speed));
5705 XMLNode &after = playlist->get_state();
5706 session->add_command(new MementoCommand<Playlist>(*playlist.get(), &before, &after));
5708 // playlist is frozen, so we have to update manually
5710 playlist->Modified(); /* EMIT SIGNAL */
5714 Editor::track_height_step_timeout ()
5716 if (get_microseconds() - last_track_height_step_timestamp < 250000) {
5717 current_stepping_trackview = 0;