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.
30 #include <pbd/error.h>
31 #include <gtkmm2ext/utils.h>
32 #include <pbd/memento_command.h>
34 #include "ardour_ui.h"
36 #include "time_axis_view.h"
37 #include "audio_time_axis.h"
38 #include "audio_region_view.h"
40 #include "streamview.h"
41 #include "region_gain_line.h"
42 #include "automation_time_axis.h"
45 #include "selection.h"
48 #include "rgb_macros.h"
50 #include <ardour/types.h>
51 #include <ardour/profile.h>
52 #include <ardour/route.h>
53 #include <ardour/audio_track.h>
54 #include <ardour/audio_diskstream.h>
55 #include <ardour/playlist.h>
56 #include <ardour/audioplaylist.h>
57 #include <ardour/audioregion.h>
58 #include <ardour/dB.h>
59 #include <ardour/utils.h>
60 #include <ardour/region_factory.h>
67 using namespace ARDOUR;
71 using namespace Editing;
74 Editor::mouse_frame (nframes64_t& where, bool& in_track_canvas) const
78 Gdk::ModifierType mask;
79 Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->track_canvas->get_window();
80 Glib::RefPtr<const Gdk::Window> pointer_window;
86 pointer_window = canvas_window->get_pointer (x, y, mask);
88 if (pointer_window == track_canvas->get_bin_window()) {
91 in_track_canvas = true;
94 in_track_canvas = false;
99 event.type = GDK_BUTTON_RELEASE;
103 where = event_frame (&event, 0, 0);
108 Editor::event_frame (GdkEvent* event, double* pcx, double* pcy) const
122 switch (event->type) {
123 case GDK_BUTTON_RELEASE:
124 case GDK_BUTTON_PRESS:
125 case GDK_2BUTTON_PRESS:
126 case GDK_3BUTTON_PRESS:
128 *pcx = event->button.x;
129 *pcy = event->button.y;
130 _trackview_group->w2i(*pcx, *pcy);
132 case GDK_MOTION_NOTIFY:
134 *pcx = event->motion.x;
135 *pcy = event->motion.y;
136 _trackview_group->w2i(*pcx, *pcy);
138 case GDK_ENTER_NOTIFY:
139 case GDK_LEAVE_NOTIFY:
140 track_canvas->w2c(event->crossing.x, event->crossing.y, *pcx, *pcy);
143 case GDK_KEY_RELEASE:
144 // track_canvas->w2c(event->key.x, event->key.y, *pcx, *pcy);
147 warning << string_compose (_("Editor::event_frame() used on unhandled event type %1"), event->type) << endmsg;
151 /* note that pixel_to_frame() never returns less than zero, so even if the pixel
152 position is negative (as can be the case with motion events in particular),
153 the frame location is always positive.
156 return pixel_to_frame (*pcx);
160 Editor::mouse_mode_toggled (MouseMode m)
162 if (ignore_mouse_mode_toggle) {
168 if (mouse_select_button.get_active()) {
174 if (mouse_move_button.get_active()) {
180 if (mouse_gain_button.get_active()) {
186 if (mouse_zoom_button.get_active()) {
192 if (mouse_timefx_button.get_active()) {
198 if (mouse_audition_button.get_active()) {
209 Editor::which_grabber_cursor ()
211 switch (_edit_point) {
213 return grabber_edit_point_cursor;
218 return grabber_cursor;
222 Editor::set_canvas_cursor ()
224 switch (mouse_mode) {
226 current_canvas_cursor = selector_cursor;
230 current_canvas_cursor = which_grabber_cursor();
234 current_canvas_cursor = cross_hair_cursor;
238 current_canvas_cursor = zoom_cursor;
242 current_canvas_cursor = time_fx_cursor; // just use playhead
246 current_canvas_cursor = speaker_cursor;
251 track_canvas->get_window()->set_cursor(*current_canvas_cursor);
256 Editor::set_mouse_mode (MouseMode m, bool force)
258 if (drag_info.item) {
262 if (!force && m == mouse_mode) {
270 if (mouse_mode != MouseRange) {
272 /* in all modes except range, hide the range selection,
273 show the object (region) selection.
276 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
277 (*i)->set_should_show_selection (true);
279 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
280 (*i)->hide_selection ();
286 in range mode,show the range selection.
289 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
290 if ((*i)->get_selected()) {
291 (*i)->show_selection (selection->time);
296 /* XXX the hack of unsetting all other buttons should go
297 away once GTK2 allows us to use regular radio buttons drawn like
298 normal buttons, rather than my silly GroupedButton hack.
301 ignore_mouse_mode_toggle = true;
303 switch (mouse_mode) {
305 mouse_select_button.set_active (true);
309 mouse_move_button.set_active (true);
313 mouse_gain_button.set_active (true);
317 mouse_zoom_button.set_active (true);
321 mouse_timefx_button.set_active (true);
325 mouse_audition_button.set_active (true);
329 ignore_mouse_mode_toggle = false;
331 set_canvas_cursor ();
335 Editor::step_mouse_mode (bool next)
337 switch (current_mouse_mode()) {
339 if (next) set_mouse_mode (MouseRange);
340 else set_mouse_mode (MouseTimeFX);
344 if (next) set_mouse_mode (MouseZoom);
345 else set_mouse_mode (MouseObject);
349 if (next) set_mouse_mode (MouseGain);
350 else set_mouse_mode (MouseRange);
354 if (next) set_mouse_mode (MouseTimeFX);
355 else set_mouse_mode (MouseZoom);
359 if (next) set_mouse_mode (MouseAudition);
360 else set_mouse_mode (MouseGain);
364 if (next) set_mouse_mode (MouseObject);
365 else set_mouse_mode (MouseTimeFX);
371 Editor::button_selection (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
373 /* in object/audition/timefx mode, any button press sets
374 the selection if the object can be selected. this is a
375 bit of hack, because we want to avoid this if the
376 mouse operation is a region alignment.
378 note: not dbl-click or triple-click
381 if (((mouse_mode != MouseObject) &&
382 (mouse_mode != MouseAudition || item_type != RegionItem) &&
383 (mouse_mode != MouseTimeFX || item_type != RegionItem) &&
384 (mouse_mode != MouseRange)) ||
386 ((event->type != GDK_BUTTON_PRESS && event->type != GDK_BUTTON_RELEASE) || event->button.button > 3)) {
391 if (event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) {
393 if ((event->button.state & Keyboard::RelevantModifierKeyMask) && event->button.button != 1) {
395 /* almost no selection action on modified button-2 or button-3 events */
397 if (item_type != RegionItem && event->button.button != 2) {
403 Selection::Operation op = Keyboard::selection_type (event->button.state);
404 bool press = (event->type == GDK_BUTTON_PRESS);
406 // begin_reversible_command (_("select on click"));
410 if (mouse_mode != MouseRange) {
411 set_selected_regionview_from_click (press, op, true);
412 } else if (event->type == GDK_BUTTON_PRESS) {
413 set_selected_track_as_side_effect ();
417 case RegionViewNameHighlight:
419 if (mouse_mode != MouseRange) {
420 set_selected_regionview_from_click (press, op, true);
421 } else if (event->type == GDK_BUTTON_PRESS) {
422 set_selected_track_as_side_effect ();
426 case FadeInHandleItem:
428 case FadeOutHandleItem:
430 if (mouse_mode != MouseRange) {
431 set_selected_regionview_from_click (press, op, true);
432 } else if (event->type == GDK_BUTTON_PRESS) {
433 set_selected_track_as_side_effect ();
437 case GainAutomationControlPointItem:
438 case PanAutomationControlPointItem:
439 case RedirectAutomationControlPointItem:
440 set_selected_track_as_side_effect ();
441 if (mouse_mode != MouseRange) {
442 set_selected_control_point_from_click (op, false);
447 /* for context click or range selection, select track */
448 if (event->button.button == 3) {
449 set_selected_track_as_side_effect ();
450 } else if (event->type == GDK_BUTTON_PRESS && mouse_mode == MouseRange) {
451 set_selected_track_as_side_effect ();
455 case AutomationTrackItem:
456 set_selected_track_as_side_effect (true);
464 const static double ZERO_GAIN_FRACTION = gain_to_slider_position(dB_to_coefficient(0.0));
467 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
469 Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->track_canvas->get_window();
472 Glib::RefPtr<const Gdk::Window> pointer_window;
475 Gdk::ModifierType mask;
477 pointer_window = canvas_window->get_pointer (x, y, mask);
479 if (pointer_window == track_canvas->get_bin_window()) {
480 track_canvas->window_to_world (x, y, wx, wy);
481 allow_vertical_scroll = true;
483 allow_vertical_scroll = false;
487 track_canvas->grab_focus();
489 if (session && session->actively_recording()) {
493 button_selection (item, event, item_type);
495 if (drag_info.item == 0 &&
496 (Keyboard::is_delete_event (&event->button) ||
497 Keyboard::is_context_menu_event (&event->button) ||
498 Keyboard::is_edit_event (&event->button))) {
500 /* handled by button release */
504 switch (event->button.button) {
507 if (event->type == GDK_BUTTON_PRESS) {
509 if (drag_info.item) {
510 drag_info.item->ungrab (event->button.time);
513 /* single mouse clicks on any of these item types operate
514 independent of mouse mode, mostly because they are
515 not on the main track canvas or because we want
520 case PlayheadCursorItem:
521 start_cursor_grab (item, event);
525 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
526 hide_marker (item, event);
528 start_marker_grab (item, event);
532 case TempoMarkerItem:
533 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
534 start_tempo_marker_copy_grab (item, event);
536 start_tempo_marker_grab (item, event);
540 case MeterMarkerItem:
541 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
542 start_meter_marker_copy_grab (item, event);
544 start_meter_marker_grab (item, event);
551 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
552 start_cursor_grab_no_stop(&playhead_cursor->canvas_item, event);
558 case RangeMarkerBarItem:
559 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
560 start_cursor_grab_no_stop(&playhead_cursor->canvas_item, event);
562 start_range_markerbar_op (item, event, CreateRangeMarker);
567 case CdMarkerBarItem:
568 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
569 start_cursor_grab_no_stop(&playhead_cursor->canvas_item, event);
571 start_range_markerbar_op (item, event, CreateCDMarker);
576 case TransportMarkerBarItem:
577 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
578 start_cursor_grab_no_stop(&playhead_cursor->canvas_item, event);
580 start_range_markerbar_op (item, event, CreateTransportMarker);
590 switch (mouse_mode) {
593 case StartSelectionTrimItem:
594 start_selection_op (item, event, SelectionStartTrim);
597 case EndSelectionTrimItem:
598 start_selection_op (item, event, SelectionEndTrim);
602 if (Keyboard::modifier_state_contains
603 (event->button.state, Keyboard::ModifierMask(Keyboard::SecondaryModifier))) {
604 // contains and not equals because I can't use alt as a modifier alone.
605 start_selection_grab (item, event);
606 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
607 /* grab selection for moving */
608 start_selection_op (item, event, SelectionMove);
610 /* this was debated, but decided the more common action was to
611 make a new selection */
612 start_selection_op (item, event, CreateSelection);
617 start_selection_op (item, event, CreateSelection);
623 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)) &&
624 event->type == GDK_BUTTON_PRESS) {
626 start_rubberband_select (item, event);
628 } else if (event->type == GDK_BUTTON_PRESS) {
631 case FadeInHandleItem:
632 start_fade_in_grab (item, event);
635 case FadeOutHandleItem:
636 start_fade_out_grab (item, event);
640 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
641 start_region_copy_grab (item, event);
642 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
643 start_region_brush_grab (item, event);
645 start_region_grab (item, event);
649 case RegionViewNameHighlight:
650 start_trim (item, event);
655 /* rename happens on edit clicks */
656 start_trim (clicked_regionview->get_name_highlight(), event);
660 case GainAutomationControlPointItem:
661 case PanAutomationControlPointItem:
662 case RedirectAutomationControlPointItem:
663 start_control_point_grab (item, event);
667 case GainAutomationLineItem:
668 case PanAutomationLineItem:
669 case RedirectAutomationLineItem:
670 start_line_grab_from_line (item, event);
675 case AutomationTrackItem:
676 start_rubberband_select (item, event);
679 /* <CMT Additions> */
680 case ImageFrameHandleStartItem:
681 imageframe_start_handle_op(item, event) ;
684 case ImageFrameHandleEndItem:
685 imageframe_end_handle_op(item, event) ;
688 case MarkerViewHandleStartItem:
689 markerview_item_start_handle_op(item, event) ;
692 case MarkerViewHandleEndItem:
693 markerview_item_end_handle_op(item, event) ;
696 /* </CMT Additions> */
698 /* <CMT Additions> */
700 start_markerview_grab(item, event) ;
703 start_imageframe_grab(item, event) ;
705 /* </CMT Additions> */
721 /* start a grab so that if we finish after moving
722 we can tell what happened.
724 drag_info.item = item;
725 drag_info.motion_callback = &Editor::region_gain_motion_callback;
726 drag_info.finished_callback = 0;
727 start_grab (event, current_canvas_cursor);
730 case GainControlPointItem:
731 start_control_point_grab (item, event);
735 start_line_grab_from_line (item, event);
738 case GainAutomationControlPointItem:
739 case PanAutomationControlPointItem:
740 case RedirectAutomationControlPointItem:
741 start_control_point_grab (item, event);
752 case GainAutomationControlPointItem:
753 case PanAutomationControlPointItem:
754 case RedirectAutomationControlPointItem:
755 start_control_point_grab (item, event);
758 case GainAutomationLineItem:
759 case PanAutomationLineItem:
760 case RedirectAutomationLineItem:
761 start_line_grab_from_line (item, event);
765 // XXX need automation mode to identify which
767 // start_line_grab_from_regionview (item, event);
777 if (event->type == GDK_BUTTON_PRESS) {
778 start_mouse_zoom (item, event);
785 if (item_type == RegionItem) {
786 start_time_fx (item, event);
793 scrub_reverse_distance = 0;
794 last_scrub_x = event->button.x;
795 scrubbing_direction = 0;
796 track_canvas->get_window()->set_cursor (*transparent_cursor);
797 /* rest handled in motion & release */
806 switch (mouse_mode) {
808 if (event->type == GDK_BUTTON_PRESS) {
811 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
812 start_region_copy_grab (item, event);
814 start_region_grab (item, event);
819 case GainAutomationControlPointItem:
820 case PanAutomationControlPointItem:
821 case RedirectAutomationControlPointItem:
822 start_control_point_grab (item, event);
833 case RegionViewNameHighlight:
834 start_trim (item, event);
839 start_trim (clicked_regionview->get_name_highlight(), event);
850 if (event->type == GDK_BUTTON_PRESS) {
851 /* relax till release */
858 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
859 temporal_zoom_session();
861 temporal_zoom_to_frame (true, event_frame(event));
884 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
886 nframes64_t where = event_frame (event, 0, 0);
887 AutomationTimeAxisView* atv = 0;
889 /* no action if we're recording */
891 if (session && session->actively_recording()) {
895 /* first, see if we're finishing a drag ... */
897 if (drag_info.item) {
898 if (end_grab (item, event)) {
899 /* grab dragged, so do nothing else */
904 button_selection (item, event, item_type);
906 /* edit events get handled here */
908 if (drag_info.item == 0 && Keyboard::is_edit_event (&event->button)) {
914 case TempoMarkerItem:
915 edit_tempo_marker (item);
918 case MeterMarkerItem:
919 edit_meter_marker (item);
923 if (clicked_regionview->name_active()) {
924 return mouse_rename_region (item, event);
934 /* context menu events get handled here */
936 if (Keyboard::is_context_menu_event (&event->button)) {
938 if (drag_info.item == 0) {
940 /* no matter which button pops up the context menu, tell the menu
941 widget to use button 1 to drive menu selection.
946 case FadeInHandleItem:
948 case FadeOutHandleItem:
949 popup_fade_context_menu (1, event->button.time, item, item_type);
953 popup_track_context_menu (1, event->button.time, item_type, false, where);
957 case RegionViewNameHighlight:
959 popup_track_context_menu (1, event->button.time, item_type, false, where);
963 popup_track_context_menu (1, event->button.time, item_type, true, where);
966 case AutomationTrackItem:
967 popup_track_context_menu (1, event->button.time, item_type, false, where);
971 case RangeMarkerBarItem:
972 case TransportMarkerBarItem:
973 case CdMarkerBarItem:
976 popup_ruler_menu (pixel_to_frame(event->button.x), item_type);
980 marker_context_menu (&event->button, item);
983 case TempoMarkerItem:
984 tm_marker_context_menu (&event->button, item);
987 case MeterMarkerItem:
988 tm_marker_context_menu (&event->button, item);
991 case CrossfadeViewItem:
992 popup_track_context_menu (1, event->button.time, item_type, false, where);
995 /* <CMT Additions> */
997 popup_imageframe_edit_menu(1, event->button.time, item, true) ;
999 case ImageFrameTimeAxisItem:
1000 popup_imageframe_edit_menu(1, event->button.time, item, false) ;
1002 case MarkerViewItem:
1003 popup_marker_time_axis_edit_menu(1, event->button.time, item, true) ;
1005 case MarkerTimeAxisItem:
1006 popup_marker_time_axis_edit_menu(1, event->button.time, item, false) ;
1008 /* <CMT Additions> */
1019 /* delete events get handled here */
1021 if (drag_info.item == 0 && Keyboard::is_delete_event (&event->button)) {
1023 switch (item_type) {
1024 case TempoMarkerItem:
1025 remove_tempo_marker (item);
1028 case MeterMarkerItem:
1029 remove_meter_marker (item);
1033 remove_marker (*item, event);
1037 if (mouse_mode == MouseObject) {
1038 remove_clicked_region ();
1042 case GainControlPointItem:
1043 if (mouse_mode == MouseGain) {
1044 remove_gain_control_point (item, event);
1048 case GainAutomationControlPointItem:
1049 case PanAutomationControlPointItem:
1050 case RedirectAutomationControlPointItem:
1051 remove_control_point (item, event);
1060 switch (event->button.button) {
1063 switch (item_type) {
1064 /* see comments in button_press_handler */
1065 case PlayheadCursorItem:
1068 case GainAutomationLineItem:
1069 case PanAutomationLineItem:
1070 case RedirectAutomationLineItem:
1071 case StartSelectionTrimItem:
1072 case EndSelectionTrimItem:
1076 if (!_dragging_playhead) {
1077 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1078 snap_to (where, 0, true);
1080 mouse_add_new_marker (where);
1084 case CdMarkerBarItem:
1085 if (!_dragging_playhead) {
1086 // if we get here then a dragged range wasn't done
1087 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1088 snap_to (where, 0, true);
1090 mouse_add_new_marker (where, true);
1095 if (!_dragging_playhead) {
1096 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1099 mouse_add_new_tempo_event (where);
1104 if (!_dragging_playhead) {
1105 mouse_add_new_meter_event (pixel_to_frame (event->button.x));
1114 switch (mouse_mode) {
1116 switch (item_type) {
1117 case AutomationTrackItem:
1118 atv = dynamic_cast<AutomationTimeAxisView*>(clicked_trackview);
1120 atv->add_automation_event (item, event, where, event->button.y);
1132 // Gain only makes sense for audio regions
1134 if (!dynamic_cast<AudioRegionView*>(clicked_regionview)) {
1138 switch (item_type) {
1140 /* check that we didn't drag before releasing, since
1141 its really annoying to create new control
1142 points when doing this.
1144 if (drag_info.first_move) {
1145 dynamic_cast<AudioRegionView*>(clicked_regionview)->add_gain_point_event (item, event);
1150 case AutomationTrackItem:
1151 dynamic_cast<AutomationTimeAxisView*>(clicked_trackview)->
1152 add_automation_event (item, event, where, event->button.y);
1162 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1163 if (scrubbing_direction == 0) {
1164 /* no drag, just a click */
1165 switch (item_type) {
1167 play_selected_region ();
1173 /* make sure we stop */
1174 session->request_transport_speed (0.0);
1188 switch (mouse_mode) {
1191 switch (item_type) {
1193 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1195 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier|Keyboard::SecondaryModifier))) {
1198 // Button2 click is unused
1211 // x_style_paste (where, 1.0);
1231 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1237 if (last_item_entered != item) {
1238 last_item_entered = item;
1239 last_item_entered_n = 0;
1242 switch (item_type) {
1243 case GainControlPointItem:
1244 if (mouse_mode == MouseGain) {
1245 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1246 cp->set_visible (true);
1250 at_y = cp->get_y ();
1251 cp->item->i2w (at_x, at_y);
1255 fraction = 1.0 - (cp->get_y() / cp->line.height());
1257 if (is_drawable() && !_scrubbing) {
1258 track_canvas->get_window()->set_cursor (*fader_cursor);
1261 last_item_entered_n++;
1262 set_verbose_canvas_cursor (cp->line.get_verbose_cursor_string (fraction), at_x, at_y);
1263 if (last_item_entered_n < 10) {
1264 show_verbose_canvas_cursor ();
1269 case GainAutomationControlPointItem:
1270 case PanAutomationControlPointItem:
1271 case RedirectAutomationControlPointItem:
1272 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1273 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1274 cp->set_visible (true);
1278 at_y = cp->get_y ();
1279 cp->item->i2w (at_x, at_y);
1283 fraction = 1.0 - (cp->get_y() / cp->line.height());
1285 set_verbose_canvas_cursor (cp->line.get_verbose_cursor_string (fraction), at_x, at_y);
1286 show_verbose_canvas_cursor ();
1288 if (is_drawable()) {
1289 track_canvas->get_window()->set_cursor (*fader_cursor);
1295 if (mouse_mode == MouseGain) {
1296 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1298 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredGainLine.get();
1299 if (is_drawable()) {
1300 track_canvas->get_window()->set_cursor (*fader_cursor);
1305 case GainAutomationLineItem:
1306 case RedirectAutomationLineItem:
1307 case PanAutomationLineItem:
1308 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1310 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1312 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredAutomationLine.get();
1314 if (is_drawable()) {
1315 track_canvas->get_window()->set_cursor (*fader_cursor);
1320 case RegionViewNameHighlight:
1321 if (is_drawable() && mouse_mode == MouseObject) {
1322 track_canvas->get_window()->set_cursor (*trimmer_cursor);
1326 case StartSelectionTrimItem:
1327 case EndSelectionTrimItem:
1328 /* <CMT Additions> */
1329 case ImageFrameHandleStartItem:
1330 case ImageFrameHandleEndItem:
1331 case MarkerViewHandleStartItem:
1332 case MarkerViewHandleEndItem:
1333 /* </CMT Additions> */
1335 if (is_drawable()) {
1336 track_canvas->get_window()->set_cursor (*trimmer_cursor);
1340 case PlayheadCursorItem:
1341 if (is_drawable()) {
1342 switch (_edit_point) {
1344 track_canvas->get_window()->set_cursor (*grabber_edit_point_cursor);
1347 track_canvas->get_window()->set_cursor (*grabber_cursor);
1353 case RegionViewName:
1355 /* when the name is not an active item, the entire name highlight is for trimming */
1357 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1358 if (mouse_mode == MouseObject && is_drawable()) {
1359 track_canvas->get_window()->set_cursor (*trimmer_cursor);
1365 case AutomationTrackItem:
1366 if (is_drawable()) {
1367 Gdk::Cursor *cursor;
1368 switch (mouse_mode) {
1370 cursor = selector_cursor;
1373 cursor = zoom_cursor;
1376 cursor = cross_hair_cursor;
1380 track_canvas->get_window()->set_cursor (*cursor);
1382 AutomationTimeAxisView* atv;
1383 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1384 clear_entered_track = false;
1385 set_entered_track (atv);
1391 case RangeMarkerBarItem:
1392 case TransportMarkerBarItem:
1393 case CdMarkerBarItem:
1396 if (is_drawable()) {
1397 track_canvas->get_window()->set_cursor (*timebar_cursor);
1402 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1405 entered_marker = marker;
1406 marker->set_color_rgba (ARDOUR_UI::config()->canvasvar_EnteredMarker.get());
1408 case MeterMarkerItem:
1409 case TempoMarkerItem:
1410 if (is_drawable()) {
1411 track_canvas->get_window()->set_cursor (*timebar_cursor);
1414 case FadeInHandleItem:
1415 case FadeOutHandleItem:
1416 if (mouse_mode == MouseObject) {
1417 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1419 rect->property_fill_color_rgba() = 0;
1420 rect->property_outline_pixels() = 1;
1429 /* second pass to handle entered track status in a comprehensible way.
1432 switch (item_type) {
1434 case GainAutomationLineItem:
1435 case RedirectAutomationLineItem:
1436 case PanAutomationLineItem:
1437 case GainControlPointItem:
1438 case GainAutomationControlPointItem:
1439 case PanAutomationControlPointItem:
1440 case RedirectAutomationControlPointItem:
1441 /* these do not affect the current entered track state */
1442 clear_entered_track = false;
1445 case AutomationTrackItem:
1446 /* handled above already */
1450 set_entered_track (0);
1458 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1467 switch (item_type) {
1468 case GainControlPointItem:
1469 case GainAutomationControlPointItem:
1470 case PanAutomationControlPointItem:
1471 case RedirectAutomationControlPointItem:
1472 cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
1473 if (cp->line.npoints() > 1) {
1474 if (!cp->selected) {
1475 cp->set_visible (false);
1479 if (is_drawable()) {
1480 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1483 hide_verbose_canvas_cursor ();
1486 case RegionViewNameHighlight:
1487 case StartSelectionTrimItem:
1488 case EndSelectionTrimItem:
1489 case PlayheadCursorItem:
1490 /* <CMT Additions> */
1491 case ImageFrameHandleStartItem:
1492 case ImageFrameHandleEndItem:
1493 case MarkerViewHandleStartItem:
1494 case MarkerViewHandleEndItem:
1495 /* </CMT Additions> */
1496 if (is_drawable()) {
1497 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1502 case GainAutomationLineItem:
1503 case RedirectAutomationLineItem:
1504 case PanAutomationLineItem:
1505 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1507 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1509 line->property_fill_color_rgba() = al->get_line_color();
1511 if (is_drawable()) {
1512 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1516 case RegionViewName:
1517 /* see enter_handler() for notes */
1518 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1519 if (is_drawable() && mouse_mode == MouseObject) {
1520 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1525 case RangeMarkerBarItem:
1526 case TransportMarkerBarItem:
1527 case CdMarkerBarItem:
1531 if (is_drawable()) {
1532 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1537 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1541 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
1542 location_flags_changed (loc, this);
1545 case MeterMarkerItem:
1546 case TempoMarkerItem:
1548 if (is_drawable()) {
1549 track_canvas->get_window()->set_cursor (*timebar_cursor);
1554 case FadeInHandleItem:
1555 case FadeOutHandleItem:
1556 rv = static_cast<RegionView*>(item->get_data ("regionview"));
1558 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1560 rect->property_fill_color_rgba() = rv->get_fill_color();
1561 rect->property_outline_pixels() = 0;
1566 case AutomationTrackItem:
1567 if (is_drawable()) {
1568 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1569 clear_entered_track = true;
1570 Glib::signal_idle().connect (mem_fun(*this, &Editor::left_automation_track));
1582 Editor::left_automation_track ()
1584 if (clear_entered_track) {
1585 set_entered_track (0);
1586 clear_entered_track = false;
1596 if (scrubbing_direction == 0) {
1598 session->request_locate (drag_info.current_pointer_frame, false);
1599 session->request_transport_speed (0.1);
1600 scrubbing_direction = 1;
1604 if (last_scrub_x > drag_info.current_pointer_x) {
1606 /* pointer moved to the left */
1608 if (scrubbing_direction > 0) {
1610 /* we reversed direction to go backwards */
1613 scrub_reverse_distance += (int) (last_scrub_x - drag_info.current_pointer_x);
1617 /* still moving to the left (backwards) */
1619 scrub_reversals = 0;
1620 scrub_reverse_distance = 0;
1622 delta = 0.01 * (last_scrub_x - drag_info.current_pointer_x);
1623 session->request_transport_speed (session->transport_speed() - delta);
1627 /* pointer moved to the right */
1629 if (scrubbing_direction < 0) {
1630 /* we reversed direction to go forward */
1633 scrub_reverse_distance += (int) (drag_info.current_pointer_x - last_scrub_x);
1636 /* still moving to the right */
1638 scrub_reversals = 0;
1639 scrub_reverse_distance = 0;
1641 delta = 0.01 * (drag_info.current_pointer_x - last_scrub_x);
1642 session->request_transport_speed (session->transport_speed() + delta);
1646 /* if there have been more than 2 opposite motion moves detected, or one that moves
1647 back more than 10 pixels, reverse direction
1650 if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
1652 if (scrubbing_direction > 0) {
1653 /* was forwards, go backwards */
1654 session->request_transport_speed (-0.1);
1655 scrubbing_direction = -1;
1657 /* was backwards, go forwards */
1658 session->request_transport_speed (0.1);
1659 scrubbing_direction = 1;
1662 scrub_reverse_distance = 0;
1663 scrub_reversals = 0;
1667 last_scrub_x = drag_info.current_pointer_x;
1671 Editor::motion_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type, bool from_autoscroll)
1673 if (event->motion.is_hint) {
1676 /* We call this so that MOTION_NOTIFY events continue to be
1677 delivered to the canvas. We need to do this because we set
1678 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
1679 the density of the events, at the expense of a round-trip
1680 to the server. Given that this will mostly occur on cases
1681 where DISPLAY = :0.0, and given the cost of what the motion
1682 event might do, its a good tradeoff.
1685 track_canvas->get_pointer (x, y);
1688 if (current_stepping_trackview) {
1689 /* don't keep the persistent stepped trackview if the mouse moves */
1690 current_stepping_trackview = 0;
1691 step_timeout.disconnect ();
1694 if (session && session->actively_recording()) {
1695 /* Sorry. no dragging stuff around while we record */
1699 drag_info.item_type = item_type;
1700 drag_info.last_pointer_x = drag_info.current_pointer_x;
1701 drag_info.last_pointer_y = drag_info.current_pointer_y;
1702 drag_info.current_pointer_frame = event_frame (event, &drag_info.current_pointer_x,
1703 &drag_info.current_pointer_y);
1706 switch (mouse_mode) {
1717 if (!from_autoscroll && drag_info.item) {
1718 /* item != 0 is the best test i can think of for dragging.
1720 if (!drag_info.move_threshold_passed) {
1722 bool x_threshold_passed = (::llabs ((nframes64_t) (drag_info.current_pointer_x - drag_info.grab_x)) > 4LL);
1723 bool y_threshold_passed = (::llabs ((nframes64_t) (drag_info.current_pointer_y - drag_info.grab_y)) > 4LL);
1725 drag_info.move_threshold_passed = (x_threshold_passed || y_threshold_passed);
1727 // and change the initial grab loc/frame if this drag info wants us to
1729 if (drag_info.want_move_threshold && drag_info.move_threshold_passed) {
1730 drag_info.grab_frame = drag_info.current_pointer_frame;
1731 drag_info.grab_x = drag_info.current_pointer_x;
1732 drag_info.grab_y = drag_info.current_pointer_y;
1733 drag_info.last_pointer_frame = drag_info.grab_frame;
1734 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
1739 switch (item_type) {
1740 case PlayheadCursorItem:
1745 case RangeMarkerBarItem:
1746 case TransportMarkerBarItem:
1747 case CdMarkerBarItem:
1748 case GainControlPointItem:
1749 case RedirectAutomationControlPointItem:
1750 case GainAutomationControlPointItem:
1751 case PanAutomationControlPointItem:
1752 case TempoMarkerItem:
1753 case MeterMarkerItem:
1754 case RegionViewNameHighlight:
1755 case StartSelectionTrimItem:
1756 case EndSelectionTrimItem:
1759 case RedirectAutomationLineItem:
1760 case GainAutomationLineItem:
1761 case PanAutomationLineItem:
1762 case FadeInHandleItem:
1763 case FadeOutHandleItem:
1764 /* <CMT Additions> */
1765 case ImageFrameHandleStartItem:
1766 case ImageFrameHandleEndItem:
1767 case MarkerViewHandleStartItem:
1768 case MarkerViewHandleEndItem:
1769 /* </CMT Additions> */
1770 if (drag_info.item && (event->motion.state & Gdk::BUTTON1_MASK ||
1771 (event->motion.state & Gdk::BUTTON2_MASK))) {
1772 if (!from_autoscroll) {
1773 maybe_autoscroll_horizontally (&event->motion);
1775 if (drag_info.motion_callback) {
1776 (this->*(drag_info.motion_callback)) (item, event);
1786 switch (mouse_mode) {
1788 if (item_type == RegionItem) {
1789 if (drag_info.item && drag_info.motion_callback) {
1790 (this->*(drag_info.motion_callback)) (item, event);
1800 if (drag_info.item && (event->motion.state & GDK_BUTTON1_MASK ||
1801 (event->motion.state & GDK_BUTTON2_MASK))) {
1802 if (!from_autoscroll) {
1803 maybe_autoscroll (&event->motion);
1805 if (drag_info.motion_callback) {
1806 (this->*(drag_info.motion_callback)) (item, event);
1818 track_canvas_motion (event);
1819 // drag_info.last_pointer_frame = drag_info.current_pointer_frame;
1827 Editor::break_drag ()
1829 stop_canvas_autoscroll ();
1830 hide_verbose_canvas_cursor ();
1832 if (drag_info.item) {
1833 drag_info.item->ungrab (0);
1835 /* put it back where it came from */
1840 drag_info.item->i2w (cxw, cyw);
1841 drag_info.item->move (drag_info.original_x - cxw, drag_info.original_y - cyw);
1848 Editor::finalize_drag ()
1851 drag_info.copy = false;
1852 drag_info.motion_callback = 0;
1853 drag_info.finished_callback = 0;
1854 drag_info.dest_trackview = 0;
1855 drag_info.source_trackview = 0;
1856 drag_info.last_frame_position = 0;
1857 drag_info.grab_frame = 0;
1858 drag_info.last_pointer_frame = 0;
1859 drag_info.current_pointer_frame = 0;
1860 drag_info.brushing = false;
1861 range_marker_drag_rect->hide();
1862 drag_info.clear_copied_locations ();
1866 Editor::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
1868 if (drag_info.item == 0) {
1869 fatal << _("programming error: start_grab called without drag item") << endmsg;
1875 cursor = which_grabber_cursor ();
1878 // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
1880 if (Keyboard::is_button2_event (&event->button)) {
1881 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
1882 drag_info.y_constrained = true;
1883 drag_info.x_constrained = false;
1885 drag_info.y_constrained = false;
1886 drag_info.x_constrained = true;
1889 drag_info.x_constrained = false;
1890 drag_info.y_constrained = false;
1893 drag_info.grab_frame = event_frame (event, &drag_info.grab_x, &drag_info.grab_y);
1894 drag_info.last_pointer_frame = drag_info.grab_frame;
1895 drag_info.current_pointer_frame = drag_info.grab_frame;
1896 drag_info.current_pointer_x = drag_info.grab_x;
1897 drag_info.current_pointer_y = drag_info.grab_y;
1898 drag_info.last_pointer_x = drag_info.current_pointer_x;
1899 drag_info.last_pointer_y = drag_info.current_pointer_y;
1900 drag_info.cumulative_x_drag = 0;
1901 drag_info.cumulative_y_drag = 0;
1902 drag_info.first_move = true;
1903 drag_info.move_threshold_passed = false;
1904 drag_info.want_move_threshold = false;
1905 drag_info.pointer_frame_offset = 0;
1906 drag_info.brushing = false;
1907 drag_info.clear_copied_locations ();
1909 drag_info.original_x = 0;
1910 drag_info.original_y = 0;
1911 drag_info.item->i2w (drag_info.original_x, drag_info.original_y);
1913 drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK,
1915 event->button.time);
1917 if (session && session->transport_rolling()) {
1918 drag_info.was_rolling = true;
1920 drag_info.was_rolling = false;
1923 switch (snap_type) {
1924 case SnapToRegionStart:
1925 case SnapToRegionEnd:
1926 case SnapToRegionSync:
1927 case SnapToRegionBoundary:
1928 build_region_boundary_cache ();
1936 Editor::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t time)
1938 drag_info.item->ungrab (0);
1939 drag_info.item = new_item;
1942 cursor = which_grabber_cursor ();
1945 drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK, *cursor, time);
1949 Editor::end_grab (ArdourCanvas::Item* item, GdkEvent* event)
1951 bool did_drag = false;
1953 stop_canvas_autoscroll ();
1955 if (drag_info.item == 0) {
1959 drag_info.item->ungrab (event->button.time);
1961 if (drag_info.finished_callback) {
1962 drag_info.last_pointer_x = drag_info.current_pointer_x;
1963 drag_info.last_pointer_y = drag_info.current_pointer_y;
1964 (this->*(drag_info.finished_callback)) (item, event);
1967 did_drag = !drag_info.first_move;
1969 hide_verbose_canvas_cursor();
1977 Editor::region_gain_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1979 if (drag_info.first_move && drag_info.move_threshold_passed) {
1980 drag_info.first_move = false;
1985 Editor::start_fade_in_grab (ArdourCanvas::Item* item, GdkEvent* event)
1987 drag_info.item = item;
1988 drag_info.motion_callback = &Editor::fade_in_drag_motion_callback;
1989 drag_info.finished_callback = &Editor::fade_in_drag_finished_callback;
1993 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
1994 fatal << _("programming error: fade in canvas item has no regionview data pointer!") << endmsg;
1998 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
2000 drag_info.pointer_frame_offset = drag_info.grab_frame - ((nframes64_t) arv->audio_region()->fade_in().back()->when + arv->region()->position());
2004 Editor::fade_in_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2006 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
2008 nframes64_t fade_length;
2010 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2011 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2017 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2021 if (pos < (arv->region()->position() + 64)) {
2022 fade_length = 64; // this should be a minimum defined somewhere
2023 } else if (pos > arv->region()->last_frame()) {
2024 fade_length = arv->region()->length();
2026 fade_length = pos - arv->region()->position();
2028 /* mapover the region selection */
2030 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2032 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2038 tmp->reset_fade_in_shape_width (fade_length);
2041 show_verbose_duration_cursor (arv->region()->position(), arv->region()->position() + fade_length, 10);
2043 drag_info.first_move = false;
2047 Editor::fade_in_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2049 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
2051 nframes64_t fade_length;
2053 if (drag_info.first_move) return;
2055 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2056 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2061 if (pos < (arv->region()->position() + 64)) {
2062 fade_length = 64; // this should be a minimum defined somewhere
2063 } else if (pos > arv->region()->last_frame()) {
2064 fade_length = arv->region()->length();
2066 fade_length = pos - arv->region()->position();
2069 begin_reversible_command (_("change fade in length"));
2071 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2073 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2079 AutomationList& alist = tmp->audio_region()->fade_in();
2080 XMLNode &before = alist.get_state();
2082 tmp->audio_region()->set_fade_in_length (fade_length);
2083 tmp->audio_region()->set_fade_in_active (true);
2085 XMLNode &after = alist.get_state();
2086 session->add_command(new MementoCommand<AutomationList>(alist, &before, &after));
2089 commit_reversible_command ();
2093 Editor::start_fade_out_grab (ArdourCanvas::Item* item, GdkEvent* event)
2095 drag_info.item = item;
2096 drag_info.motion_callback = &Editor::fade_out_drag_motion_callback;
2097 drag_info.finished_callback = &Editor::fade_out_drag_finished_callback;
2101 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
2102 fatal << _("programming error: fade out canvas item has no regionview data pointer!") << endmsg;
2106 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
2108 drag_info.pointer_frame_offset = drag_info.grab_frame - (arv->region()->length() - (nframes64_t) arv->audio_region()->fade_out().back()->when + arv->region()->position());
2112 Editor::fade_out_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2114 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
2116 nframes64_t fade_length;
2118 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2119 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2124 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2128 if (pos > (arv->region()->last_frame() - 64)) {
2129 fade_length = 64; // this should really be a minimum fade defined somewhere
2131 else if (pos < arv->region()->position()) {
2132 fade_length = arv->region()->length();
2135 fade_length = arv->region()->last_frame() - pos;
2138 /* mapover the region selection */
2140 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2142 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2148 tmp->reset_fade_out_shape_width (fade_length);
2151 show_verbose_duration_cursor (arv->region()->last_frame() - fade_length, arv->region()->last_frame(), 10);
2153 drag_info.first_move = false;
2157 Editor::fade_out_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2159 if (drag_info.first_move) return;
2161 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
2163 nframes64_t fade_length;
2165 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2166 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2172 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2176 if (pos > (arv->region()->last_frame() - 64)) {
2177 fade_length = 64; // this should really be a minimum fade defined somewhere
2179 else if (pos < arv->region()->position()) {
2180 fade_length = arv->region()->length();
2183 fade_length = arv->region()->last_frame() - pos;
2186 begin_reversible_command (_("change fade out length"));
2188 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2190 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2196 AutomationList& alist = tmp->audio_region()->fade_out();
2197 XMLNode &before = alist.get_state();
2199 tmp->audio_region()->set_fade_out_length (fade_length);
2200 tmp->audio_region()->set_fade_out_active (true);
2202 XMLNode &after = alist.get_state();
2203 session->add_command(new MementoCommand<AutomationList>(alist, &before, &after));
2206 commit_reversible_command ();
2210 Editor::start_cursor_grab (ArdourCanvas::Item* item, GdkEvent* event)
2212 drag_info.item = item;
2213 drag_info.motion_callback = &Editor::cursor_drag_motion_callback;
2214 drag_info.finished_callback = &Editor::cursor_drag_finished_callback;
2218 if ((drag_info.data = (item->get_data ("cursor"))) == 0) {
2219 fatal << _("programming error: cursor canvas item has no cursor data pointer!") << endmsg;
2223 Cursor* cursor = (Cursor *) drag_info.data;
2225 if (cursor == playhead_cursor) {
2226 _dragging_playhead = true;
2228 if (session && drag_info.was_rolling) {
2229 session->request_stop ();
2232 if (session && session->is_auditioning()) {
2233 session->cancel_audition ();
2237 drag_info.pointer_frame_offset = drag_info.grab_frame - cursor->current_frame;
2239 show_verbose_time_cursor (cursor->current_frame, 10);
2243 Editor::start_cursor_grab_no_stop (ArdourCanvas::Item* item, GdkEvent* event)
2245 drag_info.item = item;
2246 drag_info.motion_callback = &Editor::cursor_drag_motion_callback;
2247 drag_info.finished_callback = &Editor::cursor_drag_finished_ensure_locate_callback;
2251 if ((drag_info.data = (item->get_data ("cursor"))) == 0) {
2252 fatal << _("programming error: cursor canvas item has no cursor data pointer!") << endmsg;
2256 Cursor* cursor = (Cursor *) drag_info.data;
2257 nframes64_t where = event_frame (event, 0, 0);
2260 playhead_cursor->set_position (where);
2262 if (cursor == playhead_cursor) {
2263 _dragging_playhead = true;
2265 if (session && session->is_auditioning()) {
2266 session->cancel_audition ();
2270 drag_info.pointer_frame_offset = drag_info.grab_frame - cursor->current_frame;
2272 show_verbose_time_cursor (cursor->current_frame, 10);
2276 Editor::cursor_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2278 Cursor* cursor = (Cursor *) drag_info.data;
2279 nframes64_t adjusted_frame;
2281 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2282 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2288 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2289 if (cursor == playhead_cursor) {
2290 snap_to (adjusted_frame);
2294 if (adjusted_frame == drag_info.last_pointer_frame) return;
2296 cursor->set_position (adjusted_frame);
2298 show_verbose_time_cursor (cursor->current_frame, 10);
2301 track_canvas->update_now ();
2303 UpdateAllTransportClocks (cursor->current_frame);
2305 drag_info.last_pointer_frame = adjusted_frame;
2306 drag_info.first_move = false;
2310 Editor::cursor_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2312 _dragging_playhead = false;
2314 if (drag_info.first_move) {
2318 cursor_drag_motion_callback (item, event);
2320 if (item == &playhead_cursor->canvas_item) {
2322 session->request_locate (playhead_cursor->current_frame, drag_info.was_rolling);
2328 Editor::cursor_drag_finished_ensure_locate_callback (ArdourCanvas::Item* item, GdkEvent* event)
2330 _dragging_playhead = false;
2332 cursor_drag_motion_callback (item, event);
2334 if (item == &playhead_cursor->canvas_item) {
2336 session->request_locate (playhead_cursor->current_frame, drag_info.was_rolling);
2342 Editor::update_marker_drag_item (Location *location)
2344 double x1 = frame_to_pixel (location->start());
2345 double x2 = frame_to_pixel (location->end());
2347 if (location->is_mark()) {
2348 marker_drag_line_points.front().set_x(x1);
2349 marker_drag_line_points.back().set_x(x1);
2350 marker_drag_line->property_points() = marker_drag_line_points;
2352 range_marker_drag_rect->property_x1() = x1;
2353 range_marker_drag_rect->property_x2() = x2;
2358 Editor::start_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2362 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2363 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2369 Location *location = find_location_from_marker (marker, is_start);
2371 drag_info.item = item;
2372 drag_info.data = marker;
2373 drag_info.motion_callback = &Editor::marker_drag_motion_callback;
2374 drag_info.finished_callback = &Editor::marker_drag_finished_callback;
2378 _dragging_edit_point = true;
2380 drag_info.pointer_frame_offset = drag_info.grab_frame - (is_start ? location->start() : location->end());
2382 update_marker_drag_item (location);
2384 if (location->is_mark()) {
2385 // marker_drag_line->show();
2386 // marker_drag_line->raise_to_top();
2388 range_marker_drag_rect->show();
2389 //range_marker_drag_rect->raise_to_top();
2393 show_verbose_time_cursor (location->start(), 10);
2395 show_verbose_time_cursor (location->end(), 10);
2398 Selection::Operation op = Keyboard::selection_type (event->button.state);
2401 case Selection::Toggle:
2402 selection->toggle (marker);
2404 case Selection::Set:
2405 if (!selection->selected (marker)) {
2406 selection->set (marker);
2409 case Selection::Extend:
2411 Locations::LocationList ll;
2412 list<Marker*> to_add;
2414 selection->markers.range (s, e);
2415 s = min (marker->position(), s);
2416 e = max (marker->position(), e);
2419 if (e < max_frames) {
2422 session->locations()->find_all_between (s, e, ll, Location::Flags (0));
2423 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
2424 LocationMarkers* lm = find_location_markers (*i);
2427 to_add.push_back (lm->start);
2430 to_add.push_back (lm->end);
2434 if (!to_add.empty()) {
2435 selection->add (to_add);
2439 case Selection::Add:
2440 selection->add (marker);
2444 /* set up copies for us to manipulate during the drag */
2446 drag_info.clear_copied_locations ();
2448 for (MarkerSelection::iterator i = selection->markers.begin(); i != selection->markers.end(); ++i) {
2449 Location *l = find_location_from_marker (*i, is_start);
2450 drag_info.copied_locations.push_back (new Location (*l));
2455 Editor::marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2457 nframes64_t f_delta = 0;
2458 nframes64_t newframe;
2460 bool move_both = false;
2461 Marker* dragged_marker = (Marker*) drag_info.data;
2463 Location *real_location;
2464 Location *copy_location;
2466 if (drag_info.pointer_frame_offset <= drag_info.current_pointer_frame) {
2467 newframe = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2472 nframes64_t next = newframe;
2474 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2475 snap_to (newframe, 0, true);
2478 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
2482 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2486 MarkerSelection::iterator i;
2487 list<Location*>::iterator x;
2489 /* find the marker we're dragging, and compute the delta */
2491 for (i = selection->markers.begin(), x = drag_info.copied_locations.begin();
2492 x != drag_info.copied_locations.end() && i != selection->markers.end();
2498 if (marker == dragged_marker) {
2500 if ((real_location = find_location_from_marker (marker, is_start)) == 0) {
2505 if (real_location->is_mark()) {
2506 f_delta = newframe - copy_location->start();
2510 switch (marker->type()) {
2512 case Marker::LoopStart:
2513 case Marker::PunchIn:
2514 f_delta = newframe - copy_location->start();
2518 case Marker::LoopEnd:
2519 case Marker::PunchOut:
2520 f_delta = newframe - copy_location->end();
2523 /* what kind of marker is this ? */
2531 if (i == selection->markers.end()) {
2532 /* hmm, impossible - we didn't find the dragged marker */
2536 /* now move them all */
2538 for (i = selection->markers.begin(), x = drag_info.copied_locations.begin();
2539 x != drag_info.copied_locations.end() && i != selection->markers.end();
2545 /* call this to find out if its the start or end */
2547 if ((real_location = find_location_from_marker (marker, is_start)) == 0) {
2551 if (real_location->locked()) {
2555 if (copy_location->is_mark()) {
2559 copy_location->set_start (copy_location->start() + f_delta);
2563 nframes64_t new_start = copy_location->start() + f_delta;
2564 nframes64_t new_end = copy_location->end() + f_delta;
2566 if (is_start) { // start-of-range marker
2569 copy_location->set_start (new_start);
2570 copy_location->set_end (new_end);
2571 } else if (new_start < copy_location->end()) {
2572 copy_location->set_start (new_start);
2574 snap_to (next, 1, true);
2575 copy_location->set_end (next);
2576 copy_location->set_start (newframe);
2579 } else { // end marker
2582 copy_location->set_end (new_end);
2583 copy_location->set_start (new_start);
2584 } else if (new_end > copy_location->start()) {
2585 copy_location->set_end (new_end);
2586 } else if (newframe > 0) {
2587 snap_to (next, -1, true);
2588 copy_location->set_start (next);
2589 copy_location->set_end (newframe);
2593 update_marker_drag_item (copy_location);
2595 LocationMarkers* lm = find_location_markers (real_location);
2598 lm->set_position (copy_location->start(), copy_location->end());
2602 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
2603 drag_info.first_move = false;
2605 if (drag_info.copied_locations.empty()) {
2609 edit_point_clock.set (drag_info.copied_locations.front()->start());
2610 show_verbose_time_cursor (newframe, 10);
2613 track_canvas->update_now ();
2618 Editor::marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2620 if (drag_info.first_move) {
2622 /* just a click, do nothing but finish
2623 off the selection process
2626 Selection::Operation op = Keyboard::selection_type (event->button.state);
2627 Marker* marker = (Marker *) drag_info.data;
2630 case Selection::Set:
2631 if (selection->selected (marker) && selection->markers.size() > 1) {
2632 selection->set (marker);
2636 case Selection::Toggle:
2637 case Selection::Extend:
2638 case Selection::Add:
2645 _dragging_edit_point = false;
2648 begin_reversible_command ( _("move marker") );
2649 XMLNode &before = session->locations()->get_state();
2651 MarkerSelection::iterator i;
2652 list<Location*>::iterator x;
2655 for (i = selection->markers.begin(), x = drag_info.copied_locations.begin();
2656 x != drag_info.copied_locations.end() && i != selection->markers.end();
2659 Location * location = find_location_from_marker ((*i), is_start);
2663 if (location->locked()) {
2667 if (location->is_mark()) {
2668 location->set_start ((*x)->start());
2670 location->set ((*x)->start(), (*x)->end());
2675 XMLNode &after = session->locations()->get_state();
2676 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
2677 commit_reversible_command ();
2679 marker_drag_line->hide();
2680 range_marker_drag_rect->hide();
2684 Editor::start_meter_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2687 MeterMarker* meter_marker;
2689 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2690 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2694 meter_marker = dynamic_cast<MeterMarker*> (marker);
2696 MetricSection& section (meter_marker->meter());
2698 if (!section.movable()) {
2702 drag_info.item = item;
2703 drag_info.copy = false;
2704 drag_info.data = marker;
2705 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2706 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2710 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2712 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2716 Editor::start_meter_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2719 MeterMarker* meter_marker;
2721 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2722 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2726 meter_marker = dynamic_cast<MeterMarker*> (marker);
2728 // create a dummy marker for visual representation of moving the copy.
2729 // The actual copying is not done before we reach the finish callback.
2731 snprintf (name, sizeof(name), "%g/%g", meter_marker->meter().beats_per_bar(), meter_marker->meter().note_divisor ());
2732 MeterMarker* new_marker = new MeterMarker(*this, *meter_group, ARDOUR_UI::config()->canvasvar_MeterMarker.get(), name,
2733 *new MeterSection(meter_marker->meter()));
2735 drag_info.item = &new_marker->the_item();
2736 drag_info.copy = true;
2737 drag_info.data = new_marker;
2738 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2739 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2743 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2745 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2749 Editor::meter_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2751 MeterMarker* marker = (MeterMarker *) drag_info.data;
2752 nframes64_t adjusted_frame;
2754 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2755 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2761 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2762 snap_to (adjusted_frame);
2765 if (adjusted_frame == drag_info.last_pointer_frame) return;
2767 marker->set_position (adjusted_frame);
2770 drag_info.last_pointer_frame = adjusted_frame;
2771 drag_info.first_move = false;
2773 show_verbose_time_cursor (adjusted_frame, 10);
2777 Editor::meter_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2779 if (drag_info.first_move) return;
2781 meter_marker_drag_motion_callback (drag_info.item, event);
2783 MeterMarker* marker = (MeterMarker *) drag_info.data;
2786 TempoMap& map (session->tempo_map());
2787 map.bbt_time (drag_info.last_pointer_frame, when);
2789 if (drag_info.copy == true) {
2790 begin_reversible_command (_("copy meter mark"));
2791 XMLNode &before = map.get_state();
2792 map.add_meter (marker->meter(), when);
2793 XMLNode &after = map.get_state();
2794 session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2795 commit_reversible_command ();
2797 // delete the dummy marker we used for visual representation of copying.
2798 // a new visual marker will show up automatically.
2801 begin_reversible_command (_("move meter mark"));
2802 XMLNode &before = map.get_state();
2803 map.move_meter (marker->meter(), when);
2804 XMLNode &after = map.get_state();
2805 session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2806 commit_reversible_command ();
2811 Editor::start_tempo_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2814 TempoMarker* tempo_marker;
2816 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2817 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2821 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2822 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2826 MetricSection& section (tempo_marker->tempo());
2828 if (!section.movable()) {
2832 drag_info.item = item;
2833 drag_info.copy = false;
2834 drag_info.data = marker;
2835 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2836 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2840 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2841 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2845 Editor::start_tempo_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2848 TempoMarker* tempo_marker;
2850 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2851 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2855 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2856 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2860 // create a dummy marker for visual representation of moving the copy.
2861 // The actual copying is not done before we reach the finish callback.
2863 snprintf (name, sizeof (name), "%.2f", tempo_marker->tempo().beats_per_minute());
2864 TempoMarker* new_marker = new TempoMarker(*this, *tempo_group, ARDOUR_UI::config()->canvasvar_TempoMarker.get(), name,
2865 *new TempoSection(tempo_marker->tempo()));
2867 drag_info.item = &new_marker->the_item();
2868 drag_info.copy = true;
2869 drag_info.data = new_marker;
2870 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2871 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2875 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2877 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2881 Editor::tempo_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2883 TempoMarker* marker = (TempoMarker *) drag_info.data;
2884 nframes64_t adjusted_frame;
2886 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2887 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2893 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2894 snap_to (adjusted_frame);
2897 if (adjusted_frame == drag_info.last_pointer_frame) return;
2899 /* OK, we've moved far enough to make it worth actually move the thing. */
2901 marker->set_position (adjusted_frame);
2903 show_verbose_time_cursor (adjusted_frame, 10);
2905 drag_info.last_pointer_frame = adjusted_frame;
2906 drag_info.first_move = false;
2910 Editor::tempo_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2912 if (drag_info.first_move) return;
2914 tempo_marker_drag_motion_callback (drag_info.item, event);
2916 TempoMarker* marker = (TempoMarker *) drag_info.data;
2919 TempoMap& map (session->tempo_map());
2920 map.bbt_time (drag_info.last_pointer_frame, when);
2922 if (drag_info.copy == true) {
2923 begin_reversible_command (_("copy tempo mark"));
2924 XMLNode &before = map.get_state();
2925 map.add_tempo (marker->tempo(), when);
2926 XMLNode &after = map.get_state();
2927 session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2928 commit_reversible_command ();
2930 // delete the dummy marker we used for visual representation of copying.
2931 // a new visual marker will show up automatically.
2934 begin_reversible_command (_("move tempo mark"));
2935 XMLNode &before = map.get_state();
2936 map.move_tempo (marker->tempo(), when);
2937 XMLNode &after = map.get_state();
2938 session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2939 commit_reversible_command ();
2944 Editor::remove_gain_control_point (ArdourCanvas::Item*item, GdkEvent* event)
2946 ControlPoint* control_point;
2948 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2949 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2953 // We shouldn't remove the first or last gain point
2954 if (control_point->line.is_last_point(*control_point) ||
2955 control_point->line.is_first_point(*control_point)) {
2959 control_point->line.remove_point (*control_point);
2963 Editor::remove_control_point (ArdourCanvas::Item*item, GdkEvent* event)
2965 ControlPoint* control_point;
2967 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2968 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2972 control_point->line.remove_point (*control_point);
2976 Editor::start_control_point_grab (ArdourCanvas::Item* item, GdkEvent* event)
2978 ControlPoint* control_point;
2980 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2981 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2985 drag_info.item = item;
2986 drag_info.data = control_point;
2987 drag_info.motion_callback = &Editor::control_point_drag_motion_callback;
2988 drag_info.finished_callback = &Editor::control_point_drag_finished_callback;
2990 start_grab (event, fader_cursor);
2992 // start the grab at the center of the control point so
2993 // the point doesn't 'jump' to the mouse after the first drag
2994 drag_info.grab_x = control_point->get_x();
2995 drag_info.grab_y = control_point->get_y();
2996 control_point->line.parent_group().i2w(drag_info.grab_x, drag_info.grab_y);
2997 track_canvas->w2c(drag_info.grab_x, drag_info.grab_y, drag_info.grab_x, drag_info.grab_y);
2999 drag_info.grab_frame = pixel_to_frame(drag_info.grab_x);
3001 control_point->line.start_drag (control_point, drag_info.grab_frame, 0);
3003 float fraction = 1.0 - (control_point->get_y() / control_point->line.height());
3004 set_verbose_canvas_cursor (control_point->line.get_verbose_cursor_string (fraction),
3005 drag_info.current_pointer_x + 10, drag_info.current_pointer_y + 10);
3007 show_verbose_canvas_cursor ();
3011 Editor::control_point_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
3013 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
3015 double dx = drag_info.current_pointer_x - drag_info.last_pointer_x;
3016 double dy = drag_info.current_pointer_y - drag_info.last_pointer_y;
3018 if (event->button.state & Keyboard::SecondaryModifier) {
3023 double cx = drag_info.grab_x + drag_info.cumulative_x_drag + dx;
3024 double cy = drag_info.grab_y + drag_info.cumulative_y_drag + dy;
3026 // calculate zero crossing point. back off by .01 to stay on the
3027 // positive side of zero
3029 double zero_gain_y = (1.0 - ZERO_GAIN_FRACTION) * cp->line.height() - .01;
3030 cp->line.parent_group().i2w(_unused, zero_gain_y);
3032 // make sure we hit zero when passing through
3033 if ((cy < zero_gain_y and (cy - dy) > zero_gain_y)
3034 or (cy > zero_gain_y and (cy - dy) < zero_gain_y)) {
3038 if (drag_info.x_constrained) {
3039 cx = drag_info.grab_x;
3041 if (drag_info.y_constrained) {
3042 cy = drag_info.grab_y;
3045 drag_info.cumulative_x_drag = cx - drag_info.grab_x;
3046 drag_info.cumulative_y_drag = cy - drag_info.grab_y;
3048 cp->line.parent_group().w2i (cx, cy);
3052 cy = min ((double) cp->line.height(), cy);
3054 //translate cx to frames
3055 nframes64_t cx_frames = unit_to_frame (cx);
3057 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier()) && !drag_info.x_constrained) {
3058 snap_to (cx_frames);
3061 float fraction = 1.0 - (cy / cp->line.height());
3065 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) {
3071 cp->line.point_drag (*cp, cx_frames , fraction, push);
3073 set_verbose_canvas_cursor_text (cp->line.get_verbose_cursor_string (fraction));
3075 drag_info.first_move = false;
3079 Editor::control_point_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3081 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
3083 if (drag_info.first_move) {
3087 if ((event->type == GDK_BUTTON_RELEASE) && (event->button.button == 1) && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3088 reset_point_selection ();
3092 control_point_drag_motion_callback (item, event);
3094 cp->line.end_drag (cp);
3098 Editor::start_line_grab_from_regionview (ArdourCanvas::Item* item, GdkEvent* event)
3100 switch (mouse_mode) {
3102 assert(dynamic_cast<AudioRegionView*>(clicked_regionview));
3103 start_line_grab (dynamic_cast<AudioRegionView*>(clicked_regionview)->get_gain_line(), event);
3111 Editor::start_line_grab_from_line (ArdourCanvas::Item* item, GdkEvent* event)
3115 if ((al = reinterpret_cast<AutomationLine*> (item->get_data ("line"))) == 0) {
3116 fatal << _("programming error: line canvas item has no line pointer!") << endmsg;
3120 start_line_grab (al, event);
3124 Editor::start_line_grab (AutomationLine* line, GdkEvent* event)
3128 nframes64_t frame_within_region;
3130 /* need to get x coordinate in terms of parent (TimeAxisItemView)
3131 origin, and ditto for y.
3134 cx = event->button.x;
3135 cy = event->button.y;
3137 line->parent_group().w2i (cx, cy);
3139 frame_within_region = (nframes64_t) floor (cx * frames_per_unit);
3141 if (!line->control_points_adjacent (frame_within_region, current_line_drag_info.before,
3142 current_line_drag_info.after)) {
3143 /* no adjacent points */
3147 drag_info.item = &line->grab_item();
3148 drag_info.data = line;
3149 drag_info.motion_callback = &Editor::line_drag_motion_callback;
3150 drag_info.finished_callback = &Editor::line_drag_finished_callback;
3152 start_grab (event, fader_cursor);
3154 /* store grab start in parent frame */
3156 drag_info.grab_x = cx;
3157 drag_info.grab_y = cy;
3159 double fraction = 1.0 - (cy / line->height());
3161 line->start_drag (0, drag_info.grab_frame, fraction);
3163 set_verbose_canvas_cursor (line->get_verbose_cursor_string (fraction),
3164 drag_info.current_pointer_x + 10, drag_info.current_pointer_y + 10);
3165 show_verbose_canvas_cursor ();
3169 Editor::line_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
3171 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
3173 double dy = drag_info.current_pointer_y - drag_info.last_pointer_y;
3175 if (event->button.state & Keyboard::SecondaryModifier) {
3179 double cy = drag_info.grab_y + drag_info.cumulative_y_drag + dy;
3181 // calculate zero crossing point. back off by .01 to stay on the
3182 // positive side of zero
3183 double zero_gain_y = (1.0 - ZERO_GAIN_FRACTION) * line->height() - .01;
3185 // line->parent_group().i2w(_unused, zero_gain_y);
3187 // make sure we hit zero when passing through
3188 if ((cy < zero_gain_y and (cy - dy) > zero_gain_y)
3189 or (cy > zero_gain_y and (cy - dy) < zero_gain_y)) {
3193 drag_info.cumulative_y_drag = cy - drag_info.grab_y;
3196 cy = min ((double) line->height(), cy);
3199 double fraction = 1.0 - (cy / line->height());
3203 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) {
3209 line->line_drag (current_line_drag_info.before, current_line_drag_info.after, fraction, push);
3211 set_verbose_canvas_cursor_text (line->get_verbose_cursor_string (fraction));
3215 Editor::line_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3217 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
3218 line_drag_motion_callback (item, event);
3223 Editor::start_region_grab (ArdourCanvas::Item* item, GdkEvent* event)
3225 if (selection->regions.empty() || clicked_regionview == 0) {
3229 drag_info.copy = false;
3230 drag_info.item = item;
3231 drag_info.data = clicked_regionview;
3233 if (Config->get_edit_mode() == Splice) {
3234 drag_info.motion_callback = &Editor::region_drag_splice_motion_callback;
3235 drag_info.finished_callback = &Editor::region_drag_splice_finished_callback;
3237 drag_info.motion_callback = &Editor::region_drag_motion_callback;
3238 drag_info.finished_callback = &Editor::region_drag_finished_callback;
3244 TimeAxisView* tvp = clicked_trackview;
3245 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
3247 if (tv && tv->is_audio_track()) {
3248 speed = tv->get_diskstream()->speed();
3251 drag_info.last_frame_position = (nframes64_t) (clicked_regionview->region()->position() / speed);
3252 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
3253 drag_info.source_trackview = &clicked_regionview->get_time_axis_view();
3254 drag_info.dest_trackview = drag_info.source_trackview;
3255 // we want a move threshold
3256 drag_info.want_move_threshold = true;
3258 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3260 begin_reversible_command (_("move region(s)"));
3262 _region_motion_group->raise_to_top ();
3264 /* sync the canvas to what we think is its current state */
3265 track_canvas->update_now();
3269 Editor::start_region_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
3271 if (selection->regions.empty() || clicked_regionview == 0) {
3275 drag_info.copy = true;
3276 drag_info.item = item;
3277 drag_info.data = clicked_regionview;
3281 TimeAxisView* tv = &clicked_regionview->get_time_axis_view();
3282 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*>(tv);
3285 if (atv && atv->is_audio_track()) {
3286 speed = atv->get_diskstream()->speed();
3289 drag_info.source_trackview = &clicked_regionview->get_time_axis_view();
3290 drag_info.dest_trackview = drag_info.source_trackview;
3291 drag_info.last_frame_position = (nframes64_t) (clicked_regionview->region()->position() / speed);
3292 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
3293 // we want a move threshold
3294 drag_info.want_move_threshold = true;
3295 drag_info.motion_callback = &Editor::region_drag_motion_callback;
3296 drag_info.finished_callback = &Editor::region_drag_finished_callback;
3297 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3298 _region_motion_group->raise_to_top ();
3302 Editor::start_region_brush_grab (ArdourCanvas::Item* item, GdkEvent* event)
3304 if (selection->regions.empty() || clicked_regionview == 0 || Config->get_edit_mode() == Splice) {
3308 drag_info.copy = false;
3309 drag_info.item = item;
3310 drag_info.data = clicked_regionview;
3311 drag_info.motion_callback = &Editor::region_drag_motion_callback;
3312 drag_info.finished_callback = &Editor::region_drag_finished_callback;
3317 TimeAxisView* tvp = clicked_trackview;
3318 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
3320 if (tv && tv->is_audio_track()) {
3321 speed = tv->get_diskstream()->speed();
3324 drag_info.last_frame_position = (nframes64_t) (clicked_regionview->region()->position() / speed);
3325 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
3326 drag_info.source_trackview = &clicked_regionview->get_time_axis_view();
3327 drag_info.dest_trackview = drag_info.source_trackview;
3328 // we want a move threshold
3329 drag_info.want_move_threshold = true;
3330 drag_info.brushing = true;
3332 begin_reversible_command (_("Drag region brush"));
3336 Editor::possibly_copy_regions_during_grab (GdkEvent* event)
3338 if (drag_info.copy && drag_info.move_threshold_passed && drag_info.want_move_threshold) {
3340 drag_info.want_move_threshold = false; // don't copy again
3342 /* duplicate the regionview(s) and region(s) */
3344 vector<RegionView*> new_regionviews;
3346 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3348 AudioRegionView* arv;
3350 if ((arv = dynamic_cast<AudioRegionView*>(*i)) == 0) {
3351 /* XXX handle MIDI here */
3355 const boost::shared_ptr<const Region> original = arv->region();
3356 boost::shared_ptr<Region> region_copy = RegionFactory::create (original);
3357 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> (region_copy);
3359 nrv = new AudioRegionView (*arv, ar);
3360 nrv->get_canvas_group()->show ();
3362 new_regionviews.push_back (nrv);
3365 if (new_regionviews.empty()) {
3369 /* reset selection to new regionviews. This will not set selection visual status for
3370 these regionviews since they don't belong to a track, so do that by hand too.
3373 selection->set (new_regionviews);
3375 for (vector<RegionView*>::iterator i = new_regionviews.begin(); i != new_regionviews.end(); ++i) {
3376 (*i)->set_selected (true);
3379 /* reset drag_info data to reflect the fact that we are dragging the copies */
3381 drag_info.data = new_regionviews.front();
3383 swap_grab (new_regionviews.front()->get_canvas_group (), 0, event->motion.time);
3385 sync the canvas to what we think is its current state
3386 without it, the canvas seems to
3387 "forget" to update properly after the upcoming reparent()
3388 ..only if the mouse is in rapid motion at the time of the grab.
3389 something to do with regionview creation raking so long?
3391 track_canvas->update_now();
3396 Editor::check_region_drag_possible (AudioTimeAxisView** tv)
3398 /* Which trackview is this ? */
3400 TimeAxisView* tvp = trackview_by_y_position (drag_info.current_pointer_y);
3401 (*tv) = dynamic_cast<AudioTimeAxisView*>(tvp);
3403 /* The region motion is only processed if the pointer is over
3407 if (!(*tv) || !(*tv)->is_audio_track()) {
3408 /* To make sure we hide the verbose canvas cursor when the mouse is
3409 not held over and audiotrack.
3411 hide_verbose_canvas_cursor ();
3418 struct RegionSelectionByPosition {
3419 bool operator() (RegionView*a, RegionView* b) {
3420 return a->region()->position () < b->region()->position();
3425 Editor::region_drag_splice_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
3427 AudioTimeAxisView* tv;
3429 if (!check_region_drag_possible (&tv)) {
3433 if (!drag_info.move_threshold_passed) {
3439 if (drag_info.current_pointer_x - drag_info.grab_x > 0) {
3445 RegionSelection copy (selection->regions);
3447 RegionSelectionByPosition cmp;
3450 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
3452 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*> (&(*i)->get_time_axis_view());
3458 boost::shared_ptr<Playlist> playlist;
3460 if ((playlist = atv->playlist()) == 0) {
3464 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
3469 if (drag_info.current_pointer_frame < (*i)->region()->last_frame() + 1) {
3473 if (drag_info.current_pointer_frame > (*i)->region()->first_frame()) {
3479 playlist->shuffle ((*i)->region(), dir);
3481 drag_info.grab_x = drag_info.current_pointer_x;
3486 Editor::region_drag_splice_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3491 Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
3495 nframes64_t pending_region_position = 0;
3496 int32_t pointer_y_span = 0, canvas_pointer_y_span = 0, original_pointer_order;
3497 int32_t visible_y_high = 0, visible_y_low = 512; //high meaning higher numbered.. not the height on the screen
3498 bool clamp_y_axis = false;
3499 vector<int32_t> height_list(512) ;
3500 vector<int32_t>::iterator j;
3501 AudioTimeAxisView* tv;
3503 possibly_copy_regions_during_grab (event);
3505 if (!check_region_drag_possible (&tv)) {
3509 original_pointer_order = drag_info.dest_trackview->order;
3511 /************************************************************
3513 ************************************************************/
3515 if (drag_info.brushing) {
3516 clamp_y_axis = true;
3521 if ((pointer_y_span = (drag_info.dest_trackview->order - tv->order)) != 0) {
3523 int32_t children = 0, numtracks = 0;
3524 // XXX hard coding track limit, oh my, so very very bad
3525 bitset <1024> tracks (0x00);
3526 /* get a bitmask representing the visible tracks */
3528 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
3529 TimeAxisView *tracklist_timeview;
3530 tracklist_timeview = (*i);
3531 AudioTimeAxisView* atv2 = dynamic_cast<AudioTimeAxisView*>(tracklist_timeview);
3532 list<TimeAxisView*> children_list;
3534 /* zeroes are audio tracks. ones are other types. */
3536 if (!atv2->hidden()) {
3538 if (visible_y_high < atv2->order) {
3539 visible_y_high = atv2->order;
3541 if (visible_y_low > atv2->order) {
3542 visible_y_low = atv2->order;
3545 if (!atv2->is_audio_track()) {
3546 tracks = tracks |= (0x01 << atv2->order);
3549 height_list[atv2->order] = (*i)->current_height();
3551 if ((children_list = atv2->get_child_list()).size() > 0) {
3552 for (list<TimeAxisView*>::iterator j = children_list.begin(); j != children_list.end(); ++j) {
3553 tracks = tracks |= (0x01 << (atv2->order + children));
3554 height_list[atv2->order + children] = (*j)->current_height();
3562 /* find the actual span according to the canvas */
3564 canvas_pointer_y_span = pointer_y_span;
3565 if (drag_info.dest_trackview->order >= tv->order) {
3567 for (y = tv->order; y < drag_info.dest_trackview->order; y++) {
3568 if (height_list[y] == 0 ) {
3569 canvas_pointer_y_span--;
3574 for (y = drag_info.dest_trackview->order;y <= tv->order; y++) {
3575 if ( height_list[y] == 0 ) {
3576 canvas_pointer_y_span++;
3581 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3582 RegionView* rv2 = (*i);
3583 double ix1, ix2, iy1, iy2;
3586 if (rv2->region()->locked()) {
3590 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3591 rv2->get_canvas_group()->i2w (ix1, iy1);
3592 iy1 += vertical_adjustment.get_value() - canvas_timebars_vsize;
3594 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3595 RouteTimeAxisView* atv2 = dynamic_cast<RouteTimeAxisView*>(tvp2);
3597 if (atv2->order != original_pointer_order) {
3598 /* this isn't the pointer track */
3600 if (canvas_pointer_y_span > 0) {
3602 /* moving up the canvas */
3603 if ((atv2->order - canvas_pointer_y_span) >= visible_y_low) {
3605 int32_t visible_tracks = 0;
3606 while (visible_tracks < canvas_pointer_y_span ) {
3609 while (height_list[atv2->order - (visible_tracks - n)] == 0) {
3610 /* we're passing through a hidden track */
3615 if (tracks[atv2->order - (canvas_pointer_y_span - n)] != 0x00) {
3616 clamp_y_axis = true;
3620 clamp_y_axis = true;
3623 } else if (canvas_pointer_y_span < 0) {
3625 /*moving down the canvas*/
3627 if ((atv2->order - (canvas_pointer_y_span - n)) <= visible_y_high) { // we will overflow
3630 int32_t visible_tracks = 0;
3632 while (visible_tracks > canvas_pointer_y_span ) {
3635 while (height_list[atv2->order - (visible_tracks - n)] == 0) {
3639 if ( tracks[atv2->order - ( canvas_pointer_y_span - n)] != 0x00) {
3640 clamp_y_axis = true;
3645 clamp_y_axis = true;
3651 /* this is the pointer's track */
3652 if ((atv2->order - pointer_y_span) > visible_y_high) { // we will overflow
3653 clamp_y_axis = true;
3654 } else if ((atv2->order - pointer_y_span) < visible_y_low) { // we will underflow
3655 clamp_y_axis = true;
3663 } else if (drag_info.dest_trackview == tv) {
3664 clamp_y_axis = true;
3668 if (!clamp_y_axis) {
3669 drag_info.dest_trackview = tv;
3672 /************************************************************
3674 ************************************************************/
3676 /* compute the amount of pointer motion in frames, and where
3677 the region would be if we moved it by that much.
3679 if ( drag_info.move_threshold_passed ) {
3681 if (drag_info.current_pointer_frame >= drag_info.pointer_frame_offset) {
3683 nframes64_t sync_frame;
3684 nframes64_t sync_offset;
3687 pending_region_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
3689 sync_offset = clicked_regionview->region()->sync_offset (sync_dir);
3691 /* we don't handle a sync point that lies before zero.
3693 if (sync_dir >= 0 || (sync_dir < 0 && pending_region_position >= sync_offset)) {
3694 sync_frame = pending_region_position + (sync_dir*sync_offset);
3696 /* we snap if the snap modifier is not enabled.
3699 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3700 snap_to (sync_frame);
3703 pending_region_position = clicked_regionview->region()->adjust_to_sync (sync_frame);
3706 pending_region_position = drag_info.last_frame_position;
3710 pending_region_position = 0;
3713 if (pending_region_position > max_frames - clicked_regionview->region()->length()) {
3714 pending_region_position = drag_info.last_frame_position;
3717 // printf ("3: pending_region_position= %lu %lu\n", pending_region_position, drag_info.last_frame_position );
3719 bool x_move_allowed;
3721 if (Config->get_edit_mode() == Lock) {
3722 if (drag_info.copy) {
3723 x_move_allowed = !drag_info.x_constrained;
3725 /* in locked edit mode, reverse the usual meaning of x_constrained */
3726 x_move_allowed = drag_info.x_constrained;
3729 x_move_allowed = !drag_info.x_constrained;
3732 if (( pending_region_position != drag_info.last_frame_position) && x_move_allowed ) {
3734 /* now compute the canvas unit distance we need to move the regionview
3735 to make it appear at the new location.
3738 if (pending_region_position > drag_info.last_frame_position) {
3739 x_delta = ((double) (pending_region_position - drag_info.last_frame_position) / frames_per_unit);
3741 x_delta = -((double) (drag_info.last_frame_position - pending_region_position) / frames_per_unit);
3742 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3744 RegionView* rv2 = (*i);
3746 // If any regionview is at zero, we need to know so we can stop further leftward motion.
3748 double ix1, ix2, iy1, iy2;
3749 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3750 rv2->get_canvas_group()->i2w (ix1, iy1);
3752 if (-x_delta > ix1 + horizontal_adjustment.get_value()) {
3754 pending_region_position = drag_info.last_frame_position;
3761 drag_info.last_frame_position = pending_region_position;
3768 /* threshold not passed */
3773 /*************************************************************
3775 ************************************************************/
3777 if (x_delta == 0 && (pointer_y_span == 0)) {
3778 /* haven't reached next snap point, and we're not switching
3779 trackviews. nothing to do.
3784 /*************************************************************
3786 ************************************************************/
3787 bool do_move = true;
3788 if (drag_info.first_move) {
3789 if (!drag_info.move_threshold_passed) {
3796 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
3797 const list<RegionView*>& layered_regions = selection->regions.by_layer();
3799 for (list<RegionView*>::const_iterator i = layered_regions.begin(); i != layered_regions.end(); ++i) {
3801 RegionView* rv = (*i);
3802 double ix1, ix2, iy1, iy2;
3803 int32_t temp_pointer_y_span = pointer_y_span;
3805 if (rv->region()->locked()) {
3809 /* get item BBox, which will be relative to parent. so we have
3810 to query on a child, then convert to world coordinates using
3814 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3815 rv->get_canvas_group()->i2w (ix1, iy1);
3817 /* for evaluation of the track position of iy1, we have to adjust
3818 to allow for the vertical scrolling adjustment and the height of the timebars.
3821 iy1 += get_trackview_group_vertical_offset ();
3822 if (drag_info.first_move) {
3824 // hide any dependent views
3826 rv->get_time_axis_view().hide_dependent_views (*rv);
3829 reparent to a non scrolling group so that we can keep the
3830 region selection above all time axis views.
3831 reparenting means we have to move the rv as the two
3832 parent groups have different coordinates.
3835 rv->get_canvas_group()->property_y() = iy1 - 1;
3836 rv->get_canvas_group()->reparent(*_region_motion_group);
3838 rv->fake_set_opaque (true);
3841 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3842 AudioTimeAxisView* canvas_atv = dynamic_cast<AudioTimeAxisView*>(tvp2);
3843 AudioTimeAxisView* temp_atv;
3845 if ((pointer_y_span != 0) && !clamp_y_axis) {
3848 for (j = height_list.begin(); j!= height_list.end(); j++) {
3849 if (x == canvas_atv->order) {
3850 /* we found the track the region is on */
3851 if (x != original_pointer_order) {
3852 /*this isn't from the same track we're dragging from */
3853 temp_pointer_y_span = canvas_pointer_y_span;
3855 while (temp_pointer_y_span > 0) {
3856 /* we're moving up canvas-wise,
3857 so we need to find the next track height
3859 if (j != height_list.begin()) {
3862 if (x != original_pointer_order) {
3863 /* we're not from the dragged track, so ignore hidden tracks. */
3865 temp_pointer_y_span++;
3869 temp_pointer_y_span--;
3872 while (temp_pointer_y_span < 0) {
3874 if (x != original_pointer_order) {
3876 temp_pointer_y_span--;
3880 if (j != height_list.end()) {
3883 temp_pointer_y_span++;
3885 /* find out where we'll be when we move and set height accordingly */
3887 tvp2 = trackview_by_y_position (iy1 + y_delta);
3888 temp_atv = dynamic_cast<AudioTimeAxisView*>(tvp2);
3889 rv->set_height (temp_atv->current_height());
3891 /* if you un-comment the following, the region colours will follow the track colours whilst dragging,
3892 personally, i think this can confuse things, but never mind.
3895 //const GdkColor& col (temp_atv->view->get_region_color());
3896 //rv->set_color (const_cast<GdkColor&>(col));
3903 if (drag_info.brushing) {
3904 mouse_brush_insert_region (rv, pending_region_position);
3906 rv->move (x_delta, y_delta);
3909 } /* foreach region */
3913 if (drag_info.first_move && drag_info.move_threshold_passed) {
3914 cursor_group->raise_to_top();
3915 drag_info.first_move = false;
3918 if (x_delta != 0 && !drag_info.brushing) {
3919 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3924 Editor::region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3926 bool nocommit = true;
3927 vector<RegionView*> copies;
3928 RouteTimeAxisView* source_tv;
3929 boost::shared_ptr<Diskstream> ds;
3930 boost::shared_ptr<Playlist> from_playlist;
3931 vector<RegionView*> new_selection;
3932 typedef set<boost::shared_ptr<Playlist> > PlaylistSet;
3933 PlaylistSet modified_playlists;
3934 PlaylistSet frozen_playlists;
3935 list <sigc::connection> modified_playlist_connections;
3936 pair<PlaylistSet::iterator,bool> insert_result, frozen_insert_result;
3937 nframes64_t drag_delta;
3938 bool changed_tracks, changed_position;
3940 /* first_move is set to false if the regionview has been moved in the
3944 if (drag_info.first_move) {
3951 if (Config->get_edit_mode() == Splice && !pre_drag_region_selection.empty()) {
3952 selection->set (pre_drag_region_selection);
3953 pre_drag_region_selection.clear ();
3956 if (drag_info.brushing) {
3957 /* all changes were made during motion event handlers */
3959 if (drag_info.copy) {
3960 for (list<RegionView*>::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
3961 copies.push_back (*i);
3970 /* reverse this here so that we have the correct logic to finalize
3974 if (Config->get_edit_mode() == Lock && !drag_info.copy) {
3975 drag_info.x_constrained = !drag_info.x_constrained;
3978 if (drag_info.copy) {
3979 if (drag_info.x_constrained) {
3980 op_string = _("fixed time region copy");
3982 op_string = _("region copy");
3985 if (drag_info.x_constrained) {
3986 op_string = _("fixed time region drag");
3988 op_string = _("region drag");
3992 begin_reversible_command (op_string);
3993 changed_position = (drag_info.last_frame_position != (nframes64_t) (clicked_regionview->region()->position()));
3994 changed_tracks = (trackview_by_y_position (drag_info.current_pointer_y) != &clicked_regionview->get_time_axis_view());
3996 drag_delta = clicked_regionview->region()->position() - drag_info.last_frame_position;
3998 track_canvas->update_now ();
4000 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ) {
4002 RegionView* rv = (*i);
4003 double ix1, ix2, iy1, iy2;
4004 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
4005 rv->get_canvas_group()->i2w (ix1, iy1);
4006 iy1 += vertical_adjustment.get_value() - canvas_timebars_vsize;
4008 TimeAxisView* dest_tv = trackview_by_y_position (iy1);
4009 AudioTimeAxisView* dest_atv = dynamic_cast<AudioTimeAxisView*>(dest_tv);
4012 if (rv->region()->locked()) {
4017 if (changed_position && !drag_info.x_constrained) {
4018 where = rv->region()->position() - drag_delta;
4020 where = rv->region()->position();
4023 boost::shared_ptr<Region> new_region;
4025 if (drag_info.copy) {
4026 /* we already made a copy */
4027 new_region = rv->region();
4029 /* undo the previous hide_dependent_views so that xfades don't
4030 disappear on copying regions
4033 //rv->get_time_axis_view().reveal_dependent_views (*rv);
4035 } else if (changed_tracks) {
4036 new_region = RegionFactory::create (rv->region());
4039 if (changed_tracks || drag_info.copy) {
4041 boost::shared_ptr<Playlist> to_playlist = dest_atv->playlist();
4043 latest_regionviews.clear ();
4045 sigc::connection c = dest_atv->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
4047 insert_result = modified_playlists.insert (to_playlist);
4048 if (insert_result.second) {
4049 session->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
4052 to_playlist->add_region (new_region, where);
4056 if (!latest_regionviews.empty()) {
4057 // XXX why just the first one ? we only expect one
4058 //dest_atv->reveal_dependent_views (*latest_regionviews.front());
4059 new_selection.push_back (latest_regionviews.front());
4064 motion on the same track. plonk the previously reparented region
4065 back to its original canvas group (its streamview).
4066 No need to do anything for copies as they are fake regions which will be deleted.
4069 RouteTimeAxisView* dest_rtv = dynamic_cast<RouteTimeAxisView*> (dest_atv);
4070 rv->get_canvas_group()->reparent (*dest_rtv->view()->canvas_item());
4071 rv->get_canvas_group()->property_y() = 0;
4073 /* just change the model */
4075 boost::shared_ptr<Playlist> playlist = dest_atv->playlist();
4077 insert_result = modified_playlists.insert (playlist);
4078 if (insert_result.second) {
4079 session->add_command (new MementoCommand<Playlist>(*playlist, &playlist->get_state(), 0));
4081 /* freeze to avoid lots of relayering in the case of a multi-region drag */
4082 frozen_insert_result = frozen_playlists.insert(playlist);
4083 if (frozen_insert_result.second) {
4087 rv->region()->set_position (where, (void*) this);
4090 if (changed_tracks && !drag_info.copy) {
4092 /* get the playlist where this drag started. we can't use rv->region()->playlist()
4093 because we may have copied the region and it has not been attached to a playlist.
4096 assert ((source_tv = dynamic_cast<RouteTimeAxisView*> (&rv->get_time_axis_view())));
4097 assert ((ds = source_tv->get_diskstream()));
4098 assert ((from_playlist = ds->playlist()));
4100 /* moved to a different audio track, without copying */
4102 /* the region that used to be in the old playlist is not
4103 moved to the new one - we use a copy of it. as a result,
4104 any existing editor for the region should no longer be
4108 rv->hide_region_editor();
4109 rv->fake_set_opaque (false);
4111 /* remove the region from the old playlist */
4113 insert_result = modified_playlists.insert (from_playlist);
4114 if (insert_result.second) {
4115 session->add_command (new MementoCommand<Playlist>(*from_playlist, &from_playlist->get_state(), 0));
4118 from_playlist->remove_region ((rv->region()));
4120 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
4121 was selected in all of them, then removing it from a playlist will have removed all
4122 trace of it from the selection (i.e. there were N regions selected, we removed 1,
4123 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
4124 corresponding regionview, and the selection is now empty).
4126 this could have invalidated any and all iterators into the region selection.
4128 the heuristic we use here is: if the region selection is empty, break out of the loop
4129 here. if the region selection is not empty, then restart the loop because we know that
4130 we must have removed at least the region(view) we've just been working on as well as any
4131 that we processed on previous iterations.
4133 EXCEPT .... if we are doing a copy drag, then the selection hasn't been modified and
4134 we can just iterate.
4137 if (selection->regions.empty()) {
4140 i = selection->regions.by_layer().begin();
4147 if (drag_info.copy) {
4148 copies.push_back (rv);
4152 if (new_selection.empty()) {
4153 if (drag_info.copy) {
4154 /* the region(view)s that are selected and being dragged around
4155 are copies and do not belong to any track. remove them
4156 from the selection right here.
4158 selection->clear_regions();
4161 /* this will clear any existing selection that would have been
4162 cleared in the other clause above
4164 selection->set (new_selection);
4167 for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
4173 for (set<boost::shared_ptr<Playlist> >::iterator p = modified_playlists.begin(); p != modified_playlists.end(); ++p) {
4174 session->add_command (new MementoCommand<Playlist>(*(*p), 0, &(*p)->get_state()));
4176 commit_reversible_command ();
4179 for (vector<RegionView*>::iterator x = copies.begin(); x != copies.end(); ++x) {
4186 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
4188 /* Either add to or set the set the region selection, unless
4189 this is an alignment click (control used)
4192 if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
4193 TimeAxisView* tv = &rv.get_time_axis_view();
4194 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*>(tv);
4196 if (atv && atv->is_audio_track()) {
4197 speed = atv->get_diskstream()->speed();
4200 nframes64_t where = get_preferred_edit_position();
4204 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
4206 align_region (rv.region(), SyncPoint, (nframes64_t) (where * speed));
4208 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
4210 align_region (rv.region(), End, (nframes64_t) (where * speed));
4214 align_region (rv.region(), Start, (nframes64_t) (where * speed));
4221 Editor::show_verbose_time_cursor (nframes64_t frame, double offset, double xpos, double ypos)
4227 nframes64_t frame_rate;
4236 if (Profile->get_sae() || Profile->get_small_screen()) {
4237 m = ARDOUR_UI::instance()->primary_clock.mode();
4239 m = ARDOUR_UI::instance()->secondary_clock.mode();
4243 case AudioClock::BBT:
4244 session->bbt_time (frame, bbt);
4245 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, bbt.bars, bbt.beats, bbt.ticks);
4248 case AudioClock::SMPTE:
4249 session->smpte_time (frame, smpte);
4250 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
4253 case AudioClock::MinSec:
4254 /* XXX this is copied from show_verbose_duration_cursor() */
4255 frame_rate = session->frame_rate();
4256 hours = frame / (frame_rate * 3600);
4257 frame = frame % (frame_rate * 3600);
4258 mins = frame / (frame_rate * 60);
4259 frame = frame % (frame_rate * 60);
4260 secs = (float) frame / (float) frame_rate;
4261 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
4265 snprintf (buf, sizeof(buf), "%" PRIi64, frame);
4269 if (xpos >= 0 && ypos >=0) {
4270 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
4273 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);
4275 show_verbose_canvas_cursor ();
4279 Editor::show_verbose_duration_cursor (nframes64_t start, nframes64_t end, double offset, double xpos, double ypos)
4286 nframes64_t distance, frame_rate;
4288 Meter meter_at_start(session->tempo_map().meter_at(start));
4296 if (Profile->get_sae() || Profile->get_small_screen()) {
4297 m = ARDOUR_UI::instance()->primary_clock.mode ();
4299 m = ARDOUR_UI::instance()->secondary_clock.mode ();
4303 case AudioClock::BBT:
4304 session->bbt_time (start, sbbt);
4305 session->bbt_time (end, ebbt);
4308 /* XXX this computation won't work well if the
4309 user makes a selection that spans any meter changes.
4312 ebbt.bars -= sbbt.bars;
4313 if (ebbt.beats >= sbbt.beats) {
4314 ebbt.beats -= sbbt.beats;
4317 ebbt.beats = int(meter_at_start.beats_per_bar()) + ebbt.beats - sbbt.beats;
4319 if (ebbt.ticks >= sbbt.ticks) {
4320 ebbt.ticks -= sbbt.ticks;
4323 ebbt.ticks = int(Meter::ticks_per_beat) + ebbt.ticks - sbbt.ticks;
4326 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, ebbt.bars, ebbt.beats, ebbt.ticks);
4329 case AudioClock::SMPTE:
4330 session->smpte_duration (end - start, smpte);
4331 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
4334 case AudioClock::MinSec:
4335 /* XXX this stuff should be elsewhere.. */
4336 distance = end - start;
4337 frame_rate = session->frame_rate();
4338 hours = distance / (frame_rate * 3600);
4339 distance = distance % (frame_rate * 3600);
4340 mins = distance / (frame_rate * 60);
4341 distance = distance % (frame_rate * 60);
4342 secs = (float) distance / (float) frame_rate;
4343 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
4347 snprintf (buf, sizeof(buf), "%" PRIi64, end - start);
4351 if (xpos >= 0 && ypos >=0) {
4352 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
4355 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
4358 show_verbose_canvas_cursor ();
4362 Editor::collect_new_region_view (RegionView* rv)
4364 latest_regionviews.push_back (rv);
4368 Editor::start_selection_grab (ArdourCanvas::Item* item, GdkEvent* event)
4370 if (clicked_regionview == 0) {
4374 /* lets try to create new Region for the selection */
4376 vector<boost::shared_ptr<AudioRegion> > new_regions;
4377 create_region_from_selection (new_regions);
4379 if (new_regions.empty()) {
4383 /* XXX fix me one day to use all new regions */
4385 boost::shared_ptr<Region> region (new_regions.front());
4387 /* add it to the current stream/playlist.
4389 tricky: the streamview for the track will add a new regionview. we will
4390 catch the signal it sends when it creates the regionview to
4391 set the regionview we want to then drag.
4394 latest_regionviews.clear();
4395 sigc::connection c = clicked_audio_trackview->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
4397 /* A selection grab currently creates two undo/redo operations, one for
4398 creating the new region and another for moving it.
4401 begin_reversible_command (_("selection grab"));
4403 boost::shared_ptr<Playlist> playlist = clicked_trackview->playlist();
4405 XMLNode *before = &(playlist->get_state());
4406 clicked_trackview->playlist()->add_region (region, selection->time[clicked_selection].start);
4407 XMLNode *after = &(playlist->get_state());
4408 session->add_command(new MementoCommand<Playlist>(*playlist, before, after));
4410 commit_reversible_command ();
4414 if (latest_regionviews.empty()) {
4415 /* something went wrong */
4419 /* we need to deselect all other regionviews, and select this one
4420 i'm ignoring undo stuff, because the region creation will take care of it
4422 selection->set (latest_regionviews);
4424 drag_info.item = latest_regionviews.front()->get_canvas_group();
4425 drag_info.data = latest_regionviews.front();
4426 drag_info.motion_callback = &Editor::region_drag_motion_callback;
4427 drag_info.finished_callback = &Editor::region_drag_finished_callback;
4431 drag_info.source_trackview = clicked_trackview;
4432 drag_info.dest_trackview = drag_info.source_trackview;
4433 drag_info.last_frame_position = latest_regionviews.front()->region()->position();
4434 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
4436 show_verbose_time_cursor (drag_info.last_frame_position, 10);
4440 Editor::cancel_selection ()
4442 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4443 (*i)->hide_selection ();
4445 selection->clear ();
4446 clicked_selection = 0;
4450 Editor::start_selection_op (ArdourCanvas::Item* item, GdkEvent* event, SelectionOp op)
4452 nframes64_t start = 0;
4453 nframes64_t end = 0;
4459 drag_info.item = item;
4460 drag_info.motion_callback = &Editor::drag_selection;
4461 drag_info.finished_callback = &Editor::end_selection_op;
4466 case CreateSelection:
4467 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
4468 drag_info.copy = true;
4470 drag_info.copy = false;
4472 start_grab (event, selector_cursor);
4475 case SelectionStartTrim:
4476 if (clicked_trackview) {
4477 clicked_trackview->order_selection_trims (item, true);
4479 start_grab (event, trimmer_cursor);
4480 start = selection->time[clicked_selection].start;
4481 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
4484 case SelectionEndTrim:
4485 if (clicked_trackview) {
4486 clicked_trackview->order_selection_trims (item, false);
4488 start_grab (event, trimmer_cursor);
4489 end = selection->time[clicked_selection].end;
4490 drag_info.pointer_frame_offset = drag_info.grab_frame - end;
4494 start = selection->time[clicked_selection].start;
4496 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
4500 if (selection_op == SelectionMove) {
4501 show_verbose_time_cursor(start, 10);
4503 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4508 Editor::drag_selection (ArdourCanvas::Item* item, GdkEvent* event)
4510 nframes64_t start = 0;
4511 nframes64_t end = 0;
4513 nframes64_t pending_position;
4515 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
4516 pending_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
4518 pending_position = 0;
4521 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4522 snap_to (pending_position);
4525 /* only alter selection if the current frame is
4526 different from the last frame position (adjusted)
4529 if (pending_position == drag_info.last_pointer_frame) return;
4531 switch (selection_op) {
4532 case CreateSelection:
4534 if (drag_info.first_move) {
4535 snap_to (drag_info.grab_frame);
4538 if (pending_position < drag_info.grab_frame) {
4539 start = pending_position;
4540 end = drag_info.grab_frame;
4542 end = pending_position;
4543 start = drag_info.grab_frame;
4546 /* first drag: Either add to the selection
4547 or create a new selection->
4550 if (drag_info.first_move) {
4552 begin_reversible_command (_("range selection"));
4554 if (drag_info.copy) {
4555 /* adding to the selection */
4556 clicked_selection = selection->add (start, end);
4557 drag_info.copy = false;
4559 /* new selection-> */
4560 clicked_selection = selection->set (clicked_trackview, start, end);
4565 case SelectionStartTrim:
4567 if (drag_info.first_move) {
4568 begin_reversible_command (_("trim selection start"));
4571 start = selection->time[clicked_selection].start;
4572 end = selection->time[clicked_selection].end;
4574 if (pending_position > end) {
4577 start = pending_position;
4581 case SelectionEndTrim:
4583 if (drag_info.first_move) {
4584 begin_reversible_command (_("trim selection end"));
4587 start = selection->time[clicked_selection].start;
4588 end = selection->time[clicked_selection].end;
4590 if (pending_position < start) {
4593 end = pending_position;
4600 if (drag_info.first_move) {
4601 begin_reversible_command (_("move selection"));
4604 start = selection->time[clicked_selection].start;
4605 end = selection->time[clicked_selection].end;
4607 length = end - start;
4609 start = pending_position;
4612 end = start + length;
4617 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
4618 start_canvas_autoscroll (1, 0);
4622 selection->replace (clicked_selection, start, end);
4625 drag_info.last_pointer_frame = pending_position;
4626 drag_info.first_move = false;
4628 if (selection_op == SelectionMove) {
4629 show_verbose_time_cursor(start, 10);
4631 show_verbose_time_cursor(pending_position, 10);
4636 Editor::end_selection_op (ArdourCanvas::Item* item, GdkEvent* event)
4638 if (!drag_info.first_move) {
4639 drag_selection (item, event);
4640 /* XXX this is not object-oriented programming at all. ick */
4641 if (selection->time.consolidate()) {
4642 selection->TimeChanged ();
4644 commit_reversible_command ();
4646 /* just a click, no pointer movement.*/
4648 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
4650 selection->clear_time();
4655 /* XXX what happens if its a music selection? */
4656 session->set_audio_range (selection->time);
4657 stop_canvas_autoscroll ();
4661 Editor::start_trim (ArdourCanvas::Item* item, GdkEvent* event)
4664 TimeAxisView* tvp = clicked_trackview;
4665 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
4667 if (tv && tv->is_audio_track()) {
4668 speed = tv->get_diskstream()->speed();
4671 nframes64_t region_start = (nframes64_t) (clicked_regionview->region()->position() / speed);
4672 nframes64_t region_end = (nframes64_t) (clicked_regionview->region()->last_frame() / speed);
4673 nframes64_t region_length = (nframes64_t) (clicked_regionview->region()->length() / speed);
4675 //drag_info.item = clicked_regionview->get_name_highlight();
4676 drag_info.item = item;
4677 drag_info.motion_callback = &Editor::trim_motion_callback;
4678 drag_info.finished_callback = &Editor::trim_finished_callback;
4680 start_grab (event, trimmer_cursor);
4682 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
4683 trim_op = ContentsTrim;
4685 /* These will get overridden for a point trim.*/
4686 if (drag_info.current_pointer_frame < (region_start + region_length/2)) {
4687 /* closer to start */
4688 trim_op = StartTrim;
4689 } else if (drag_info.current_pointer_frame > (region_end - region_length/2)) {
4697 show_verbose_time_cursor(region_start, 10);
4700 show_verbose_time_cursor(region_end, 10);
4703 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4709 Editor::trim_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
4711 RegionView* rv = clicked_regionview;
4712 nframes64_t frame_delta = 0;
4713 bool left_direction;
4714 bool obey_snap = !Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier());
4716 /* snap modifier works differently here..
4717 its' current state has to be passed to the
4718 various trim functions in order to work properly
4722 TimeAxisView* tvp = clicked_trackview;
4723 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4724 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
4726 if (tv && tv->is_audio_track()) {
4727 speed = tv->get_diskstream()->speed();
4730 if (drag_info.last_pointer_frame > drag_info.current_pointer_frame) {
4731 left_direction = true;
4733 left_direction = false;
4737 snap_to (drag_info.current_pointer_frame);
4740 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
4744 if (drag_info.first_move) {
4750 trim_type = "Region start trim";
4753 trim_type = "Region end trim";
4756 trim_type = "Region content trim";
4760 begin_reversible_command (trim_type);
4762 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4763 (*i)->fake_set_opaque(false);
4764 (*i)->region()->freeze ();
4766 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
4768 arv->temporarily_hide_envelope ();
4770 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4771 insert_result = motion_frozen_playlists.insert (pl);
4772 if (insert_result.second) {
4773 session->add_command(new MementoCommand<Playlist>(*pl, &pl->get_state(), 0));
4778 if (left_direction) {
4779 frame_delta = (drag_info.last_pointer_frame - drag_info.current_pointer_frame);
4781 frame_delta = (drag_info.current_pointer_frame - drag_info.last_pointer_frame);
4786 if ((left_direction == false) && (drag_info.current_pointer_frame <= rv->region()->first_frame()/speed)) {
4789 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4790 single_start_trim (**i, frame_delta, left_direction, obey_snap);
4796 if ((left_direction == true) && (drag_info.current_pointer_frame > (nframes64_t) (rv->region()->last_frame()/speed))) {
4799 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4800 single_end_trim (**i, frame_delta, left_direction, obey_snap);
4807 bool swap_direction = false;
4809 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
4810 swap_direction = true;
4813 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4814 i != selection->regions.by_layer().end(); ++i)
4816 single_contents_trim (**i, frame_delta, left_direction, swap_direction, obey_snap);
4824 show_verbose_time_cursor((nframes64_t) (rv->region()->position()/speed), 10);
4827 show_verbose_time_cursor((nframes64_t) (rv->region()->last_frame()/speed), 10);
4830 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4834 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4835 drag_info.first_move = false;
4839 Editor::single_contents_trim (RegionView& rv, nframes64_t frame_delta, bool left_direction, bool swap_direction, bool obey_snap)
4841 boost::shared_ptr<Region> region (rv.region());
4843 if (region->locked()) {
4847 nframes64_t new_bound;
4850 TimeAxisView* tvp = clicked_trackview;
4851 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4853 if (tv && tv->is_audio_track()) {
4854 speed = tv->get_diskstream()->speed();
4857 if (left_direction) {
4858 if (swap_direction) {
4859 new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
4861 new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
4864 if (swap_direction) {
4865 new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
4867 new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
4872 snap_to (new_bound);
4874 region->trim_start ((nframes64_t) (new_bound * speed), this);
4875 rv.region_changed (StartChanged);
4879 Editor::single_start_trim (RegionView& rv, nframes64_t frame_delta, bool left_direction, bool obey_snap)
4881 boost::shared_ptr<Region> region (rv.region());
4883 if (region->locked()) {
4887 nframes64_t new_bound;
4890 TimeAxisView* tvp = clicked_trackview;
4891 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
4893 if (tv && tv->is_audio_track()) {
4894 speed = tv->get_diskstream()->speed();
4897 if (left_direction) {
4898 new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
4900 new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
4904 snap_to (new_bound, (left_direction ? 0 : 1));
4907 region->trim_front ((nframes64_t) (new_bound * speed), this);
4909 rv.region_changed (Change (LengthChanged|PositionChanged|StartChanged));
4913 Editor::single_end_trim (RegionView& rv, nframes64_t frame_delta, bool left_direction, bool obey_snap)
4915 boost::shared_ptr<Region> region (rv.region());
4917 if (region->locked()) {
4921 nframes64_t new_bound;
4924 TimeAxisView* tvp = clicked_trackview;
4925 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
4927 if (tv && tv->is_audio_track()) {
4928 speed = tv->get_diskstream()->speed();
4931 if (left_direction) {
4932 new_bound = (nframes64_t) ((region->last_frame() + 1)/speed) - frame_delta;
4934 new_bound = (nframes64_t) ((region->last_frame() + 1)/speed) + frame_delta;
4938 snap_to (new_bound);
4940 region->trim_end ((nframes64_t) (new_bound * speed), this);
4941 rv.region_changed (LengthChanged);
4945 Editor::trim_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
4947 if (!drag_info.first_move) {
4948 trim_motion_callback (item, event);
4950 if (!selection->selected (clicked_regionview)) {
4951 thaw_region_after_trim (*clicked_regionview);
4954 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4955 i != selection->regions.by_layer().end(); ++i)
4957 thaw_region_after_trim (**i);
4958 (*i)->fake_set_opaque (true);
4962 for (set<boost::shared_ptr<Playlist> >::iterator p = motion_frozen_playlists.begin(); p != motion_frozen_playlists.end(); ++p) {
4964 session->add_command (new MementoCommand<Playlist>(*(*p).get(), 0, &(*p)->get_state()));
4967 motion_frozen_playlists.clear ();
4969 commit_reversible_command();
4971 /* no mouse movement */
4977 Editor::point_trim (GdkEvent* event)
4979 RegionView* rv = clicked_regionview;
4980 nframes64_t new_bound = drag_info.current_pointer_frame;
4982 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4983 snap_to (new_bound);
4986 /* Choose action dependant on which button was pressed */
4987 switch (event->button.button) {
4989 trim_op = StartTrim;
4990 begin_reversible_command (_("Start point trim"));
4992 if (selection->selected (rv)) {
4994 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4995 i != selection->regions.by_layer().end(); ++i)
4997 if (!(*i)->region()->locked()) {
4998 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4999 XMLNode &before = pl->get_state();
5000 (*i)->region()->trim_front (new_bound, this);
5001 XMLNode &after = pl->get_state();
5002 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
5008 if (!rv->region()->locked()) {
5009 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
5010 XMLNode &before = pl->get_state();
5011 rv->region()->trim_front (new_bound, this);
5012 XMLNode &after = pl->get_state();
5013 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
5017 commit_reversible_command();
5022 begin_reversible_command (_("End point trim"));
5024 if (selection->selected (rv)) {
5026 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
5028 if (!(*i)->region()->locked()) {
5029 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
5030 XMLNode &before = pl->get_state();
5031 (*i)->region()->trim_end (new_bound, this);
5032 XMLNode &after = pl->get_state();
5033 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
5039 if (!rv->region()->locked()) {
5040 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
5041 XMLNode &before = pl->get_state();
5042 rv->region()->trim_end (new_bound, this);
5043 XMLNode &after = pl->get_state();
5044 session->add_command (new MementoCommand<Playlist>(*pl.get(), &before, &after));
5048 commit_reversible_command();
5057 Editor::thaw_region_after_trim (RegionView& rv)
5059 boost::shared_ptr<Region> region (rv.region());
5061 if (region->locked()) {
5065 region->thaw (_("trimmed region"));
5066 XMLNode &after = region->playlist()->get_state();
5067 session->add_command (new MementoCommand<Playlist>(*(region->playlist()), 0, &after));
5069 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(&rv);
5071 arv->unhide_envelope ();
5075 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* event)
5080 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
5081 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
5085 Location* location = find_location_from_marker (marker, is_start);
5086 location->set_hidden (true, this);
5091 Editor::start_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event, RangeMarkerOp op)
5097 drag_info.item = item;
5098 drag_info.motion_callback = &Editor::drag_range_markerbar_op;
5099 drag_info.finished_callback = &Editor::end_range_markerbar_op;
5101 range_marker_op = op;
5103 if (!temp_location) {
5104 temp_location = new Location;
5108 case CreateRangeMarker:
5109 case CreateTransportMarker:
5110 case CreateCDMarker:
5112 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
5113 drag_info.copy = true;
5115 drag_info.copy = false;
5117 start_grab (event, selector_cursor);
5121 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
5126 Editor::drag_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
5128 nframes64_t start = 0;
5129 nframes64_t end = 0;
5130 ArdourCanvas::SimpleRect *crect;
5132 switch (range_marker_op) {
5133 case CreateRangeMarker:
5134 crect = range_bar_drag_rect;
5136 case CreateTransportMarker:
5137 crect = transport_bar_drag_rect;
5139 case CreateCDMarker:
5140 crect = cd_marker_bar_drag_rect;
5143 cerr << "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()" << endl;
5148 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
5149 snap_to (drag_info.current_pointer_frame);
5152 /* only alter selection if the current frame is
5153 different from the last frame position.
5156 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
5158 switch (range_marker_op) {
5159 case CreateRangeMarker:
5160 case CreateTransportMarker:
5161 case CreateCDMarker:
5162 if (drag_info.first_move) {
5163 snap_to (drag_info.grab_frame);
5166 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
5167 start = drag_info.current_pointer_frame;
5168 end = drag_info.grab_frame;
5170 end = drag_info.current_pointer_frame;
5171 start = drag_info.grab_frame;
5174 /* first drag: Either add to the selection
5175 or create a new selection.
5178 if (drag_info.first_move) {
5180 temp_location->set (start, end);
5184 update_marker_drag_item (temp_location);
5185 range_marker_drag_rect->show();
5186 //range_marker_drag_rect->raise_to_top();
5192 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
5193 start_canvas_autoscroll (1, 0);
5197 temp_location->set (start, end);
5199 double x1 = frame_to_pixel (start);
5200 double x2 = frame_to_pixel (end);
5201 crect->property_x1() = x1;
5202 crect->property_x2() = x2;
5204 update_marker_drag_item (temp_location);
5207 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
5208 drag_info.first_move = false;
5210 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
5215 Editor::end_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
5217 Location * newloc = 0;
5221 if (!drag_info.first_move) {
5222 drag_range_markerbar_op (item, event);
5224 switch (range_marker_op) {
5225 case CreateRangeMarker:
5226 case CreateCDMarker:
5228 begin_reversible_command (_("new range marker"));
5229 XMLNode &before = session->locations()->get_state();
5230 session->locations()->next_available_name(rangename,"unnamed");
5231 if (range_marker_op == CreateCDMarker) {
5232 flags = Location::IsRangeMarker|Location::IsCDMarker;
5233 cd_marker_bar_drag_rect->hide();
5236 flags = Location::IsRangeMarker;
5237 range_bar_drag_rect->hide();
5239 newloc = new Location(temp_location->start(), temp_location->end(), rangename, (Location::Flags) flags);
5240 session->locations()->add (newloc, true);
5241 XMLNode &after = session->locations()->get_state();
5242 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
5243 commit_reversible_command ();
5245 range_marker_drag_rect->hide();
5249 case CreateTransportMarker:
5250 // popup menu to pick loop or punch
5251 new_transport_marker_context_menu (&event->button, item);
5256 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
5258 if (Keyboard::no_modifier_keys_pressed (&event->button) && range_marker_op != CreateCDMarker) {
5263 start = session->locations()->first_mark_before (drag_info.grab_frame);
5264 end = session->locations()->first_mark_after (drag_info.grab_frame);
5266 if (end == max_frames) {
5267 end = session->current_end_frame ();
5271 start = session->current_start_frame ();
5274 switch (mouse_mode) {
5276 /* find the two markers on either side and then make the selection from it */
5277 select_all_within (start, end, 0.0f, FLT_MAX, track_views, Selection::Set);
5281 /* find the two markers on either side of the click and make the range out of it */
5282 selection->set (0, start, end);
5291 stop_canvas_autoscroll ();
5297 Editor::start_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
5299 drag_info.item = item;
5300 drag_info.motion_callback = &Editor::drag_mouse_zoom;
5301 drag_info.finished_callback = &Editor::end_mouse_zoom;
5303 start_grab (event, zoom_cursor);
5305 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5309 Editor::drag_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
5314 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
5315 snap_to (drag_info.current_pointer_frame);
5317 if (drag_info.first_move) {
5318 snap_to (drag_info.grab_frame);
5322 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
5324 /* base start and end on initial click position */
5325 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
5326 start = drag_info.current_pointer_frame;
5327 end = drag_info.grab_frame;
5329 end = drag_info.current_pointer_frame;
5330 start = drag_info.grab_frame;
5335 if (drag_info.first_move) {
5337 zoom_rect->raise_to_top();
5340 reposition_zoom_rect(start, end);
5342 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
5343 drag_info.first_move = false;
5345 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5350 Editor::end_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
5352 if (!drag_info.first_move) {
5353 drag_mouse_zoom (item, event);
5355 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
5356 temporal_zoom_by_frame (drag_info.grab_frame, drag_info.last_pointer_frame, "mouse zoom");
5358 temporal_zoom_by_frame (drag_info.last_pointer_frame, drag_info.grab_frame, "mouse zoom");
5361 temporal_zoom_to_frame (false, drag_info.grab_frame);
5363 temporal_zoom_step (false);
5364 center_screen (drag_info.grab_frame);
5372 Editor::reposition_zoom_rect (nframes64_t start, nframes64_t end)
5374 double x1 = frame_to_pixel (start);
5375 double x2 = frame_to_pixel (end);
5376 double y2 = full_canvas_height - 1.0;
5378 zoom_rect->property_x1() = x1;
5379 zoom_rect->property_y1() = 1.0;
5380 zoom_rect->property_x2() = x2;
5381 zoom_rect->property_y2() = y2;
5385 Editor::start_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
5387 drag_info.item = item;
5388 drag_info.motion_callback = &Editor::drag_rubberband_select;
5389 drag_info.finished_callback = &Editor::end_rubberband_select;
5391 start_grab (event, cross_hair_cursor);
5393 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5397 Editor::drag_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
5404 /* use a bigger drag threshold than the default */
5406 if (abs ((int) (drag_info.current_pointer_frame - drag_info.grab_frame)) < 8) {
5410 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier()) && Config->get_rubberbanding_snaps_to_grid()) {
5411 if (drag_info.first_move) {
5412 snap_to (drag_info.grab_frame);
5414 snap_to (drag_info.current_pointer_frame);
5417 /* base start and end on initial click position */
5419 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
5420 start = drag_info.current_pointer_frame;
5421 end = drag_info.grab_frame;
5423 end = drag_info.current_pointer_frame;
5424 start = drag_info.grab_frame;
5427 if (drag_info.current_pointer_y < drag_info.grab_y) {
5428 y1 = drag_info.current_pointer_y;
5429 y2 = drag_info.grab_y;
5431 y2 = drag_info.current_pointer_y;
5432 y1 = drag_info.grab_y;
5436 if (start != end || y1 != y2) {
5438 double x1 = frame_to_pixel (start);
5439 double x2 = frame_to_pixel (end);
5441 rubberband_rect->property_x1() = x1;
5442 rubberband_rect->property_y1() = y1;
5443 rubberband_rect->property_x2() = x2;
5444 rubberband_rect->property_y2() = y2;
5446 rubberband_rect->show();
5447 rubberband_rect->raise_to_top();
5449 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
5450 drag_info.first_move = false;
5452 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5457 Editor::end_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
5459 if (!drag_info.first_move) {
5461 drag_rubberband_select (item, event);
5464 if (drag_info.current_pointer_y < drag_info.grab_y) {
5465 y1 = drag_info.current_pointer_y;
5466 y2 = drag_info.grab_y;
5468 y2 = drag_info.current_pointer_y;
5469 y1 = drag_info.grab_y;
5473 Selection::Operation op = Keyboard::selection_type (event->button.state);
5476 begin_reversible_command (_("rubberband selection"));
5478 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
5479 commit = select_all_within (drag_info.grab_frame, drag_info.last_pointer_frame, y1, y2, track_views, op);
5481 commit = select_all_within (drag_info.last_pointer_frame, drag_info.grab_frame, y1, y2, track_views, op);
5485 commit_reversible_command ();
5489 if (!getenv("ARDOUR_SAE")) {
5490 selection->clear_tracks();
5492 selection->clear_regions();
5493 selection->clear_points ();
5494 selection->clear_lines ();
5497 rubberband_rect->hide();
5502 Editor::mouse_rename_region (ArdourCanvas::Item* item, GdkEvent* event)
5504 using namespace Gtkmm2ext;
5506 ArdourPrompter prompter (false);
5508 prompter.set_prompt (_("Name for region:"));
5509 prompter.set_initial_text (clicked_regionview->region()->name());
5510 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
5511 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
5512 prompter.show_all ();
5513 switch (prompter.run ()) {
5514 case Gtk::RESPONSE_ACCEPT:
5516 prompter.get_result(str);
5518 clicked_regionview->region()->set_name (str);
5526 Editor::start_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
5528 drag_info.item = item;
5529 drag_info.motion_callback = &Editor::time_fx_motion;
5530 drag_info.finished_callback = &Editor::end_time_fx;
5534 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5538 Editor::time_fx_motion (ArdourCanvas::Item *item, GdkEvent* event)
5540 RegionView* rv = clicked_regionview;
5542 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
5543 snap_to (drag_info.current_pointer_frame);
5546 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
5550 if (drag_info.current_pointer_frame > rv->region()->position()) {
5551 rv->get_time_axis_view().show_timestretch (rv->region()->position(), drag_info.current_pointer_frame);
5554 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
5555 drag_info.first_move = false;
5557 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5561 Editor::end_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
5563 clicked_regionview->get_time_axis_view().hide_timestretch ();
5565 if (drag_info.first_move) {
5569 if (drag_info.last_pointer_frame < clicked_regionview->region()->position()) {
5570 /* backwards drag of the left edge - not usable */
5574 nframes64_t newlen = drag_info.last_pointer_frame - clicked_regionview->region()->position();
5575 #ifdef USE_RUBBERBAND
5576 float percentage = (float) ((double) newlen / (double) clicked_regionview->region()->length());
5578 float percentage = (float) ((double) newlen - (double) clicked_regionview->region()->length()) / ((double) newlen) * 100.0f;
5581 begin_reversible_command (_("timestretch"));
5583 // XXX how do timeFX on multiple regions ?
5586 rs.add (clicked_regionview);
5588 if (time_stretch (rs, percentage) == 0) {
5589 session->commit_reversible_command ();
5594 Editor::mouse_brush_insert_region (RegionView* rv, nframes64_t pos)
5596 /* no brushing without a useful snap setting */
5599 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
5602 switch (snap_mode) {
5604 return; /* can't work because it allows region to be placed anywhere */
5609 switch (snap_type) {
5617 /* don't brush a copy over the original */
5619 if (pos == rv->region()->position()) {
5623 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*>(&arv->get_time_axis_view());
5625 if (atv == 0 || !atv->is_audio_track()) {
5629 boost::shared_ptr<Playlist> playlist = atv->playlist();
5630 double speed = atv->get_diskstream()->speed();
5632 XMLNode &before = playlist->get_state();
5633 playlist->add_region (boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (arv->audio_region())), (nframes64_t) (pos * speed));
5634 XMLNode &after = playlist->get_state();
5635 session->add_command(new MementoCommand<Playlist>(*playlist.get(), &before, &after));
5637 // playlist is frozen, so we have to update manually
5639 playlist->Modified(); /* EMIT SIGNAL */
5643 Editor::track_height_step_timeout ()
5645 if (get_microseconds() - last_track_height_step_timestamp < 250000) {
5646 current_stepping_trackview = 0;