2 Copyright (C) 2000-2001 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
29 #include <pbd/error.h>
30 #include <gtkmm2ext/utils.h>
31 #include <pbd/memento_command.h>
33 #include "ardour_ui.h"
35 #include "time_axis_view.h"
36 #include "audio_time_axis.h"
37 #include "audio_region_view.h"
39 #include "streamview.h"
40 #include "region_gain_line.h"
41 #include "automation_time_axis.h"
44 #include "selection.h"
47 #include "rgb_macros.h"
49 #include <ardour/types.h>
50 #include <ardour/profile.h>
51 #include <ardour/route.h>
52 #include <ardour/audio_track.h>
53 #include <ardour/audio_diskstream.h>
54 #include <ardour/playlist.h>
55 #include <ardour/audioplaylist.h>
56 #include <ardour/audioregion.h>
57 #include <ardour/dB.h>
58 #include <ardour/utils.h>
59 #include <ardour/region_factory.h>
66 using namespace ARDOUR;
70 using namespace Editing;
73 Editor::mouse_frame (nframes64_t& where, bool& in_track_canvas) const
77 Gdk::ModifierType mask;
78 Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->track_canvas->get_window();
79 Glib::RefPtr<const Gdk::Window> pointer_window;
85 pointer_window = canvas_window->get_pointer (x, y, mask);
87 if (pointer_window == track_canvas->get_bin_window()) {
89 track_canvas->window_to_world (x, y, wx, wy);
90 in_track_canvas = true;
93 in_track_canvas = false;
95 if (pointer_window == time_canvas->get_bin_window()) {
96 time_canvas->window_to_world (x, y, wx, wy);
102 wx += horizontal_adjustment.get_value();
103 wy += vertical_adjustment.get_value();
106 event.type = GDK_BUTTON_RELEASE;
110 where = event_frame (&event, 0, 0);
115 Editor::event_frame (GdkEvent* event, double* pcx, double* pcy) const
129 switch (event->type) {
130 case GDK_BUTTON_RELEASE:
131 case GDK_BUTTON_PRESS:
132 case GDK_2BUTTON_PRESS:
133 case GDK_3BUTTON_PRESS:
134 track_canvas->w2c(event->button.x, event->button.y, *pcx, *pcy);
136 case GDK_MOTION_NOTIFY:
137 track_canvas->w2c(event->motion.x, event->motion.y, *pcx, *pcy);
139 case GDK_ENTER_NOTIFY:
140 case GDK_LEAVE_NOTIFY:
141 track_canvas->w2c(event->crossing.x, event->crossing.y, *pcx, *pcy);
144 case GDK_KEY_RELEASE:
145 // track_canvas->w2c(event->key.x, event->key.y, *pcx, *pcy);
148 warning << string_compose (_("Editor::event_frame() used on unhandled event type %1"), event->type) << endmsg;
152 /* note that pixel_to_frame() never returns less than zero, so even if the pixel
153 position is negative (as can be the case with motion events in particular),
154 the frame location is always positive.
157 return pixel_to_frame (*pcx);
161 Editor::mouse_mode_toggled (MouseMode m)
163 if (ignore_mouse_mode_toggle) {
169 if (mouse_select_button.get_active()) {
175 if (mouse_move_button.get_active()) {
181 if (mouse_gain_button.get_active()) {
187 if (mouse_zoom_button.get_active()) {
193 if (mouse_timefx_button.get_active()) {
199 if (mouse_audition_button.get_active()) {
210 Editor::which_grabber_cursor ()
212 switch (_edit_point) {
214 return grabber_edit_point_cursor;
217 return grabber_cursor;
223 Editor::set_canvas_cursor ()
225 switch (mouse_mode) {
227 current_canvas_cursor = selector_cursor;
231 current_canvas_cursor = which_grabber_cursor();
235 current_canvas_cursor = cross_hair_cursor;
239 current_canvas_cursor = zoom_cursor;
243 current_canvas_cursor = time_fx_cursor; // just use playhead
247 current_canvas_cursor = speaker_cursor;
252 track_canvas->get_window()->set_cursor(*current_canvas_cursor);
257 Editor::set_mouse_mode (MouseMode m, bool force)
259 if (drag_info.item) {
263 if (!force && m == mouse_mode) {
271 if (mouse_mode != MouseRange) {
273 /* in all modes except range, hide the range selection,
274 show the object (region) selection.
277 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
278 (*i)->set_should_show_selection (true);
280 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
281 (*i)->hide_selection ();
287 in range mode,show the range selection.
290 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
291 if ((*i)->get_selected()) {
292 (*i)->show_selection (selection->time);
297 /* XXX the hack of unsetting all other buttons should go
298 away once GTK2 allows us to use regular radio buttons drawn like
299 normal buttons, rather than my silly GroupedButton hack.
302 ignore_mouse_mode_toggle = true;
304 switch (mouse_mode) {
306 mouse_select_button.set_active (true);
310 mouse_move_button.set_active (true);
314 mouse_gain_button.set_active (true);
318 mouse_zoom_button.set_active (true);
322 mouse_timefx_button.set_active (true);
326 mouse_audition_button.set_active (true);
330 ignore_mouse_mode_toggle = false;
332 set_canvas_cursor ();
336 Editor::step_mouse_mode (bool next)
338 switch (current_mouse_mode()) {
340 if (next) set_mouse_mode (MouseRange);
341 else set_mouse_mode (MouseTimeFX);
345 if (next) set_mouse_mode (MouseZoom);
346 else set_mouse_mode (MouseObject);
350 if (next) set_mouse_mode (MouseGain);
351 else set_mouse_mode (MouseRange);
355 if (next) set_mouse_mode (MouseTimeFX);
356 else set_mouse_mode (MouseZoom);
360 if (next) set_mouse_mode (MouseAudition);
361 else set_mouse_mode (MouseGain);
365 if (next) set_mouse_mode (MouseObject);
366 else set_mouse_mode (MouseTimeFX);
372 Editor::button_selection (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
374 /* in object/audition/timefx mode, any button press sets
375 the selection if the object can be selected. this is a
376 bit of hack, because we want to avoid this if the
377 mouse operation is a region alignment.
379 note: not dbl-click or triple-click
382 if (((mouse_mode != MouseObject) &&
383 (mouse_mode != MouseAudition || item_type != RegionItem) &&
384 (mouse_mode != MouseTimeFX || item_type != RegionItem) &&
385 (mouse_mode != MouseRange)) ||
387 ((event->type != GDK_BUTTON_PRESS && event->type != GDK_BUTTON_RELEASE) || event->button.button > 3)) {
392 if (event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) {
394 if ((event->button.state & Keyboard::RelevantModifierKeyMask) && event->button.button != 1) {
396 /* almost no selection action on modified button-2 or button-3 events */
398 if (item_type != RegionItem && event->button.button != 2) {
404 Selection::Operation op = Keyboard::selection_type (event->button.state);
405 bool press = (event->type == GDK_BUTTON_PRESS);
407 // begin_reversible_command (_("select on click"));
411 if (mouse_mode != MouseRange) {
412 set_selected_regionview_from_click (press, op, true);
413 } else if (event->type == GDK_BUTTON_PRESS) {
414 set_selected_track_as_side_effect ();
418 case RegionViewNameHighlight:
420 if (mouse_mode != MouseRange) {
421 set_selected_regionview_from_click (press, op, true);
422 } else if (event->type == GDK_BUTTON_PRESS) {
423 set_selected_track_as_side_effect ();
427 case FadeInHandleItem:
429 case FadeOutHandleItem:
431 if (mouse_mode != MouseRange) {
432 set_selected_regionview_from_click (press, op, true);
433 } else if (event->type == GDK_BUTTON_PRESS) {
434 set_selected_track_as_side_effect ();
438 case GainAutomationControlPointItem:
439 case PanAutomationControlPointItem:
440 case RedirectAutomationControlPointItem:
441 set_selected_track_as_side_effect ();
442 if (mouse_mode != MouseRange) {
443 set_selected_control_point_from_click (op, false);
448 /* for context click or range selection, select track */
449 if (event->button.button == 3) {
450 set_selected_track_as_side_effect ();
451 } else if (event->type == GDK_BUTTON_PRESS && mouse_mode == MouseRange) {
452 set_selected_track_as_side_effect ();
456 case AutomationTrackItem:
457 set_selected_track_as_side_effect (true);
465 const static double ZERO_GAIN_FRACTION = gain_to_slider_position(dB_to_coefficient(0.0));
468 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
470 Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->track_canvas->get_window();
473 Glib::RefPtr<const Gdk::Window> pointer_window;
476 Gdk::ModifierType mask;
478 pointer_window = canvas_window->get_pointer (x, y, mask);
480 if (pointer_window == track_canvas->get_bin_window()) {
482 track_canvas->window_to_world (x, y, wx, wy);
483 allow_vertical_scroll = true;
485 allow_vertical_scroll = false;
489 track_canvas->grab_focus();
491 if (session && session->actively_recording()) {
495 button_selection (item, event, item_type);
497 if (drag_info.item == 0 &&
498 (Keyboard::is_delete_event (&event->button) ||
499 Keyboard::is_context_menu_event (&event->button) ||
500 Keyboard::is_edit_event (&event->button))) {
502 /* handled by button release */
506 switch (event->button.button) {
509 if (event->type == GDK_BUTTON_PRESS) {
511 if (drag_info.item) {
512 drag_info.item->ungrab (event->button.time);
515 /* single mouse clicks on any of these item types operate
516 independent of mouse mode, mostly because they are
517 not on the main track canvas or because we want
522 case PlayheadCursorItem:
523 start_cursor_grab (item, event);
527 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
528 hide_marker (item, event);
530 start_marker_grab (item, event);
534 case TempoMarkerItem:
535 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
536 start_tempo_marker_copy_grab (item, event);
538 start_tempo_marker_grab (item, event);
542 case MeterMarkerItem:
543 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
544 start_meter_marker_copy_grab (item, event);
546 start_meter_marker_grab (item, event);
556 case RangeMarkerBarItem:
557 start_range_markerbar_op (item, event, CreateRangeMarker);
561 case CdMarkerBarItem:
562 start_range_markerbar_op (item, event, CreateCDMarker);
566 case TransportMarkerBarItem:
567 start_range_markerbar_op (item, event, CreateTransportMarker);
576 switch (mouse_mode) {
579 case StartSelectionTrimItem:
580 start_selection_op (item, event, SelectionStartTrim);
583 case EndSelectionTrimItem:
584 start_selection_op (item, event, SelectionEndTrim);
588 if (Keyboard::modifier_state_contains
589 (event->button.state, Keyboard::ModifierMask(Keyboard::SecondaryModifier))) {
590 // contains and not equals because I can't use alt as a modifier alone.
591 start_selection_grab (item, event);
592 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
593 /* grab selection for moving */
594 start_selection_op (item, event, SelectionMove);
596 /* this was debated, but decided the more common action was to
597 make a new selection */
598 start_selection_op (item, event, CreateSelection);
603 start_selection_op (item, event, CreateSelection);
609 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)) &&
610 event->type == GDK_BUTTON_PRESS) {
612 start_rubberband_select (item, event);
614 } else if (event->type == GDK_BUTTON_PRESS) {
617 case FadeInHandleItem:
618 start_fade_in_grab (item, event);
621 case FadeOutHandleItem:
622 start_fade_out_grab (item, event);
626 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
627 start_region_copy_grab (item, event);
628 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
629 start_region_brush_grab (item, event);
631 start_region_grab (item, event);
635 case RegionViewNameHighlight:
636 start_trim (item, event);
641 /* rename happens on edit clicks */
642 start_trim (clicked_regionview->get_name_highlight(), event);
646 case GainAutomationControlPointItem:
647 case PanAutomationControlPointItem:
648 case RedirectAutomationControlPointItem:
649 start_control_point_grab (item, event);
653 case GainAutomationLineItem:
654 case PanAutomationLineItem:
655 case RedirectAutomationLineItem:
656 start_line_grab_from_line (item, event);
661 case AutomationTrackItem:
662 start_rubberband_select (item, event);
665 /* <CMT Additions> */
666 case ImageFrameHandleStartItem:
667 imageframe_start_handle_op(item, event) ;
670 case ImageFrameHandleEndItem:
671 imageframe_end_handle_op(item, event) ;
674 case MarkerViewHandleStartItem:
675 markerview_item_start_handle_op(item, event) ;
678 case MarkerViewHandleEndItem:
679 markerview_item_end_handle_op(item, event) ;
682 /* </CMT Additions> */
684 /* <CMT Additions> */
686 start_markerview_grab(item, event) ;
689 start_imageframe_grab(item, event) ;
691 /* </CMT Additions> */
707 // start_line_grab_from_regionview (item, event);
710 case GainControlPointItem:
711 start_control_point_grab (item, event);
715 start_line_grab_from_line (item, event);
718 case GainAutomationControlPointItem:
719 case PanAutomationControlPointItem:
720 case RedirectAutomationControlPointItem:
721 start_control_point_grab (item, event);
732 case GainAutomationControlPointItem:
733 case PanAutomationControlPointItem:
734 case RedirectAutomationControlPointItem:
735 start_control_point_grab (item, event);
738 case GainAutomationLineItem:
739 case PanAutomationLineItem:
740 case RedirectAutomationLineItem:
741 start_line_grab_from_line (item, event);
745 // XXX need automation mode to identify which
747 // start_line_grab_from_regionview (item, event);
757 if (event->type == GDK_BUTTON_PRESS) {
758 start_mouse_zoom (item, event);
765 if (item_type == RegionItem) {
766 start_time_fx (item, event);
773 scrub_reverse_distance = 0;
774 last_scrub_x = event->button.x;
775 scrubbing_direction = 0;
776 track_canvas->get_window()->set_cursor (*transparent_cursor);
777 /* rest handled in motion & release */
786 switch (mouse_mode) {
788 if (event->type == GDK_BUTTON_PRESS) {
791 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
792 start_region_copy_grab (item, event);
794 start_region_grab (item, event);
799 case GainAutomationControlPointItem:
800 case PanAutomationControlPointItem:
801 case RedirectAutomationControlPointItem:
802 start_control_point_grab (item, event);
813 case RegionViewNameHighlight:
814 start_trim (item, event);
819 start_trim (clicked_regionview->get_name_highlight(), event);
830 if (event->type == GDK_BUTTON_PRESS) {
831 /* relax till release */
838 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
839 temporal_zoom_session();
841 temporal_zoom_to_frame (true, event_frame(event));
864 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
866 nframes64_t where = event_frame (event, 0, 0);
867 AutomationTimeAxisView* atv = 0;
869 /* no action if we're recording */
871 if (session && session->actively_recording()) {
875 /* first, see if we're finishing a drag ... */
877 if (drag_info.item) {
878 if (end_grab (item, event)) {
879 /* grab dragged, so do nothing else */
884 button_selection (item, event, item_type);
886 /* edit events get handled here */
888 if (drag_info.item == 0 && Keyboard::is_edit_event (&event->button)) {
894 case TempoMarkerItem:
895 edit_tempo_marker (item);
898 case MeterMarkerItem:
899 edit_meter_marker (item);
903 if (clicked_regionview->name_active()) {
904 return mouse_rename_region (item, event);
914 /* context menu events get handled here */
916 if (Keyboard::is_context_menu_event (&event->button)) {
918 if (drag_info.item == 0) {
920 /* no matter which button pops up the context menu, tell the menu
921 widget to use button 1 to drive menu selection.
926 case FadeInHandleItem:
928 case FadeOutHandleItem:
929 popup_fade_context_menu (1, event->button.time, item, item_type);
933 popup_track_context_menu (1, event->button.time, item_type, false, where);
937 case RegionViewNameHighlight:
939 popup_track_context_menu (1, event->button.time, item_type, false, where);
943 popup_track_context_menu (1, event->button.time, item_type, true, where);
946 case AutomationTrackItem:
947 popup_track_context_menu (1, event->button.time, item_type, false, where);
951 case RangeMarkerBarItem:
952 case TransportMarkerBarItem:
953 case CdMarkerBarItem:
956 popup_ruler_menu (pixel_to_frame(event->button.x), item_type);
960 marker_context_menu (&event->button, item);
963 case TempoMarkerItem:
964 tm_marker_context_menu (&event->button, item);
967 case MeterMarkerItem:
968 tm_marker_context_menu (&event->button, item);
971 case CrossfadeViewItem:
972 popup_track_context_menu (1, event->button.time, item_type, false, where);
975 /* <CMT Additions> */
977 popup_imageframe_edit_menu(1, event->button.time, item, true) ;
979 case ImageFrameTimeAxisItem:
980 popup_imageframe_edit_menu(1, event->button.time, item, false) ;
983 popup_marker_time_axis_edit_menu(1, event->button.time, item, true) ;
985 case MarkerTimeAxisItem:
986 popup_marker_time_axis_edit_menu(1, event->button.time, item, false) ;
988 /* <CMT Additions> */
999 /* delete events get handled here */
1001 if (drag_info.item == 0 && Keyboard::is_delete_event (&event->button)) {
1003 switch (item_type) {
1004 case TempoMarkerItem:
1005 remove_tempo_marker (item);
1008 case MeterMarkerItem:
1009 remove_meter_marker (item);
1013 remove_marker (*item, event);
1017 if (mouse_mode == MouseObject) {
1018 remove_clicked_region ();
1022 case GainControlPointItem:
1023 if (mouse_mode == MouseGain) {
1024 remove_gain_control_point (item, event);
1028 case GainAutomationControlPointItem:
1029 case PanAutomationControlPointItem:
1030 case RedirectAutomationControlPointItem:
1031 remove_control_point (item, event);
1040 switch (event->button.button) {
1043 switch (item_type) {
1044 /* see comments in button_press_handler */
1045 case PlayheadCursorItem:
1048 case GainAutomationLineItem:
1049 case PanAutomationLineItem:
1050 case RedirectAutomationLineItem:
1051 case StartSelectionTrimItem:
1052 case EndSelectionTrimItem:
1056 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1057 snap_to (where, 0, true);
1059 mouse_add_new_marker (where);
1062 case CdMarkerBarItem:
1063 // if we get here then a dragged range wasn't done
1064 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1065 snap_to (where, 0, true);
1067 mouse_add_new_marker (where, true);
1071 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1074 mouse_add_new_tempo_event (where);
1078 mouse_add_new_meter_event (pixel_to_frame (event->button.x));
1086 switch (mouse_mode) {
1088 switch (item_type) {
1089 case AutomationTrackItem:
1090 atv = dynamic_cast<AutomationTimeAxisView*>(clicked_trackview);
1092 atv->add_automation_event (item, event, where, event->button.y);
1104 // Gain only makes sense for audio regions
1106 if (!dynamic_cast<AudioRegionView*>(clicked_regionview)) {
1110 switch (item_type) {
1112 dynamic_cast<AudioRegionView*>(clicked_regionview)->add_gain_point_event (item, event);
1116 case AutomationTrackItem:
1117 dynamic_cast<AutomationTimeAxisView*>(clicked_trackview)->
1118 add_automation_event (item, event, where, event->button.y);
1128 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1129 if (scrubbing_direction == 0) {
1130 /* no drag, just a click */
1131 switch (item_type) {
1133 play_selected_region ();
1139 /* make sure we stop */
1140 session->request_transport_speed (0.0);
1154 switch (mouse_mode) {
1157 switch (item_type) {
1159 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1161 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier|Keyboard::SecondaryModifier))) {
1164 // Button2 click is unused
1177 // x_style_paste (where, 1.0);
1197 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1203 if (last_item_entered != item) {
1204 last_item_entered = item;
1205 last_item_entered_n = 0;
1208 switch (item_type) {
1209 case GainControlPointItem:
1210 if (mouse_mode == MouseGain) {
1211 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1212 cp->set_visible (true);
1216 at_y = cp->get_y ();
1217 cp->item->i2w (at_x, at_y);
1221 fraction = 1.0 - (cp->get_y() / cp->line.height());
1223 if (is_drawable() && !_scrubbing) {
1224 track_canvas->get_window()->set_cursor (*fader_cursor);
1227 last_item_entered_n++;
1228 set_verbose_canvas_cursor (cp->line.get_verbose_cursor_string (fraction), at_x, at_y);
1229 if (last_item_entered_n < 10) {
1230 show_verbose_canvas_cursor ();
1235 case GainAutomationControlPointItem:
1236 case PanAutomationControlPointItem:
1237 case RedirectAutomationControlPointItem:
1238 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1239 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1240 cp->set_visible (true);
1244 at_y = cp->get_y ();
1245 cp->item->i2w (at_x, at_y);
1249 fraction = 1.0 - (cp->get_y() / cp->line.height());
1251 set_verbose_canvas_cursor (cp->line.get_verbose_cursor_string (fraction), at_x, at_y);
1252 show_verbose_canvas_cursor ();
1254 if (is_drawable()) {
1255 track_canvas->get_window()->set_cursor (*fader_cursor);
1261 if (mouse_mode == MouseGain) {
1262 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1264 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredGainLine.get();
1265 if (is_drawable()) {
1266 track_canvas->get_window()->set_cursor (*fader_cursor);
1271 case GainAutomationLineItem:
1272 case RedirectAutomationLineItem:
1273 case PanAutomationLineItem:
1274 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1276 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1278 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredAutomationLine.get();
1280 if (is_drawable()) {
1281 track_canvas->get_window()->set_cursor (*fader_cursor);
1286 case RegionViewNameHighlight:
1287 if (is_drawable() && mouse_mode == MouseObject) {
1288 track_canvas->get_window()->set_cursor (*trimmer_cursor);
1292 case StartSelectionTrimItem:
1293 case EndSelectionTrimItem:
1294 /* <CMT Additions> */
1295 case ImageFrameHandleStartItem:
1296 case ImageFrameHandleEndItem:
1297 case MarkerViewHandleStartItem:
1298 case MarkerViewHandleEndItem:
1299 /* </CMT Additions> */
1301 if (is_drawable()) {
1302 track_canvas->get_window()->set_cursor (*trimmer_cursor);
1306 case PlayheadCursorItem:
1307 if (is_drawable()) {
1308 switch (_edit_point) {
1310 track_canvas->get_window()->set_cursor (*grabber_edit_point_cursor);
1313 track_canvas->get_window()->set_cursor (*grabber_cursor);
1319 case RegionViewName:
1321 /* when the name is not an active item, the entire name highlight is for trimming */
1323 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1324 if (mouse_mode == MouseObject && is_drawable()) {
1325 track_canvas->get_window()->set_cursor (*trimmer_cursor);
1331 case AutomationTrackItem:
1332 if (is_drawable()) {
1333 Gdk::Cursor *cursor;
1334 switch (mouse_mode) {
1336 cursor = selector_cursor;
1339 cursor = zoom_cursor;
1342 cursor = cross_hair_cursor;
1346 track_canvas->get_window()->set_cursor (*cursor);
1348 AutomationTimeAxisView* atv;
1349 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1350 clear_entered_track = false;
1351 set_entered_track (atv);
1357 case RangeMarkerBarItem:
1358 case TransportMarkerBarItem:
1359 case CdMarkerBarItem:
1362 if (is_drawable()) {
1363 time_canvas->get_window()->set_cursor (*timebar_cursor);
1368 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1371 entered_marker = marker;
1372 marker->set_color_rgba (ARDOUR_UI::config()->canvasvar_EnteredMarker.get());
1374 case MeterMarkerItem:
1375 case TempoMarkerItem:
1376 if (is_drawable()) {
1377 time_canvas->get_window()->set_cursor (*timebar_cursor);
1380 case FadeInHandleItem:
1381 case FadeOutHandleItem:
1382 if (mouse_mode == MouseObject) {
1383 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1385 rect->property_fill_color_rgba() = 0;
1386 rect->property_outline_pixels() = 1;
1395 /* second pass to handle entered track status in a comprehensible way.
1398 switch (item_type) {
1400 case GainAutomationLineItem:
1401 case RedirectAutomationLineItem:
1402 case PanAutomationLineItem:
1403 case GainControlPointItem:
1404 case GainAutomationControlPointItem:
1405 case PanAutomationControlPointItem:
1406 case RedirectAutomationControlPointItem:
1407 /* these do not affect the current entered track state */
1408 clear_entered_track = false;
1411 case AutomationTrackItem:
1412 /* handled above already */
1416 set_entered_track (0);
1424 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1433 switch (item_type) {
1434 case GainControlPointItem:
1435 case GainAutomationControlPointItem:
1436 case PanAutomationControlPointItem:
1437 case RedirectAutomationControlPointItem:
1438 cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
1439 if (cp->line.npoints() > 1) {
1440 if (!cp->selected) {
1441 cp->set_visible (false);
1445 if (is_drawable()) {
1446 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1449 hide_verbose_canvas_cursor ();
1452 case RegionViewNameHighlight:
1453 case StartSelectionTrimItem:
1454 case EndSelectionTrimItem:
1455 case PlayheadCursorItem:
1456 /* <CMT Additions> */
1457 case ImageFrameHandleStartItem:
1458 case ImageFrameHandleEndItem:
1459 case MarkerViewHandleStartItem:
1460 case MarkerViewHandleEndItem:
1461 /* </CMT Additions> */
1462 if (is_drawable()) {
1463 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1468 case GainAutomationLineItem:
1469 case RedirectAutomationLineItem:
1470 case PanAutomationLineItem:
1471 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1473 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1475 line->property_fill_color_rgba() = al->get_line_color();
1477 if (is_drawable()) {
1478 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1482 case RegionViewName:
1483 /* see enter_handler() for notes */
1484 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1485 if (is_drawable() && mouse_mode == MouseObject) {
1486 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1491 case RangeMarkerBarItem:
1492 case TransportMarkerBarItem:
1493 case CdMarkerBarItem:
1497 if (is_drawable()) {
1498 time_canvas->get_window()->set_cursor (*timebar_cursor);
1503 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1507 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
1508 location_flags_changed (loc, this);
1511 case MeterMarkerItem:
1512 case TempoMarkerItem:
1514 if (is_drawable()) {
1515 time_canvas->get_window()->set_cursor (*timebar_cursor);
1520 case FadeInHandleItem:
1521 case FadeOutHandleItem:
1522 rv = static_cast<RegionView*>(item->get_data ("regionview"));
1524 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1526 rect->property_fill_color_rgba() = rv->get_fill_color();
1527 rect->property_outline_pixels() = 0;
1532 case AutomationTrackItem:
1533 if (is_drawable()) {
1534 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1535 clear_entered_track = true;
1536 Glib::signal_idle().connect (mem_fun(*this, &Editor::left_automation_track));
1548 Editor::left_automation_track ()
1550 if (clear_entered_track) {
1551 set_entered_track (0);
1552 clear_entered_track = false;
1562 if (scrubbing_direction == 0) {
1564 session->request_locate (drag_info.current_pointer_frame, false);
1565 session->request_transport_speed (0.1);
1566 scrubbing_direction = 1;
1570 if (last_scrub_x > drag_info.current_pointer_x) {
1572 /* pointer moved to the left */
1574 if (scrubbing_direction > 0) {
1576 /* we reversed direction to go backwards */
1579 scrub_reverse_distance += (int) (last_scrub_x - drag_info.current_pointer_x);
1583 /* still moving to the left (backwards) */
1585 scrub_reversals = 0;
1586 scrub_reverse_distance = 0;
1588 delta = 0.01 * (last_scrub_x - drag_info.current_pointer_x);
1589 session->request_transport_speed (session->transport_speed() - delta);
1593 /* pointer moved to the right */
1595 if (scrubbing_direction < 0) {
1596 /* we reversed direction to go forward */
1599 scrub_reverse_distance += (int) (drag_info.current_pointer_x - last_scrub_x);
1602 /* still moving to the right */
1604 scrub_reversals = 0;
1605 scrub_reverse_distance = 0;
1607 delta = 0.01 * (drag_info.current_pointer_x - last_scrub_x);
1608 session->request_transport_speed (session->transport_speed() + delta);
1612 /* if there have been more than 2 opposite motion moves detected, or one that moves
1613 back more than 10 pixels, reverse direction
1616 if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
1618 if (scrubbing_direction > 0) {
1619 /* was forwards, go backwards */
1620 session->request_transport_speed (-0.1);
1621 scrubbing_direction = -1;
1623 /* was backwards, go forwards */
1624 session->request_transport_speed (0.1);
1625 scrubbing_direction = 1;
1628 scrub_reverse_distance = 0;
1629 scrub_reversals = 0;
1633 last_scrub_x = drag_info.current_pointer_x;
1637 Editor::motion_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type, bool from_autoscroll)
1639 if (event->motion.is_hint) {
1642 /* We call this so that MOTION_NOTIFY events continue to be
1643 delivered to the canvas. We need to do this because we set
1644 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
1645 the density of the events, at the expense of a round-trip
1646 to the server. Given that this will mostly occur on cases
1647 where DISPLAY = :0.0, and given the cost of what the motion
1648 event might do, its a good tradeoff.
1651 track_canvas->get_pointer (x, y);
1654 if (current_stepping_trackview) {
1655 /* don't keep the persistent stepped trackview if the mouse moves */
1656 current_stepping_trackview = 0;
1657 step_timeout.disconnect ();
1660 if (session && session->actively_recording()) {
1661 /* Sorry. no dragging stuff around while we record */
1665 drag_info.item_type = item_type;
1666 drag_info.last_pointer_x = drag_info.current_pointer_x;
1667 drag_info.last_pointer_y = drag_info.current_pointer_y;
1668 drag_info.current_pointer_frame = event_frame (event, &drag_info.current_pointer_x,
1669 &drag_info.current_pointer_y);
1672 switch (mouse_mode) {
1683 if (!from_autoscroll && drag_info.item) {
1684 /* item != 0 is the best test i can think of for dragging.
1686 if (!drag_info.move_threshold_passed) {
1688 bool x_threshold_passed = (::llabs ((nframes64_t) (drag_info.current_pointer_x - drag_info.grab_x)) > 4LL);
1689 bool y_threshold_passed = (::llabs ((nframes64_t) (drag_info.current_pointer_y - drag_info.grab_y)) > 4LL);
1691 drag_info.move_threshold_passed = (x_threshold_passed || y_threshold_passed);
1693 // and change the initial grab loc/frame if this drag info wants us to
1695 if (drag_info.want_move_threshold && drag_info.move_threshold_passed) {
1696 drag_info.grab_frame = drag_info.current_pointer_frame;
1697 drag_info.grab_x = drag_info.current_pointer_x;
1698 drag_info.grab_y = drag_info.current_pointer_y;
1699 drag_info.last_pointer_frame = drag_info.grab_frame;
1700 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
1705 switch (item_type) {
1706 case PlayheadCursorItem:
1708 case GainControlPointItem:
1709 case RedirectAutomationControlPointItem:
1710 case GainAutomationControlPointItem:
1711 case PanAutomationControlPointItem:
1712 case TempoMarkerItem:
1713 case MeterMarkerItem:
1714 case RegionViewNameHighlight:
1715 case StartSelectionTrimItem:
1716 case EndSelectionTrimItem:
1719 case RedirectAutomationLineItem:
1720 case GainAutomationLineItem:
1721 case PanAutomationLineItem:
1722 case FadeInHandleItem:
1723 case FadeOutHandleItem:
1724 /* <CMT Additions> */
1725 case ImageFrameHandleStartItem:
1726 case ImageFrameHandleEndItem:
1727 case MarkerViewHandleStartItem:
1728 case MarkerViewHandleEndItem:
1729 /* </CMT Additions> */
1730 if (drag_info.item && (event->motion.state & Gdk::BUTTON1_MASK ||
1731 (event->motion.state & Gdk::BUTTON2_MASK))) {
1732 if (!from_autoscroll) {
1733 maybe_autoscroll (&event->motion);
1735 (this->*(drag_info.motion_callback)) (item, event);
1744 switch (mouse_mode) {
1749 if (drag_info.item && (event->motion.state & GDK_BUTTON1_MASK ||
1750 (event->motion.state & GDK_BUTTON2_MASK))) {
1751 if (!from_autoscroll) {
1752 maybe_autoscroll (&event->motion);
1754 (this->*(drag_info.motion_callback)) (item, event);
1765 track_canvas_motion (event);
1766 // drag_info.last_pointer_frame = drag_info.current_pointer_frame;
1774 Editor::break_drag ()
1776 stop_canvas_autoscroll ();
1777 hide_verbose_canvas_cursor ();
1779 if (drag_info.item) {
1780 drag_info.item->ungrab (0);
1782 /* put it back where it came from */
1787 drag_info.item->i2w (cxw, cyw);
1788 drag_info.item->move (drag_info.original_x - cxw, drag_info.original_y - cyw);
1795 Editor::finalize_drag ()
1798 drag_info.copy = false;
1799 drag_info.motion_callback = 0;
1800 drag_info.finished_callback = 0;
1801 drag_info.dest_trackview = 0;
1802 drag_info.source_trackview = 0;
1803 drag_info.last_frame_position = 0;
1804 drag_info.grab_frame = 0;
1805 drag_info.last_pointer_frame = 0;
1806 drag_info.current_pointer_frame = 0;
1807 drag_info.brushing = false;
1809 if (drag_info.copied_location) {
1810 delete drag_info.copied_location;
1811 drag_info.copied_location = 0;
1816 Editor::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
1818 if (drag_info.item == 0) {
1819 fatal << _("programming error: start_grab called without drag item") << endmsg;
1825 cursor = which_grabber_cursor ();
1828 // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
1830 if (event->button.button == 2) {
1831 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
1832 drag_info.y_constrained = true;
1833 drag_info.x_constrained = false;
1835 drag_info.y_constrained = false;
1836 drag_info.x_constrained = true;
1839 drag_info.x_constrained = false;
1840 drag_info.y_constrained = false;
1843 drag_info.grab_frame = event_frame (event, &drag_info.grab_x, &drag_info.grab_y);
1844 drag_info.last_pointer_frame = drag_info.grab_frame;
1845 drag_info.current_pointer_frame = drag_info.grab_frame;
1846 drag_info.current_pointer_x = drag_info.grab_x;
1847 drag_info.current_pointer_y = drag_info.grab_y;
1848 drag_info.last_pointer_x = drag_info.current_pointer_x;
1849 drag_info.last_pointer_y = drag_info.current_pointer_y;
1850 drag_info.cumulative_x_drag = 0;
1851 drag_info.cumulative_y_drag = 0;
1852 drag_info.first_move = true;
1853 drag_info.move_threshold_passed = false;
1854 drag_info.want_move_threshold = false;
1855 drag_info.pointer_frame_offset = 0;
1856 drag_info.brushing = false;
1857 drag_info.copied_location = 0;
1859 drag_info.original_x = 0;
1860 drag_info.original_y = 0;
1861 drag_info.item->i2w (drag_info.original_x, drag_info.original_y);
1863 drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK,
1865 event->button.time);
1867 if (session && session->transport_rolling()) {
1868 drag_info.was_rolling = true;
1870 drag_info.was_rolling = false;
1873 switch (snap_type) {
1874 case SnapToRegionStart:
1875 case SnapToRegionEnd:
1876 case SnapToRegionSync:
1877 case SnapToRegionBoundary:
1878 build_region_boundary_cache ();
1886 Editor::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t time)
1888 drag_info.item->ungrab (0);
1889 drag_info.item = new_item;
1892 cursor = which_grabber_cursor ();
1895 drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK, *cursor, time);
1899 Editor::end_grab (ArdourCanvas::Item* item, GdkEvent* event)
1901 bool did_drag = false;
1903 stop_canvas_autoscroll ();
1905 if (drag_info.item == 0) {
1909 drag_info.item->ungrab (event->button.time);
1911 if (drag_info.finished_callback) {
1912 drag_info.last_pointer_x = drag_info.current_pointer_x;
1913 drag_info.last_pointer_y = drag_info.current_pointer_y;
1914 (this->*(drag_info.finished_callback)) (item, event);
1917 did_drag = !drag_info.first_move;
1919 hide_verbose_canvas_cursor();
1927 Editor::start_fade_in_grab (ArdourCanvas::Item* item, GdkEvent* event)
1929 drag_info.item = item;
1930 drag_info.motion_callback = &Editor::fade_in_drag_motion_callback;
1931 drag_info.finished_callback = &Editor::fade_in_drag_finished_callback;
1935 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
1936 fatal << _("programming error: fade in canvas item has no regionview data pointer!") << endmsg;
1940 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1942 drag_info.pointer_frame_offset = drag_info.grab_frame - ((nframes64_t) arv->audio_region()->fade_in().back()->when + arv->region()->position());
1946 Editor::fade_in_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1948 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1950 nframes64_t fade_length;
1952 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1953 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1959 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1963 if (pos < (arv->region()->position() + 64)) {
1964 fade_length = 64; // this should be a minimum defined somewhere
1965 } else if (pos > arv->region()->last_frame()) {
1966 fade_length = arv->region()->length();
1968 fade_length = pos - arv->region()->position();
1970 /* mapover the region selection */
1972 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1974 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1980 tmp->reset_fade_in_shape_width (fade_length);
1983 show_verbose_duration_cursor (arv->region()->position(), arv->region()->position() + fade_length, 10);
1985 drag_info.first_move = false;
1989 Editor::fade_in_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1991 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1993 nframes64_t fade_length;
1995 if (drag_info.first_move) return;
1997 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1998 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2003 if (pos < (arv->region()->position() + 64)) {
2004 fade_length = 64; // this should be a minimum defined somewhere
2005 } else if (pos > arv->region()->last_frame()) {
2006 fade_length = arv->region()->length();
2008 fade_length = pos - arv->region()->position();
2011 begin_reversible_command (_("change fade in length"));
2013 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2015 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2021 AutomationList& alist = tmp->audio_region()->fade_in();
2022 XMLNode &before = alist.get_state();
2024 tmp->audio_region()->set_fade_in_length (fade_length);
2025 tmp->audio_region()->set_fade_in_active (true);
2027 XMLNode &after = alist.get_state();
2028 session->add_command(new MementoCommand<AutomationList>(alist, &before, &after));
2031 commit_reversible_command ();
2035 Editor::start_fade_out_grab (ArdourCanvas::Item* item, GdkEvent* event)
2037 drag_info.item = item;
2038 drag_info.motion_callback = &Editor::fade_out_drag_motion_callback;
2039 drag_info.finished_callback = &Editor::fade_out_drag_finished_callback;
2043 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
2044 fatal << _("programming error: fade out canvas item has no regionview data pointer!") << endmsg;
2048 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
2050 drag_info.pointer_frame_offset = drag_info.grab_frame - (arv->region()->length() - (nframes64_t) arv->audio_region()->fade_out().back()->when + arv->region()->position());
2054 Editor::fade_out_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2056 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
2058 nframes64_t fade_length;
2060 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2061 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2066 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2070 if (pos > (arv->region()->last_frame() - 64)) {
2071 fade_length = 64; // this should really be a minimum fade defined somewhere
2073 else if (pos < arv->region()->position()) {
2074 fade_length = arv->region()->length();
2077 fade_length = arv->region()->last_frame() - pos;
2080 /* mapover the region selection */
2082 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2084 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2090 tmp->reset_fade_out_shape_width (fade_length);
2093 show_verbose_duration_cursor (arv->region()->last_frame() - fade_length, arv->region()->last_frame(), 10);
2095 drag_info.first_move = false;
2099 Editor::fade_out_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2101 if (drag_info.first_move) return;
2103 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
2105 nframes64_t fade_length;
2107 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2108 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2114 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2118 if (pos > (arv->region()->last_frame() - 64)) {
2119 fade_length = 64; // this should really be a minimum fade defined somewhere
2121 else if (pos < arv->region()->position()) {
2122 fade_length = arv->region()->length();
2125 fade_length = arv->region()->last_frame() - pos;
2128 begin_reversible_command (_("change fade out length"));
2130 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2132 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2138 AutomationList& alist = tmp->audio_region()->fade_out();
2139 XMLNode &before = alist.get_state();
2141 tmp->audio_region()->set_fade_out_length (fade_length);
2142 tmp->audio_region()->set_fade_out_active (true);
2144 XMLNode &after = alist.get_state();
2145 session->add_command(new MementoCommand<AutomationList>(alist, &before, &after));
2148 commit_reversible_command ();
2152 Editor::start_cursor_grab (ArdourCanvas::Item* item, GdkEvent* event)
2154 drag_info.item = item;
2155 drag_info.motion_callback = &Editor::cursor_drag_motion_callback;
2156 drag_info.finished_callback = &Editor::cursor_drag_finished_callback;
2160 if ((drag_info.data = (item->get_data ("cursor"))) == 0) {
2161 fatal << _("programming error: cursor canvas item has no cursor data pointer!") << endmsg;
2165 Cursor* cursor = (Cursor *) drag_info.data;
2167 if (cursor == playhead_cursor) {
2168 _dragging_playhead = true;
2170 if (session && drag_info.was_rolling) {
2171 session->request_stop ();
2174 if (session && session->is_auditioning()) {
2175 session->cancel_audition ();
2179 drag_info.pointer_frame_offset = drag_info.grab_frame - cursor->current_frame;
2181 show_verbose_time_cursor (cursor->current_frame, 10);
2185 Editor::cursor_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2187 Cursor* cursor = (Cursor *) drag_info.data;
2188 nframes64_t adjusted_frame;
2190 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2191 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2197 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2198 if (cursor == playhead_cursor) {
2199 snap_to (adjusted_frame);
2203 if (adjusted_frame == drag_info.last_pointer_frame) return;
2205 cursor->set_position (adjusted_frame);
2207 UpdateAllTransportClocks (cursor->current_frame);
2209 show_verbose_time_cursor (cursor->current_frame, 10);
2211 drag_info.last_pointer_frame = adjusted_frame;
2212 drag_info.first_move = false;
2216 Editor::cursor_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2218 if (drag_info.first_move) return;
2220 cursor_drag_motion_callback (item, event);
2222 _dragging_playhead = false;
2224 if (item == &playhead_cursor->canvas_item) {
2226 session->request_locate (playhead_cursor->current_frame, drag_info.was_rolling);
2232 Editor::update_marker_drag_item (Location *location)
2234 double x1 = frame_to_pixel (location->start());
2235 double x2 = frame_to_pixel (location->end());
2237 if (location->is_mark()) {
2238 marker_drag_line_points.front().set_x(x1);
2239 marker_drag_line_points.back().set_x(x1);
2240 marker_drag_line->property_points() = marker_drag_line_points;
2243 range_marker_drag_rect->property_x1() = x1;
2244 range_marker_drag_rect->property_x2() = x2;
2249 Editor::start_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2253 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2254 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2260 Location *location = find_location_from_marker (marker, is_start);
2262 drag_info.item = item;
2263 drag_info.data = marker;
2264 drag_info.motion_callback = &Editor::marker_drag_motion_callback;
2265 drag_info.finished_callback = &Editor::marker_drag_finished_callback;
2269 _dragging_edit_point = true;
2271 drag_info.copied_location = new Location (*location);
2272 drag_info.pointer_frame_offset = drag_info.grab_frame - (is_start ? location->start() : location->end());
2274 update_marker_drag_item (location);
2276 if (location->is_mark()) {
2277 // marker_drag_line->show();
2278 // marker_drag_line->raise_to_top();
2280 range_marker_drag_rect->show();
2281 range_marker_drag_rect->raise_to_top();
2285 show_verbose_time_cursor (location->start(), 10);
2287 show_verbose_time_cursor (location->end(), 10);
2290 Selection::Operation op = Keyboard::selection_type (event->button.state);
2293 case Selection::Toggle:
2294 selection->toggle (marker);
2296 case Selection::Set:
2297 selection->set (marker);
2299 case Selection::Extend:
2300 selection->add (marker);
2302 case Selection::Add:
2303 selection->add (marker);
2309 Editor::marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2311 nframes64_t f_delta;
2312 Marker* marker = (Marker *) drag_info.data;
2313 Location *real_location;
2314 Location *copy_location;
2316 bool move_both = false;
2318 nframes64_t newframe;
2319 if (drag_info.pointer_frame_offset <= drag_info.current_pointer_frame) {
2320 newframe = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2325 nframes64_t next = newframe;
2327 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2328 snap_to (newframe, 0, true);
2331 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
2335 /* call this to find out if its the start or end */
2337 if ((real_location = find_location_from_marker (marker, is_start)) == 0) {
2341 if (real_location->locked()) {
2345 /* use the copy that we're "dragging" around */
2347 copy_location = drag_info.copied_location;
2349 f_delta = copy_location->end() - copy_location->start();
2351 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2355 if (copy_location->is_mark()) {
2358 copy_location->set_start (newframe);
2362 if (is_start) { // start-of-range marker
2365 copy_location->set_start (newframe);
2366 copy_location->set_end (newframe + f_delta);
2367 } else if (newframe < copy_location->end()) {
2368 copy_location->set_start (newframe);
2370 snap_to (next, 1, true);
2371 copy_location->set_end (next);
2372 copy_location->set_start (newframe);
2375 } else { // end marker
2378 copy_location->set_end (newframe);
2379 copy_location->set_start (newframe - f_delta);
2380 } else if (newframe > copy_location->start()) {
2381 copy_location->set_end (newframe);
2383 } else if (newframe > 0) {
2384 snap_to (next, -1, true);
2385 copy_location->set_start (next);
2386 copy_location->set_end (newframe);
2391 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
2392 drag_info.first_move = false;
2394 update_marker_drag_item (copy_location);
2396 LocationMarkers* lm = find_location_markers (real_location);
2397 lm->set_position (copy_location->start(), copy_location->end());
2398 edit_point_clock.set (copy_location->start());
2400 show_verbose_time_cursor (newframe, 10);
2404 Editor::marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2406 if (drag_info.first_move) {
2407 /* just a click, do nothing but whatever selection occured */
2411 _dragging_edit_point = false;
2413 Marker* marker = (Marker *) drag_info.data;
2416 begin_reversible_command ( _("move marker") );
2417 XMLNode &before = session->locations()->get_state();
2419 Location * location = find_location_from_marker (marker, is_start);
2423 if (location->locked()) {
2427 if (location->is_mark()) {
2428 location->set_start (drag_info.copied_location->start());
2430 location->set (drag_info.copied_location->start(), drag_info.copied_location->end());
2434 XMLNode &after = session->locations()->get_state();
2435 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
2436 commit_reversible_command ();
2438 marker_drag_line->hide();
2439 range_marker_drag_rect->hide();
2443 Editor::start_meter_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2446 MeterMarker* meter_marker;
2448 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2449 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2453 meter_marker = dynamic_cast<MeterMarker*> (marker);
2455 MetricSection& section (meter_marker->meter());
2457 if (!section.movable()) {
2461 drag_info.item = item;
2462 drag_info.copy = false;
2463 drag_info.data = marker;
2464 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2465 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2469 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2471 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2475 Editor::start_meter_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2478 MeterMarker* meter_marker;
2480 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2481 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2485 meter_marker = dynamic_cast<MeterMarker*> (marker);
2487 // create a dummy marker for visual representation of moving the copy.
2488 // The actual copying is not done before we reach the finish callback.
2490 snprintf (name, sizeof(name), "%g/%g", meter_marker->meter().beats_per_bar(), meter_marker->meter().note_divisor ());
2491 MeterMarker* new_marker = new MeterMarker(*this, *meter_group, ARDOUR_UI::config()->canvasvar_MeterMarker.get(), name,
2492 *new MeterSection(meter_marker->meter()));
2494 drag_info.item = &new_marker->the_item();
2495 drag_info.copy = true;
2496 drag_info.data = new_marker;
2497 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2498 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2502 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2504 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2508 Editor::meter_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2510 MeterMarker* marker = (MeterMarker *) drag_info.data;
2511 nframes64_t adjusted_frame;
2513 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2514 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2520 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2521 snap_to (adjusted_frame);
2524 if (adjusted_frame == drag_info.last_pointer_frame) return;
2526 marker->set_position (adjusted_frame);
2529 drag_info.last_pointer_frame = adjusted_frame;
2530 drag_info.first_move = false;
2532 show_verbose_time_cursor (adjusted_frame, 10);
2536 Editor::meter_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2538 if (drag_info.first_move) return;
2540 meter_marker_drag_motion_callback (drag_info.item, event);
2542 MeterMarker* marker = (MeterMarker *) drag_info.data;
2545 TempoMap& map (session->tempo_map());
2546 map.bbt_time (drag_info.last_pointer_frame, when);
2548 if (drag_info.copy == true) {
2549 begin_reversible_command (_("copy meter mark"));
2550 XMLNode &before = map.get_state();
2551 map.add_meter (marker->meter(), when);
2552 XMLNode &after = map.get_state();
2553 session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2554 commit_reversible_command ();
2556 // delete the dummy marker we used for visual representation of copying.
2557 // a new visual marker will show up automatically.
2560 begin_reversible_command (_("move meter mark"));
2561 XMLNode &before = map.get_state();
2562 map.move_meter (marker->meter(), when);
2563 XMLNode &after = map.get_state();
2564 session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2565 commit_reversible_command ();
2570 Editor::start_tempo_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2573 TempoMarker* tempo_marker;
2575 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2576 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2580 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2581 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2585 MetricSection& section (tempo_marker->tempo());
2587 if (!section.movable()) {
2591 drag_info.item = item;
2592 drag_info.copy = false;
2593 drag_info.data = marker;
2594 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2595 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2599 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2600 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2604 Editor::start_tempo_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2607 TempoMarker* tempo_marker;
2609 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2610 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2614 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2615 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2619 // create a dummy marker for visual representation of moving the copy.
2620 // The actual copying is not done before we reach the finish callback.
2622 snprintf (name, sizeof (name), "%.2f", tempo_marker->tempo().beats_per_minute());
2623 TempoMarker* new_marker = new TempoMarker(*this, *tempo_group, ARDOUR_UI::config()->canvasvar_TempoMarker.get(), name,
2624 *new TempoSection(tempo_marker->tempo()));
2626 drag_info.item = &new_marker->the_item();
2627 drag_info.copy = true;
2628 drag_info.data = new_marker;
2629 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2630 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2634 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2636 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2640 Editor::tempo_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2642 TempoMarker* marker = (TempoMarker *) drag_info.data;
2643 nframes64_t adjusted_frame;
2645 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2646 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2652 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2653 snap_to (adjusted_frame);
2656 if (adjusted_frame == drag_info.last_pointer_frame) return;
2658 /* OK, we've moved far enough to make it worth actually move the thing. */
2660 marker->set_position (adjusted_frame);
2662 show_verbose_time_cursor (adjusted_frame, 10);
2664 drag_info.last_pointer_frame = adjusted_frame;
2665 drag_info.first_move = false;
2669 Editor::tempo_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2671 if (drag_info.first_move) return;
2673 tempo_marker_drag_motion_callback (drag_info.item, event);
2675 TempoMarker* marker = (TempoMarker *) drag_info.data;
2678 TempoMap& map (session->tempo_map());
2679 map.bbt_time (drag_info.last_pointer_frame, when);
2681 if (drag_info.copy == true) {
2682 begin_reversible_command (_("copy tempo mark"));
2683 XMLNode &before = map.get_state();
2684 map.add_tempo (marker->tempo(), when);
2685 XMLNode &after = map.get_state();
2686 session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2687 commit_reversible_command ();
2689 // delete the dummy marker we used for visual representation of copying.
2690 // a new visual marker will show up automatically.
2693 begin_reversible_command (_("move tempo mark"));
2694 XMLNode &before = map.get_state();
2695 map.move_tempo (marker->tempo(), when);
2696 XMLNode &after = map.get_state();
2697 session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2698 commit_reversible_command ();
2703 Editor::remove_gain_control_point (ArdourCanvas::Item*item, GdkEvent* event)
2705 ControlPoint* control_point;
2707 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2708 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2712 // We shouldn't remove the first or last gain point
2713 if (control_point->line.is_last_point(*control_point) ||
2714 control_point->line.is_first_point(*control_point)) {
2718 control_point->line.remove_point (*control_point);
2722 Editor::remove_control_point (ArdourCanvas::Item*item, GdkEvent* event)
2724 ControlPoint* control_point;
2726 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2727 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2731 control_point->line.remove_point (*control_point);
2735 Editor::start_control_point_grab (ArdourCanvas::Item* item, GdkEvent* event)
2737 ControlPoint* control_point;
2739 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2740 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2744 drag_info.item = item;
2745 drag_info.data = control_point;
2746 drag_info.motion_callback = &Editor::control_point_drag_motion_callback;
2747 drag_info.finished_callback = &Editor::control_point_drag_finished_callback;
2749 start_grab (event, fader_cursor);
2751 // start the grab at the center of the control point so
2752 // the point doesn't 'jump' to the mouse after the first drag
2753 drag_info.grab_x = control_point->get_x();
2754 drag_info.grab_y = control_point->get_y();
2755 control_point->line.parent_group().i2w(drag_info.grab_x, drag_info.grab_y);
2756 track_canvas->w2c(drag_info.grab_x, drag_info.grab_y,
2757 drag_info.grab_x, drag_info.grab_y);
2759 drag_info.grab_frame = pixel_to_frame(drag_info.grab_x);
2761 control_point->line.start_drag (control_point, drag_info.grab_frame, 0);
2763 float fraction = 1.0 - (control_point->get_y() / control_point->line.height());
2764 set_verbose_canvas_cursor (control_point->line.get_verbose_cursor_string (fraction),
2765 drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
2767 show_verbose_canvas_cursor ();
2771 Editor::control_point_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2773 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2775 double dx = drag_info.current_pointer_x - drag_info.last_pointer_x;
2776 double dy = drag_info.current_pointer_y - drag_info.last_pointer_y;
2778 if (event->button.state & Keyboard::SecondaryModifier) {
2783 double cx = drag_info.grab_x + drag_info.cumulative_x_drag + dx;
2784 double cy = drag_info.grab_y + drag_info.cumulative_y_drag + dy;
2786 // calculate zero crossing point. back off by .01 to stay on the
2787 // positive side of zero
2789 double zero_gain_y = (1.0 - ZERO_GAIN_FRACTION) * cp->line.height() - .01;
2790 cp->line.parent_group().i2w(_unused, zero_gain_y);
2792 // make sure we hit zero when passing through
2793 if ((cy < zero_gain_y and (cy - dy) > zero_gain_y)
2794 or (cy > zero_gain_y and (cy - dy) < zero_gain_y)) {
2798 if (drag_info.x_constrained) {
2799 cx = drag_info.grab_x;
2801 if (drag_info.y_constrained) {
2802 cy = drag_info.grab_y;
2805 drag_info.cumulative_x_drag = cx - drag_info.grab_x;
2806 drag_info.cumulative_y_drag = cy - drag_info.grab_y;
2808 cp->line.parent_group().w2i (cx, cy);
2812 cy = min ((double) cp->line.height(), cy);
2814 //translate cx to frames
2815 nframes64_t cx_frames = unit_to_frame (cx);
2817 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier()) && !drag_info.x_constrained) {
2818 snap_to (cx_frames);
2821 float fraction = 1.0 - (cy / cp->line.height());
2825 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) {
2831 cp->line.point_drag (*cp, cx_frames , fraction, push);
2833 set_verbose_canvas_cursor_text (cp->line.get_verbose_cursor_string (fraction));
2835 drag_info.first_move = false;
2839 Editor::control_point_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2841 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2843 if (drag_info.first_move) {
2847 if ((event->type == GDK_BUTTON_RELEASE) && (event->button.button == 1) && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2848 reset_point_selection ();
2852 control_point_drag_motion_callback (item, event);
2854 cp->line.end_drag (cp);
2858 Editor::start_line_grab_from_regionview (ArdourCanvas::Item* item, GdkEvent* event)
2860 switch (mouse_mode) {
2862 assert(dynamic_cast<AudioRegionView*>(clicked_regionview));
2863 start_line_grab (dynamic_cast<AudioRegionView*>(clicked_regionview)->get_gain_line(), event);
2871 Editor::start_line_grab_from_line (ArdourCanvas::Item* item, GdkEvent* event)
2875 if ((al = reinterpret_cast<AutomationLine*> (item->get_data ("line"))) == 0) {
2876 fatal << _("programming error: line canvas item has no line pointer!") << endmsg;
2880 start_line_grab (al, event);
2884 Editor::start_line_grab (AutomationLine* line, GdkEvent* event)
2888 nframes64_t frame_within_region;
2890 /* need to get x coordinate in terms of parent (TimeAxisItemView)
2894 cx = event->button.x;
2895 cy = event->button.y;
2896 line->parent_group().w2i (cx, cy);
2897 frame_within_region = (nframes64_t) floor (cx * frames_per_unit);
2899 if (!line->control_points_adjacent (frame_within_region, current_line_drag_info.before,
2900 current_line_drag_info.after)) {
2901 /* no adjacent points */
2905 drag_info.item = &line->grab_item();
2906 drag_info.data = line;
2907 drag_info.motion_callback = &Editor::line_drag_motion_callback;
2908 drag_info.finished_callback = &Editor::line_drag_finished_callback;
2910 start_grab (event, fader_cursor);
2912 double fraction = 1.0 - (cy / line->height());
2914 line->start_drag (0, drag_info.grab_frame, fraction);
2916 set_verbose_canvas_cursor (line->get_verbose_cursor_string (fraction),
2917 drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
2918 show_verbose_canvas_cursor ();
2922 Editor::line_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2924 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
2926 double dy = drag_info.current_pointer_y - drag_info.last_pointer_y;
2928 if (event->button.state & Keyboard::SecondaryModifier) {
2932 double cx = drag_info.current_pointer_x;
2933 double cy = drag_info.grab_y + drag_info.cumulative_y_drag + dy;
2935 // calculate zero crossing point. back off by .01 to stay on the
2936 // positive side of zero
2938 double zero_gain_y = (1.0 - ZERO_GAIN_FRACTION) * line->height() - .01;
2939 line->parent_group().i2w(_unused, zero_gain_y);
2941 // make sure we hit zero when passing through
2942 if ((cy < zero_gain_y and (cy - dy) > zero_gain_y)
2943 or (cy > zero_gain_y and (cy - dy) < zero_gain_y)) {
2947 drag_info.cumulative_y_drag = cy - drag_info.grab_y;
2949 line->parent_group().w2i (cx, cy);
2952 cy = min ((double) line->height(), cy);
2955 fraction = 1.0 - (cy / line->height());
2959 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) {
2965 line->line_drag (current_line_drag_info.before, current_line_drag_info.after, fraction, push);
2967 set_verbose_canvas_cursor_text (line->get_verbose_cursor_string (fraction));
2971 Editor::line_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2973 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
2974 line_drag_motion_callback (item, event);
2979 Editor::start_region_grab (ArdourCanvas::Item* item, GdkEvent* event)
2981 if (selection->regions.empty() || clicked_regionview == 0) {
2985 drag_info.copy = false;
2986 drag_info.item = item;
2987 drag_info.data = clicked_regionview;
2989 if (Config->get_edit_mode() == Splice) {
2990 drag_info.motion_callback = &Editor::region_drag_splice_motion_callback;
2991 drag_info.finished_callback = &Editor::region_drag_splice_finished_callback;
2993 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2994 drag_info.finished_callback = &Editor::region_drag_finished_callback;
3000 TimeAxisView* tvp = clicked_trackview;
3001 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
3003 if (tv && tv->is_audio_track()) {
3004 speed = tv->get_diskstream()->speed();
3007 drag_info.last_frame_position = (nframes64_t) (clicked_regionview->region()->position() / speed);
3008 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
3009 drag_info.source_trackview = &clicked_regionview->get_time_axis_view();
3010 drag_info.dest_trackview = drag_info.source_trackview;
3011 // we want a move threshold
3012 drag_info.want_move_threshold = true;
3014 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3016 begin_reversible_command (_("move region(s)"));
3020 Editor::start_region_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
3022 if (selection->regions.empty() || clicked_regionview == 0) {
3026 drag_info.copy = true;
3027 drag_info.item = item;
3028 drag_info.data = clicked_regionview;
3032 TimeAxisView* tv = &clicked_regionview->get_time_axis_view();
3033 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*>(tv);
3036 if (atv && atv->is_audio_track()) {
3037 speed = atv->get_diskstream()->speed();
3040 drag_info.source_trackview = &clicked_regionview->get_time_axis_view();
3041 drag_info.dest_trackview = drag_info.source_trackview;
3042 drag_info.last_frame_position = (nframes64_t) (clicked_regionview->region()->position() / speed);
3043 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
3044 // we want a move threshold
3045 drag_info.want_move_threshold = true;
3046 drag_info.motion_callback = &Editor::region_drag_motion_callback;
3047 drag_info.finished_callback = &Editor::region_drag_finished_callback;
3048 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3052 Editor::start_region_brush_grab (ArdourCanvas::Item* item, GdkEvent* event)
3054 if (selection->regions.empty() || clicked_regionview == 0 || Config->get_edit_mode() == Splice) {
3058 drag_info.copy = false;
3059 drag_info.item = item;
3060 drag_info.data = clicked_regionview;
3061 drag_info.motion_callback = &Editor::region_drag_motion_callback;
3062 drag_info.finished_callback = &Editor::region_drag_finished_callback;
3067 TimeAxisView* tvp = clicked_trackview;
3068 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
3070 if (tv && tv->is_audio_track()) {
3071 speed = tv->get_diskstream()->speed();
3074 drag_info.last_frame_position = (nframes64_t) (clicked_regionview->region()->position() / speed);
3075 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
3076 drag_info.source_trackview = &clicked_regionview->get_time_axis_view();
3077 drag_info.dest_trackview = drag_info.source_trackview;
3078 // we want a move threshold
3079 drag_info.want_move_threshold = true;
3080 drag_info.brushing = true;
3082 begin_reversible_command (_("Drag region brush"));
3086 Editor::possibly_copy_regions_during_grab (GdkEvent* event)
3088 if (drag_info.copy && drag_info.move_threshold_passed && drag_info.want_move_threshold) {
3090 drag_info.want_move_threshold = false; // don't copy again
3092 /* duplicate the regionview(s) and region(s) */
3094 vector<RegionView*> new_regionviews;
3096 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3099 AudioRegionView* arv;
3104 if ((arv = dynamic_cast<AudioRegionView*>(rv)) == 0) {
3105 /* XXX handle MIDI here */
3109 const boost::shared_ptr<const Region> original = arv->region();
3110 boost::shared_ptr<Region> region_copy = RegionFactory::create (original);
3111 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> (region_copy);
3113 nrv = new AudioRegionView (*arv, ar);
3114 nrv->get_canvas_group()->show ();
3116 new_regionviews.push_back (nrv);
3119 if (new_regionviews.empty()) {
3123 /* reset selection to new regionviews. This will not set selection visual status for
3124 these regionviews since they don't belong to a track, so do that by hand too.
3127 selection->set (new_regionviews);
3129 for (vector<RegionView*>::iterator i = new_regionviews.begin(); i != new_regionviews.end(); ++i) {
3130 (*i)->set_selected (true);
3133 /* reset drag_info data to reflect the fact that we are dragging the copies */
3135 drag_info.data = new_regionviews.front();
3137 swap_grab (new_regionviews.front()->get_canvas_group (), 0, event->motion.time);
3142 Editor::check_region_drag_possible (AudioTimeAxisView** tv)
3144 /* Which trackview is this ? */
3146 TimeAxisView* tvp = trackview_by_y_position (drag_info.current_pointer_y);
3147 (*tv) = dynamic_cast<AudioTimeAxisView*>(tvp);
3149 /* The region motion is only processed if the pointer is over
3153 if (!(*tv) || !(*tv)->is_audio_track()) {
3154 /* To make sure we hide the verbose canvas cursor when the mouse is
3155 not held over and audiotrack.
3157 hide_verbose_canvas_cursor ();
3164 struct RegionSelectionByPosition {
3165 bool operator() (RegionView*a, RegionView* b) {
3166 return a->region()->position () < b->region()->position();
3171 Editor::region_drag_splice_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
3173 AudioTimeAxisView* tv;
3175 if (!check_region_drag_possible (&tv)) {
3179 if (!drag_info.move_threshold_passed) {
3185 if (drag_info.current_pointer_x - drag_info.grab_x > 0) {
3191 RegionSelection copy (selection->regions);
3193 RegionSelectionByPosition cmp;
3196 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
3198 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*> (&(*i)->get_time_axis_view());
3204 boost::shared_ptr<Playlist> playlist;
3206 if ((playlist = atv->playlist()) == 0) {
3210 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
3215 if (drag_info.current_pointer_frame < (*i)->region()->last_frame() + 1) {
3219 if (drag_info.current_pointer_frame > (*i)->region()->first_frame()) {
3225 playlist->shuffle ((*i)->region(), dir);
3227 drag_info.grab_x = drag_info.current_pointer_x;
3232 Editor::region_drag_splice_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3237 Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
3241 RegionView* rv = reinterpret_cast<RegionView*> (drag_info.data);
3242 nframes64_t pending_region_position = 0;
3243 int32_t pointer_y_span = 0, canvas_pointer_y_span = 0, original_pointer_order;
3244 int32_t visible_y_high = 0, visible_y_low = 512; //high meaning higher numbered.. not the height on the screen
3245 bool clamp_y_axis = false;
3246 vector<int32_t> height_list(512) ;
3247 vector<int32_t>::iterator j;
3248 AudioTimeAxisView* tv;
3250 possibly_copy_regions_during_grab (event);
3252 if (!check_region_drag_possible (&tv)) {
3256 original_pointer_order = drag_info.dest_trackview->order;
3258 /************************************************************
3260 ************************************************************/
3262 if (drag_info.brushing) {
3263 clamp_y_axis = true;
3268 if ((pointer_y_span = (drag_info.dest_trackview->order - tv->order)) != 0) {
3270 int32_t children = 0, numtracks = 0;
3271 // XXX hard coding track limit, oh my, so very very bad
3272 bitset <1024> tracks (0x00);
3273 /* get a bitmask representing the visible tracks */
3275 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
3276 TimeAxisView *tracklist_timeview;
3277 tracklist_timeview = (*i);
3278 AudioTimeAxisView* atv2 = dynamic_cast<AudioTimeAxisView*>(tracklist_timeview);
3279 list<TimeAxisView*> children_list;
3281 /* zeroes are audio tracks. ones are other types. */
3283 if (!atv2->hidden()) {
3285 if (visible_y_high < atv2->order) {
3286 visible_y_high = atv2->order;
3288 if (visible_y_low > atv2->order) {
3289 visible_y_low = atv2->order;
3292 if (!atv2->is_audio_track()) {
3293 tracks = tracks |= (0x01 << atv2->order);
3296 height_list[atv2->order] = (*i)->current_height();
3298 if ((children_list = atv2->get_child_list()).size() > 0) {
3299 for (list<TimeAxisView*>::iterator j = children_list.begin(); j != children_list.end(); ++j) {
3300 tracks = tracks |= (0x01 << (atv2->order + children));
3301 height_list[atv2->order + children] = (*j)->current_height();
3309 /* find the actual span according to the canvas */
3311 canvas_pointer_y_span = pointer_y_span;
3312 if (drag_info.dest_trackview->order >= tv->order) {
3314 for (y = tv->order; y < drag_info.dest_trackview->order; y++) {
3315 if (height_list[y] == 0 ) {
3316 canvas_pointer_y_span--;
3321 for (y = drag_info.dest_trackview->order;y <= tv->order; y++) {
3322 if ( height_list[y] == 0 ) {
3323 canvas_pointer_y_span++;
3328 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3329 RegionView* rv2 = (*i);
3330 double ix1, ix2, iy1, iy2;
3333 if (rv2->region()->locked()) {
3337 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3338 rv2->get_canvas_group()->i2w (ix1, iy1);
3339 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3340 RouteTimeAxisView* atv2 = dynamic_cast<RouteTimeAxisView*>(tvp2);
3342 if (atv2->order != original_pointer_order) {
3343 /* this isn't the pointer track */
3345 if (canvas_pointer_y_span > 0) {
3347 /* moving up the canvas */
3348 if ((atv2->order - canvas_pointer_y_span) >= visible_y_low) {
3350 int32_t visible_tracks = 0;
3351 while (visible_tracks < canvas_pointer_y_span ) {
3354 while (height_list[atv2->order - (visible_tracks - n)] == 0) {
3355 /* we're passing through a hidden track */
3360 if (tracks[atv2->order - (canvas_pointer_y_span - n)] != 0x00) {
3361 clamp_y_axis = true;
3365 clamp_y_axis = true;
3368 } else if (canvas_pointer_y_span < 0) {
3370 /*moving down the canvas*/
3372 if ((atv2->order - (canvas_pointer_y_span - n)) <= visible_y_high) { // we will overflow
3375 int32_t visible_tracks = 0;
3377 while (visible_tracks > canvas_pointer_y_span ) {
3380 while (height_list[atv2->order - (visible_tracks - n)] == 0) {
3384 if ( tracks[atv2->order - ( canvas_pointer_y_span - n)] != 0x00) {
3385 clamp_y_axis = true;
3390 clamp_y_axis = true;
3396 /* this is the pointer's track */
3397 if ((atv2->order - pointer_y_span) > visible_y_high) { // we will overflow
3398 clamp_y_axis = true;
3399 } else if ((atv2->order - pointer_y_span) < visible_y_low) { // we will underflow
3400 clamp_y_axis = true;
3408 } else if (drag_info.dest_trackview == tv) {
3409 clamp_y_axis = true;
3413 if (!clamp_y_axis) {
3414 drag_info.dest_trackview = tv;
3417 /************************************************************
3419 ************************************************************/
3421 /* compute the amount of pointer motion in frames, and where
3422 the region would be if we moved it by that much.
3425 if ( drag_info.move_threshold_passed ) {
3427 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
3429 nframes64_t sync_frame;
3430 nframes64_t sync_offset;
3433 pending_region_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
3435 sync_offset = rv->region()->sync_offset (sync_dir);
3437 /* we don't handle a sync point that lies before zero.
3439 if (sync_dir >= 0 || (sync_dir < 0 && pending_region_position >= sync_offset)) {
3440 sync_frame = pending_region_position + (sync_dir*sync_offset);
3442 /* we snap if the snap modifier is not enabled.
3445 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3446 snap_to (sync_frame);
3449 pending_region_position = rv->region()->adjust_to_sync (sync_frame);
3452 pending_region_position = drag_info.last_frame_position;
3456 pending_region_position = 0;
3459 if (pending_region_position > max_frames - rv->region()->length()) {
3460 pending_region_position = drag_info.last_frame_position;
3463 // printf ("3: pending_region_position= %lu %lu\n", pending_region_position, drag_info.last_frame_position );
3465 bool x_move_allowed;
3467 if (Config->get_edit_mode() == Lock) {
3468 if (drag_info.copy) {
3469 x_move_allowed = !drag_info.x_constrained;
3471 /* in locked edit mode, reverse the usual meaning of x_constrained */
3472 x_move_allowed = drag_info.x_constrained;
3475 x_move_allowed = !drag_info.x_constrained;
3478 if ( pending_region_position != drag_info.last_frame_position && x_move_allowed ) {
3480 /* now compute the canvas unit distance we need to move the regionview
3481 to make it appear at the new location.
3484 if (pending_region_position > drag_info.last_frame_position) {
3485 x_delta = ((double) (pending_region_position - drag_info.last_frame_position) / frames_per_unit);
3487 x_delta = -((double) (drag_info.last_frame_position - pending_region_position) / frames_per_unit);
3490 drag_info.last_frame_position = pending_region_position;
3497 /* threshold not passed */
3502 /*************************************************************
3504 ************************************************************/
3506 if (x_delta == 0 && (pointer_y_span == 0)) {
3507 /* haven't reached next snap point, and we're not switching
3508 trackviews. nothing to do.
3515 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3517 RegionView* rv2 = (*i);
3519 // If any regionview is at zero, we need to know so we can stop further leftward motion.
3521 double ix1, ix2, iy1, iy2;
3522 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3523 rv2->get_canvas_group()->i2w (ix1, iy1);
3532 /*************************************************************
3534 ************************************************************/
3538 if (drag_info.first_move) {
3539 if (drag_info.move_threshold_passed) {
3550 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
3551 const list<RegionView*>& layered_regions = selection->regions.by_layer();
3553 for (list<RegionView*>::const_iterator i = layered_regions.begin(); i != layered_regions.end(); ++i) {
3555 RegionView* rv = (*i);
3556 double ix1, ix2, iy1, iy2;
3557 int32_t temp_pointer_y_span = pointer_y_span;
3559 if (rv->region()->locked()) {
3563 /* get item BBox, which will be relative to parent. so we have
3564 to query on a child, then convert to world coordinates using
3568 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3569 rv->get_canvas_group()->i2w (ix1, iy1);
3570 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3571 AudioTimeAxisView* canvas_atv = dynamic_cast<AudioTimeAxisView*>(tvp2);
3572 AudioTimeAxisView* temp_atv;
3574 if ((pointer_y_span != 0) && !clamp_y_axis) {
3577 for (j = height_list.begin(); j!= height_list.end(); j++) {
3578 if (x == canvas_atv->order) {
3579 /* we found the track the region is on */
3580 if (x != original_pointer_order) {
3581 /*this isn't from the same track we're dragging from */
3582 temp_pointer_y_span = canvas_pointer_y_span;
3584 while (temp_pointer_y_span > 0) {
3585 /* we're moving up canvas-wise,
3586 so we need to find the next track height
3588 if (j != height_list.begin()) {
3591 if (x != original_pointer_order) {
3592 /* we're not from the dragged track, so ignore hidden tracks. */
3594 temp_pointer_y_span++;
3598 temp_pointer_y_span--;
3601 while (temp_pointer_y_span < 0) {
3603 if (x != original_pointer_order) {
3605 temp_pointer_y_span--;
3609 if (j != height_list.end()) {
3612 temp_pointer_y_span++;
3614 /* find out where we'll be when we move and set height accordingly */
3616 tvp2 = trackview_by_y_position (iy1 + y_delta);
3617 temp_atv = dynamic_cast<AudioTimeAxisView*>(tvp2);
3618 rv->set_height (temp_atv->current_height());
3620 /* if you un-comment the following, the region colours will follow the track colours whilst dragging,
3621 personally, i think this can confuse things, but never mind.
3624 //const GdkColor& col (temp_atv->view->get_region_color());
3625 //rv->set_color (const_cast<GdkColor&>(col));
3633 /* prevent the regionview from being moved to before
3634 the zero position on the canvas.
3639 if (-x_delta > ix1) {
3642 } else if ((x_delta > 0) && (rv->region()->last_frame() > max_frames - x_delta)) {
3643 x_delta = max_frames - rv->region()->last_frame();
3647 if (drag_info.first_move) {
3649 /* hide any dependent views */
3651 rv->get_time_axis_view().hide_dependent_views (*rv);
3653 /* this is subtle. raising the regionview itself won't help,
3654 because raise_to_top() just puts the item on the top of
3655 its parent's stack. so, we need to put the trackview canvas_display group
3656 on the top, since its parent is the whole canvas.
3659 rv->get_canvas_group()->raise_to_top();
3660 rv->get_time_axis_view().canvas_display->raise_to_top();
3661 cursor_group->raise_to_top();
3662 rv->fake_set_opaque (true);
3665 if (drag_info.brushing) {
3666 mouse_brush_insert_region (rv, pending_region_position);
3668 rv->move (x_delta, y_delta);
3671 } /* foreach region */
3675 if (drag_info.first_move && drag_info.move_threshold_passed) {
3676 cursor_group->raise_to_top();
3677 drag_info.first_move = false;
3680 if (x_delta != 0 && !drag_info.brushing) {
3681 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3686 Editor::region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3688 bool nocommit = true;
3689 vector<RegionView*> copies;
3690 RouteTimeAxisView* source_tv;
3691 boost::shared_ptr<Diskstream> ds;
3692 boost::shared_ptr<Playlist> from_playlist;
3693 vector<RegionView*> new_selection;
3694 typedef set<boost::shared_ptr<Playlist> > PlaylistSet;
3695 PlaylistSet modified_playlists;
3696 pair<PlaylistSet::iterator,bool> insert_result;
3698 /* first_move is set to false if the regionview has been moved in the
3702 if (drag_info.first_move) {
3709 /* The regionview has been moved at some stage during the grab so we need
3710 to account for any mouse movement between this event and the last one.
3713 region_drag_motion_callback (item, event);
3715 if (Config->get_edit_mode() == Splice && !pre_drag_region_selection.empty()) {
3716 selection->set (pre_drag_region_selection);
3717 pre_drag_region_selection.clear ();
3720 if (drag_info.brushing) {
3721 /* all changes were made during motion event handlers */
3723 if (drag_info.copy) {
3724 for (list<RegionView*>::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
3725 copies.push_back (*i);
3734 /* reverse this here so that we have the correct logic to finalize
3738 if (Config->get_edit_mode() == Lock && !drag_info.copy) {
3739 drag_info.x_constrained = !drag_info.x_constrained;
3742 if (drag_info.copy) {
3743 if (drag_info.x_constrained) {
3744 op_string = _("fixed time region copy");
3746 op_string = _("region copy");
3749 if (drag_info.x_constrained) {
3750 op_string = _("fixed time region drag");
3752 op_string = _("region drag");
3756 begin_reversible_command (op_string);
3758 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ) {
3760 RegionView* rv = (*i);
3761 double ix1, ix2, iy1, iy2;
3762 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3763 rv->get_canvas_group()->i2w (ix1, iy1);
3764 TimeAxisView* dest_tv = trackview_by_y_position (iy1);
3765 AudioTimeAxisView* dest_atv = dynamic_cast<AudioTimeAxisView*>(dest_tv);
3767 bool changed_tracks;
3768 bool changed_position;
3771 if (rv->region()->locked()) {
3776 /* adjust for track speed */
3780 if (dest_atv && dest_atv->get_diskstream()) {
3781 speed = dest_atv->get_diskstream()->speed();
3784 changed_position = (drag_info.last_frame_position != (nframes64_t) (rv->region()->position()/speed));
3785 changed_tracks = (dest_tv != &rv->get_time_axis_view());
3787 if (changed_position && !drag_info.x_constrained) {
3788 where = (nframes64_t) (unit_to_frame (ix1) * speed);
3790 where = rv->region()->position();
3793 /* undo the previous hide_dependent_views so that xfades don't
3794 disappear on copying regions
3797 rv->get_time_axis_view().reveal_dependent_views (*rv);
3799 boost::shared_ptr<Region> new_region;
3801 if (drag_info.copy) {
3802 /* we already made a copy */
3803 new_region = rv->region();
3805 new_region = RegionFactory::create (rv->region());
3808 if (changed_tracks || drag_info.copy) {
3810 boost::shared_ptr<Playlist> to_playlist = dest_atv->playlist();
3812 latest_regionviews.clear ();
3814 sigc::connection c = dest_atv->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3816 insert_result = modified_playlists.insert (to_playlist);
3817 if (insert_result.second) {
3818 session->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
3821 to_playlist->add_region (new_region, where);
3825 if (!latest_regionviews.empty()) {
3826 // XXX why just the first one ? we only expect one
3827 dest_atv->reveal_dependent_views (*latest_regionviews.front());
3828 new_selection.push_back (latest_regionviews.front());
3833 /* just change the model */
3835 boost::shared_ptr<Playlist> playlist = dest_atv->playlist();
3837 insert_result = modified_playlists.insert (playlist);
3838 if (insert_result.second) {
3839 session->add_command (new MementoCommand<Playlist>(*playlist, &playlist->get_state(), 0));
3842 rv->region()->set_position (where, (void*) this);
3845 if (changed_tracks && !drag_info.copy) {
3847 /* get the playlist where this drag started. we can't use rv->region()->playlist()
3848 because we may have copied the region and it has not been attached to a playlist.
3851 assert ((source_tv = dynamic_cast<RouteTimeAxisView*> (&rv->get_time_axis_view())));
3852 assert ((ds = source_tv->get_diskstream()));
3853 assert ((from_playlist = ds->playlist()));
3855 /* moved to a different audio track, without copying */
3857 /* the region that used to be in the old playlist is not
3858 moved to the new one - we use a copy of it. as a result,
3859 any existing editor for the region should no longer be
3863 rv->hide_region_editor();
3864 rv->fake_set_opaque (false);
3866 /* remove the region from the old playlist */
3868 insert_result = modified_playlists.insert (from_playlist);
3869 if (insert_result.second) {
3870 session->add_command (new MementoCommand<Playlist>(*from_playlist, &from_playlist->get_state(), 0));
3873 from_playlist->remove_region ((rv->region()));
3875 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
3876 was selected in all of them, then removing it from a playlist will have removed all
3877 trace of it from the selection (i.e. there were N regions selected, we removed 1,
3878 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
3879 corresponding regionview, and the selection is now empty).
3881 this could have invalidated any and all iterators into the region selection.
3883 the heuristic we use here is: if the region selection is empty, break out of the loop
3884 here. if the region selection is not empty, then restart the loop because we know that
3885 we must have removed at least the region(view) we've just been working on as well as any
3886 that we processed on previous iterations.
3888 EXCEPT .... if we are doing a copy drag, then the selection hasn't been modified and
3889 we can just iterate.
3892 if (selection->regions.empty()) {
3895 i = selection->regions.by_layer().begin();
3902 if (drag_info.copy) {
3903 copies.push_back (rv);
3908 if (new_selection.empty()) {
3909 if (drag_info.copy) {
3910 /* the region(view)s that are selected and being dragged around
3911 are copies and do not belong to any track. remove them
3912 from the selection right here.
3914 selection->clear_regions();
3917 /* this will clear any existing selection that would have been
3918 cleared in the other clause above
3920 selection->set (new_selection);
3925 for (set<boost::shared_ptr<Playlist> >::iterator p = modified_playlists.begin(); p != modified_playlists.end(); ++p) {
3926 session->add_command (new MementoCommand<Playlist>(*(*p), 0, &(*p)->get_state()));
3928 commit_reversible_command ();
3931 for (vector<RegionView*>::iterator x = copies.begin(); x != copies.end(); ++x) {
3937 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
3939 /* Either add to or set the set the region selection, unless
3940 this is an alignment click (control used)
3943 if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
3944 TimeAxisView* tv = &rv.get_time_axis_view();
3945 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*>(tv);
3947 if (atv && atv->is_audio_track()) {
3948 speed = atv->get_diskstream()->speed();
3951 nframes64_t where = get_preferred_edit_position();
3955 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
3957 align_region (rv.region(), SyncPoint, (nframes64_t) (where * speed));
3959 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
3961 align_region (rv.region(), End, (nframes64_t) (where * speed));
3965 align_region (rv.region(), Start, (nframes64_t) (where * speed));
3972 Editor::show_verbose_time_cursor (nframes64_t frame, double offset, double xpos, double ypos)
3978 nframes64_t frame_rate;
3987 if (Profile->get_sae() || Profile->get_small_screen()) {
3988 m = ARDOUR_UI::instance()->primary_clock.mode();
3990 m = ARDOUR_UI::instance()->secondary_clock.mode();
3994 case AudioClock::BBT:
3995 session->bbt_time (frame, bbt);
3996 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, bbt.bars, bbt.beats, bbt.ticks);
3999 case AudioClock::SMPTE:
4000 session->smpte_time (frame, smpte);
4001 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
4004 case AudioClock::MinSec:
4005 /* XXX this is copied from show_verbose_duration_cursor() */
4006 frame_rate = session->frame_rate();
4007 hours = frame / (frame_rate * 3600);
4008 frame = frame % (frame_rate * 3600);
4009 mins = frame / (frame_rate * 60);
4010 frame = frame % (frame_rate * 60);
4011 secs = (float) frame / (float) frame_rate;
4012 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
4016 snprintf (buf, sizeof(buf), "%" PRIi64, frame);
4020 if (xpos >= 0 && ypos >=0) {
4021 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
4024 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
4026 show_verbose_canvas_cursor ();
4030 Editor::show_verbose_duration_cursor (nframes64_t start, nframes64_t end, double offset, double xpos, double ypos)
4037 nframes64_t distance, frame_rate;
4039 Meter meter_at_start(session->tempo_map().meter_at(start));
4047 if (Profile->get_sae() || Profile->get_small_screen()) {
4048 m = ARDOUR_UI::instance()->primary_clock.mode ();
4050 m = ARDOUR_UI::instance()->secondary_clock.mode ();
4054 case AudioClock::BBT:
4055 session->bbt_time (start, sbbt);
4056 session->bbt_time (end, ebbt);
4059 /* XXX this computation won't work well if the
4060 user makes a selection that spans any meter changes.
4063 ebbt.bars -= sbbt.bars;
4064 if (ebbt.beats >= sbbt.beats) {
4065 ebbt.beats -= sbbt.beats;
4068 ebbt.beats = int(meter_at_start.beats_per_bar()) + ebbt.beats - sbbt.beats;
4070 if (ebbt.ticks >= sbbt.ticks) {
4071 ebbt.ticks -= sbbt.ticks;
4074 ebbt.ticks = int(Meter::ticks_per_beat) + ebbt.ticks - sbbt.ticks;
4077 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, ebbt.bars, ebbt.beats, ebbt.ticks);
4080 case AudioClock::SMPTE:
4081 session->smpte_duration (end - start, smpte);
4082 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
4085 case AudioClock::MinSec:
4086 /* XXX this stuff should be elsewhere.. */
4087 distance = end - start;
4088 frame_rate = session->frame_rate();
4089 hours = distance / (frame_rate * 3600);
4090 distance = distance % (frame_rate * 3600);
4091 mins = distance / (frame_rate * 60);
4092 distance = distance % (frame_rate * 60);
4093 secs = (float) distance / (float) frame_rate;
4094 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
4098 snprintf (buf, sizeof(buf), "%" PRIi64, end - start);
4102 if (xpos >= 0 && ypos >=0) {
4103 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
4106 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
4109 show_verbose_canvas_cursor ();
4113 Editor::collect_new_region_view (RegionView* rv)
4115 latest_regionviews.push_back (rv);
4119 Editor::start_selection_grab (ArdourCanvas::Item* item, GdkEvent* event)
4121 if (clicked_regionview == 0) {
4125 /* lets try to create new Region for the selection */
4127 vector<boost::shared_ptr<AudioRegion> > new_regions;
4128 create_region_from_selection (new_regions);
4130 if (new_regions.empty()) {
4134 /* XXX fix me one day to use all new regions */
4136 boost::shared_ptr<Region> region (new_regions.front());
4138 /* add it to the current stream/playlist.
4140 tricky: the streamview for the track will add a new regionview. we will
4141 catch the signal it sends when it creates the regionview to
4142 set the regionview we want to then drag.
4145 latest_regionviews.clear();
4146 sigc::connection c = clicked_audio_trackview->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
4148 /* A selection grab currently creates two undo/redo operations, one for
4149 creating the new region and another for moving it.
4152 begin_reversible_command (_("selection grab"));
4154 boost::shared_ptr<Playlist> playlist = clicked_trackview->playlist();
4156 XMLNode *before = &(playlist->get_state());
4157 clicked_trackview->playlist()->add_region (region, selection->time[clicked_selection].start);
4158 XMLNode *after = &(playlist->get_state());
4159 session->add_command(new MementoCommand<Playlist>(*playlist, before, after));
4161 commit_reversible_command ();
4165 if (latest_regionviews.empty()) {
4166 /* something went wrong */
4170 /* we need to deselect all other regionviews, and select this one
4171 i'm ignoring undo stuff, because the region creation will take care of it
4173 selection->set (latest_regionviews);
4175 drag_info.item = latest_regionviews.front()->get_canvas_group();
4176 drag_info.data = latest_regionviews.front();
4177 drag_info.motion_callback = &Editor::region_drag_motion_callback;
4178 drag_info.finished_callback = &Editor::region_drag_finished_callback;
4182 drag_info.source_trackview = clicked_trackview;
4183 drag_info.dest_trackview = drag_info.source_trackview;
4184 drag_info.last_frame_position = latest_regionviews.front()->region()->position();
4185 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
4187 show_verbose_time_cursor (drag_info.last_frame_position, 10);
4191 Editor::cancel_selection ()
4193 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4194 (*i)->hide_selection ();
4196 selection->clear ();
4197 clicked_selection = 0;
4201 Editor::start_selection_op (ArdourCanvas::Item* item, GdkEvent* event, SelectionOp op)
4203 nframes64_t start = 0;
4204 nframes64_t end = 0;
4210 drag_info.item = item;
4211 drag_info.motion_callback = &Editor::drag_selection;
4212 drag_info.finished_callback = &Editor::end_selection_op;
4217 case CreateSelection:
4218 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
4219 drag_info.copy = true;
4221 drag_info.copy = false;
4223 start_grab (event, selector_cursor);
4226 case SelectionStartTrim:
4227 if (clicked_trackview) {
4228 clicked_trackview->order_selection_trims (item, true);
4230 start_grab (event, trimmer_cursor);
4231 start = selection->time[clicked_selection].start;
4232 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
4235 case SelectionEndTrim:
4236 if (clicked_trackview) {
4237 clicked_trackview->order_selection_trims (item, false);
4239 start_grab (event, trimmer_cursor);
4240 end = selection->time[clicked_selection].end;
4241 drag_info.pointer_frame_offset = drag_info.grab_frame - end;
4245 start = selection->time[clicked_selection].start;
4247 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
4251 if (selection_op == SelectionMove) {
4252 show_verbose_time_cursor(start, 10);
4254 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4259 Editor::drag_selection (ArdourCanvas::Item* item, GdkEvent* event)
4261 nframes64_t start = 0;
4262 nframes64_t end = 0;
4264 nframes64_t pending_position;
4266 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
4267 pending_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
4269 pending_position = 0;
4272 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4273 snap_to (pending_position);
4276 /* only alter selection if the current frame is
4277 different from the last frame position (adjusted)
4280 if (pending_position == drag_info.last_pointer_frame) return;
4282 switch (selection_op) {
4283 case CreateSelection:
4285 if (drag_info.first_move) {
4286 snap_to (drag_info.grab_frame);
4289 if (pending_position < drag_info.grab_frame) {
4290 start = pending_position;
4291 end = drag_info.grab_frame;
4293 end = pending_position;
4294 start = drag_info.grab_frame;
4297 /* first drag: Either add to the selection
4298 or create a new selection->
4301 if (drag_info.first_move) {
4303 begin_reversible_command (_("range selection"));
4305 if (drag_info.copy) {
4306 /* adding to the selection */
4307 clicked_selection = selection->add (start, end);
4308 drag_info.copy = false;
4310 /* new selection-> */
4311 clicked_selection = selection->set (clicked_trackview, start, end);
4316 case SelectionStartTrim:
4318 if (drag_info.first_move) {
4319 begin_reversible_command (_("trim selection start"));
4322 start = selection->time[clicked_selection].start;
4323 end = selection->time[clicked_selection].end;
4325 if (pending_position > end) {
4328 start = pending_position;
4332 case SelectionEndTrim:
4334 if (drag_info.first_move) {
4335 begin_reversible_command (_("trim selection end"));
4338 start = selection->time[clicked_selection].start;
4339 end = selection->time[clicked_selection].end;
4341 if (pending_position < start) {
4344 end = pending_position;
4351 if (drag_info.first_move) {
4352 begin_reversible_command (_("move selection"));
4355 start = selection->time[clicked_selection].start;
4356 end = selection->time[clicked_selection].end;
4358 length = end - start;
4360 start = pending_position;
4363 end = start + length;
4368 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
4369 start_canvas_autoscroll (1, 0);
4373 selection->replace (clicked_selection, start, end);
4376 drag_info.last_pointer_frame = pending_position;
4377 drag_info.first_move = false;
4379 if (selection_op == SelectionMove) {
4380 show_verbose_time_cursor(start, 10);
4382 show_verbose_time_cursor(pending_position, 10);
4387 Editor::end_selection_op (ArdourCanvas::Item* item, GdkEvent* event)
4389 if (!drag_info.first_move) {
4390 drag_selection (item, event);
4391 /* XXX this is not object-oriented programming at all. ick */
4392 if (selection->time.consolidate()) {
4393 selection->TimeChanged ();
4395 commit_reversible_command ();
4397 /* just a click, no pointer movement.*/
4399 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
4401 selection->clear_time();
4406 /* XXX what happens if its a music selection? */
4407 session->set_audio_range (selection->time);
4408 stop_canvas_autoscroll ();
4412 Editor::start_trim (ArdourCanvas::Item* item, GdkEvent* event)
4415 TimeAxisView* tvp = clicked_trackview;
4416 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
4418 if (tv && tv->is_audio_track()) {
4419 speed = tv->get_diskstream()->speed();
4422 nframes64_t region_start = (nframes64_t) (clicked_regionview->region()->position() / speed);
4423 nframes64_t region_end = (nframes64_t) (clicked_regionview->region()->last_frame() / speed);
4424 nframes64_t region_length = (nframes64_t) (clicked_regionview->region()->length() / speed);
4426 //drag_info.item = clicked_regionview->get_name_highlight();
4427 drag_info.item = item;
4428 drag_info.motion_callback = &Editor::trim_motion_callback;
4429 drag_info.finished_callback = &Editor::trim_finished_callback;
4431 start_grab (event, trimmer_cursor);
4433 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
4434 trim_op = ContentsTrim;
4436 /* These will get overridden for a point trim.*/
4437 if (drag_info.current_pointer_frame < (region_start + region_length/2)) {
4438 /* closer to start */
4439 trim_op = StartTrim;
4440 } else if (drag_info.current_pointer_frame > (region_end - region_length/2)) {
4448 show_verbose_time_cursor(region_start, 10);
4451 show_verbose_time_cursor(region_end, 10);
4454 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4460 Editor::trim_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
4462 RegionView* rv = clicked_regionview;
4463 nframes64_t frame_delta = 0;
4464 bool left_direction;
4465 bool obey_snap = !Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier());
4467 /* snap modifier works differently here..
4468 its' current state has to be passed to the
4469 various trim functions in order to work properly
4473 TimeAxisView* tvp = clicked_trackview;
4474 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4475 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
4477 if (tv && tv->is_audio_track()) {
4478 speed = tv->get_diskstream()->speed();
4481 if (drag_info.last_pointer_frame > drag_info.current_pointer_frame) {
4482 left_direction = true;
4484 left_direction = false;
4488 snap_to (drag_info.current_pointer_frame);
4491 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
4495 if (drag_info.first_move) {
4501 trim_type = "Region start trim";
4504 trim_type = "Region end trim";
4507 trim_type = "Region content trim";
4511 begin_reversible_command (trim_type);
4513 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4514 (*i)->fake_set_opaque(false);
4515 (*i)->region()->freeze ();
4517 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
4519 arv->temporarily_hide_envelope ();
4521 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4522 insert_result = motion_frozen_playlists.insert (pl);
4523 if (insert_result.second) {
4524 session->add_command(new MementoCommand<Playlist>(*pl, &pl->get_state(), 0));
4529 if (left_direction) {
4530 frame_delta = (drag_info.last_pointer_frame - drag_info.current_pointer_frame);
4532 frame_delta = (drag_info.current_pointer_frame - drag_info.last_pointer_frame);
4537 if ((left_direction == false) && (drag_info.current_pointer_frame <= rv->region()->first_frame()/speed)) {
4540 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4541 single_start_trim (**i, frame_delta, left_direction, obey_snap);
4547 if ((left_direction == true) && (drag_info.current_pointer_frame > (nframes64_t) (rv->region()->last_frame()/speed))) {
4550 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4551 single_end_trim (**i, frame_delta, left_direction, obey_snap);
4558 bool swap_direction = false;
4560 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
4561 swap_direction = true;
4564 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4565 i != selection->regions.by_layer().end(); ++i)
4567 single_contents_trim (**i, frame_delta, left_direction, swap_direction, obey_snap);
4575 show_verbose_time_cursor((nframes64_t) (rv->region()->position()/speed), 10);
4578 show_verbose_time_cursor((nframes64_t) (rv->region()->last_frame()/speed), 10);
4581 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4585 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4586 drag_info.first_move = false;
4590 Editor::single_contents_trim (RegionView& rv, nframes64_t frame_delta, bool left_direction, bool swap_direction, bool obey_snap)
4592 boost::shared_ptr<Region> region (rv.region());
4594 if (region->locked()) {
4598 nframes64_t new_bound;
4601 TimeAxisView* tvp = clicked_trackview;
4602 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4604 if (tv && tv->is_audio_track()) {
4605 speed = tv->get_diskstream()->speed();
4608 if (left_direction) {
4609 if (swap_direction) {
4610 new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
4612 new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
4615 if (swap_direction) {
4616 new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
4618 new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
4623 snap_to (new_bound);
4625 region->trim_start ((nframes64_t) (new_bound * speed), this);
4626 rv.region_changed (StartChanged);
4630 Editor::single_start_trim (RegionView& rv, nframes64_t frame_delta, bool left_direction, bool obey_snap)
4632 boost::shared_ptr<Region> region (rv.region());
4634 if (region->locked()) {
4638 nframes64_t new_bound;
4641 TimeAxisView* tvp = clicked_trackview;
4642 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
4644 if (tv && tv->is_audio_track()) {
4645 speed = tv->get_diskstream()->speed();
4648 if (left_direction) {
4649 new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
4651 new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
4655 snap_to (new_bound, (left_direction ? 0 : 1));
4658 region->trim_front ((nframes64_t) (new_bound * speed), this);
4660 rv.region_changed (Change (LengthChanged|PositionChanged|StartChanged));
4664 Editor::single_end_trim (RegionView& rv, nframes64_t frame_delta, bool left_direction, bool obey_snap)
4666 boost::shared_ptr<Region> region (rv.region());
4668 if (region->locked()) {
4672 nframes64_t new_bound;
4675 TimeAxisView* tvp = clicked_trackview;
4676 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
4678 if (tv && tv->is_audio_track()) {
4679 speed = tv->get_diskstream()->speed();
4682 if (left_direction) {
4683 new_bound = (nframes64_t) ((region->last_frame() + 1)/speed) - frame_delta;
4685 new_bound = (nframes64_t) ((region->last_frame() + 1)/speed) + frame_delta;
4689 snap_to (new_bound);
4691 region->trim_end ((nframes64_t) (new_bound * speed), this);
4692 rv.region_changed (LengthChanged);
4696 Editor::trim_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
4698 if (!drag_info.first_move) {
4699 trim_motion_callback (item, event);
4701 if (!selection->selected (clicked_regionview)) {
4702 thaw_region_after_trim (*clicked_regionview);
4705 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4706 i != selection->regions.by_layer().end(); ++i)
4708 thaw_region_after_trim (**i);
4709 (*i)->fake_set_opaque (true);
4713 for (set<boost::shared_ptr<Playlist> >::iterator p = motion_frozen_playlists.begin(); p != motion_frozen_playlists.end(); ++p) {
4715 session->add_command (new MementoCommand<Playlist>(*(*p).get(), 0, &(*p)->get_state()));
4718 motion_frozen_playlists.clear ();
4720 commit_reversible_command();
4722 /* no mouse movement */
4728 Editor::point_trim (GdkEvent* event)
4730 RegionView* rv = clicked_regionview;
4731 nframes64_t new_bound = drag_info.current_pointer_frame;
4733 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4734 snap_to (new_bound);
4737 /* Choose action dependant on which button was pressed */
4738 switch (event->button.button) {
4740 trim_op = StartTrim;
4741 begin_reversible_command (_("Start point trim"));
4743 if (selection->selected (rv)) {
4745 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4746 i != selection->regions.by_layer().end(); ++i)
4748 if (!(*i)->region()->locked()) {
4749 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4750 XMLNode &before = pl->get_state();
4751 (*i)->region()->trim_front (new_bound, this);
4752 XMLNode &after = pl->get_state();
4753 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4759 if (!rv->region()->locked()) {
4760 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
4761 XMLNode &before = pl->get_state();
4762 rv->region()->trim_front (new_bound, this);
4763 XMLNode &after = pl->get_state();
4764 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4768 commit_reversible_command();
4773 begin_reversible_command (_("End point trim"));
4775 if (selection->selected (rv)) {
4777 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
4779 if (!(*i)->region()->locked()) {
4780 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4781 XMLNode &before = pl->get_state();
4782 (*i)->region()->trim_end (new_bound, this);
4783 XMLNode &after = pl->get_state();
4784 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4790 if (!rv->region()->locked()) {
4791 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
4792 XMLNode &before = pl->get_state();
4793 rv->region()->trim_end (new_bound, this);
4794 XMLNode &after = pl->get_state();
4795 session->add_command (new MementoCommand<Playlist>(*pl.get(), &before, &after));
4799 commit_reversible_command();
4808 Editor::thaw_region_after_trim (RegionView& rv)
4810 boost::shared_ptr<Region> region (rv.region());
4812 if (region->locked()) {
4816 region->thaw (_("trimmed region"));
4817 XMLNode &after = region->playlist()->get_state();
4818 session->add_command (new MementoCommand<Playlist>(*(region->playlist()), 0, &after));
4820 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(&rv);
4822 arv->unhide_envelope ();
4826 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* event)
4831 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
4832 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
4836 Location* location = find_location_from_marker (marker, is_start);
4837 location->set_hidden (true, this);
4842 Editor::start_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event, RangeMarkerOp op)
4848 drag_info.item = item;
4849 drag_info.motion_callback = &Editor::drag_range_markerbar_op;
4850 drag_info.finished_callback = &Editor::end_range_markerbar_op;
4852 range_marker_op = op;
4854 if (!temp_location) {
4855 temp_location = new Location;
4859 case CreateRangeMarker:
4860 case CreateTransportMarker:
4861 case CreateCDMarker:
4863 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
4864 drag_info.copy = true;
4866 drag_info.copy = false;
4868 start_grab (event, selector_cursor);
4872 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4877 Editor::drag_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4879 nframes64_t start = 0;
4880 nframes64_t end = 0;
4881 ArdourCanvas::SimpleRect *crect;
4883 switch (range_marker_op) {
4884 case CreateRangeMarker:
4885 crect = range_bar_drag_rect;
4887 case CreateTransportMarker:
4888 crect = transport_bar_drag_rect;
4890 case CreateCDMarker:
4891 crect = cd_marker_bar_drag_rect;
4894 cerr << "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()" << endl;
4899 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4900 snap_to (drag_info.current_pointer_frame);
4903 /* only alter selection if the current frame is
4904 different from the last frame position.
4907 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4909 switch (range_marker_op) {
4910 case CreateRangeMarker:
4911 case CreateTransportMarker:
4912 case CreateCDMarker:
4913 if (drag_info.first_move) {
4914 snap_to (drag_info.grab_frame);
4917 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4918 start = drag_info.current_pointer_frame;
4919 end = drag_info.grab_frame;
4921 end = drag_info.current_pointer_frame;
4922 start = drag_info.grab_frame;
4925 /* first drag: Either add to the selection
4926 or create a new selection.
4929 if (drag_info.first_move) {
4931 temp_location->set (start, end);
4935 update_marker_drag_item (temp_location);
4936 range_marker_drag_rect->show();
4937 range_marker_drag_rect->raise_to_top();
4943 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
4944 start_canvas_autoscroll (1, 0);
4948 temp_location->set (start, end);
4950 double x1 = frame_to_pixel (start);
4951 double x2 = frame_to_pixel (end);
4952 crect->property_x1() = x1;
4953 crect->property_x2() = x2;
4955 update_marker_drag_item (temp_location);
4958 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4959 drag_info.first_move = false;
4961 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4966 Editor::end_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4968 Location * newloc = 0;
4972 if (!drag_info.first_move) {
4973 drag_range_markerbar_op (item, event);
4975 switch (range_marker_op) {
4976 case CreateRangeMarker:
4977 case CreateCDMarker:
4979 begin_reversible_command (_("new range marker"));
4980 XMLNode &before = session->locations()->get_state();
4981 session->locations()->next_available_name(rangename,"unnamed");
4982 if (range_marker_op == CreateCDMarker) {
4983 flags = Location::IsRangeMarker|Location::IsCDMarker;
4984 cd_marker_bar_drag_rect->hide();
4987 flags = Location::IsRangeMarker;
4988 range_bar_drag_rect->hide();
4990 newloc = new Location(temp_location->start(), temp_location->end(), rangename, (Location::Flags) flags);
4991 session->locations()->add (newloc, true);
4992 XMLNode &after = session->locations()->get_state();
4993 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
4994 commit_reversible_command ();
4996 range_marker_drag_rect->hide();
5000 case CreateTransportMarker:
5001 // popup menu to pick loop or punch
5002 new_transport_marker_context_menu (&event->button, item);
5007 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
5009 if (Keyboard::no_modifier_keys_pressed (&event->button) && range_marker_op != CreateCDMarker) {
5014 start = session->locations()->first_mark_before (drag_info.grab_frame);
5015 end = session->locations()->first_mark_after (drag_info.grab_frame);
5017 if (end == max_frames) {
5018 end = session->current_end_frame ();
5022 start = session->current_start_frame ();
5025 switch (mouse_mode) {
5027 /* find the two markers on either side and then make the selection from it */
5028 select_all_within (start, end, 0.0f, FLT_MAX, track_views, Selection::Set);
5032 /* find the two markers on either side of the click and make the range out of it */
5033 selection->set (0, start, end);
5042 stop_canvas_autoscroll ();
5048 Editor::start_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
5050 drag_info.item = item;
5051 drag_info.motion_callback = &Editor::drag_mouse_zoom;
5052 drag_info.finished_callback = &Editor::end_mouse_zoom;
5054 start_grab (event, zoom_cursor);
5056 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5060 Editor::drag_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
5065 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
5066 snap_to (drag_info.current_pointer_frame);
5068 if (drag_info.first_move) {
5069 snap_to (drag_info.grab_frame);
5073 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
5075 /* base start and end on initial click position */
5076 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
5077 start = drag_info.current_pointer_frame;
5078 end = drag_info.grab_frame;
5080 end = drag_info.current_pointer_frame;
5081 start = drag_info.grab_frame;
5086 if (drag_info.first_move) {
5088 zoom_rect->raise_to_top();
5091 reposition_zoom_rect(start, end);
5093 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
5094 drag_info.first_move = false;
5096 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5101 Editor::end_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
5103 if (!drag_info.first_move) {
5104 drag_mouse_zoom (item, event);
5106 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
5107 temporal_zoom_by_frame (drag_info.grab_frame, drag_info.last_pointer_frame, "mouse zoom");
5109 temporal_zoom_by_frame (drag_info.last_pointer_frame, drag_info.grab_frame, "mouse zoom");
5112 temporal_zoom_to_frame (false, drag_info.grab_frame);
5114 temporal_zoom_step (false);
5115 center_screen (drag_info.grab_frame);
5123 Editor::reposition_zoom_rect (nframes64_t start, nframes64_t end)
5125 double x1 = frame_to_pixel (start);
5126 double x2 = frame_to_pixel (end);
5127 double y2 = full_canvas_height - 1.0;
5129 zoom_rect->property_x1() = x1;
5130 zoom_rect->property_y1() = 1.0;
5131 zoom_rect->property_x2() = x2;
5132 zoom_rect->property_y2() = y2;
5136 Editor::start_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
5138 drag_info.item = item;
5139 drag_info.motion_callback = &Editor::drag_rubberband_select;
5140 drag_info.finished_callback = &Editor::end_rubberband_select;
5142 start_grab (event, cross_hair_cursor);
5144 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5148 Editor::drag_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
5155 /* use a bigger drag threshold than the default */
5157 if (abs ((int) (drag_info.current_pointer_frame - drag_info.grab_frame)) < 8) {
5161 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier()) && Config->get_rubberbanding_snaps_to_grid()) {
5162 if (drag_info.first_move) {
5163 snap_to (drag_info.grab_frame);
5165 snap_to (drag_info.current_pointer_frame);
5168 /* base start and end on initial click position */
5170 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
5171 start = drag_info.current_pointer_frame;
5172 end = drag_info.grab_frame;
5174 end = drag_info.current_pointer_frame;
5175 start = drag_info.grab_frame;
5178 if (drag_info.current_pointer_y < drag_info.grab_y) {
5179 y1 = drag_info.current_pointer_y;
5180 y2 = drag_info.grab_y;
5182 y2 = drag_info.current_pointer_y;
5183 y1 = drag_info.grab_y;
5187 if (start != end || y1 != y2) {
5189 double x1 = frame_to_pixel (start);
5190 double x2 = frame_to_pixel (end);
5192 rubberband_rect->property_x1() = x1;
5193 rubberband_rect->property_y1() = y1;
5194 rubberband_rect->property_x2() = x2;
5195 rubberband_rect->property_y2() = y2;
5197 rubberband_rect->show();
5198 rubberband_rect->raise_to_top();
5200 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
5201 drag_info.first_move = false;
5203 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5208 Editor::end_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
5210 if (!drag_info.first_move) {
5212 drag_rubberband_select (item, event);
5215 if (drag_info.current_pointer_y < drag_info.grab_y) {
5216 y1 = drag_info.current_pointer_y;
5217 y2 = drag_info.grab_y;
5220 y2 = drag_info.current_pointer_y;
5221 y1 = drag_info.grab_y;
5225 Selection::Operation op = Keyboard::selection_type (event->button.state);
5228 begin_reversible_command (_("rubberband selection"));
5230 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
5231 commit = select_all_within (drag_info.grab_frame, drag_info.last_pointer_frame, y1, y2, track_views, op);
5233 commit = select_all_within (drag_info.last_pointer_frame, drag_info.grab_frame, y1, y2, track_views, op);
5237 commit_reversible_command ();
5241 selection->clear_tracks();
5242 selection->clear_regions();
5243 selection->clear_points ();
5244 selection->clear_lines ();
5247 rubberband_rect->hide();
5252 Editor::mouse_rename_region (ArdourCanvas::Item* item, GdkEvent* event)
5254 using namespace Gtkmm2ext;
5256 ArdourPrompter prompter (false);
5258 prompter.set_prompt (_("Name for region:"));
5259 prompter.set_initial_text (clicked_regionview->region()->name());
5260 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
5261 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
5262 prompter.show_all ();
5263 switch (prompter.run ()) {
5264 case Gtk::RESPONSE_ACCEPT:
5266 prompter.get_result(str);
5268 clicked_regionview->region()->set_name (str);
5276 Editor::start_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
5278 drag_info.item = item;
5279 drag_info.motion_callback = &Editor::time_fx_motion;
5280 drag_info.finished_callback = &Editor::end_time_fx;
5284 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5288 Editor::time_fx_motion (ArdourCanvas::Item *item, GdkEvent* event)
5290 RegionView* rv = clicked_regionview;
5292 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
5293 snap_to (drag_info.current_pointer_frame);
5296 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
5300 if (drag_info.current_pointer_frame > rv->region()->position()) {
5301 rv->get_time_axis_view().show_timestretch (rv->region()->position(), drag_info.current_pointer_frame);
5304 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
5305 drag_info.first_move = false;
5307 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5311 Editor::end_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
5313 clicked_regionview->get_time_axis_view().hide_timestretch ();
5315 if (drag_info.first_move) {
5319 if (drag_info.last_pointer_frame < clicked_regionview->region()->position()) {
5320 /* backwards drag of the left edge - not usable */
5324 nframes64_t newlen = drag_info.last_pointer_frame - clicked_regionview->region()->position();
5325 #ifdef USE_RUBBERBAND
5326 float percentage = (float) ((double) newlen / (double) clicked_regionview->region()->length());
5328 float percentage = (float) ((double) newlen - (double) clicked_regionview->region()->length()) / ((double) newlen) * 100.0f;
5331 begin_reversible_command (_("timestretch"));
5333 // XXX how do timeFX on multiple regions ?
5336 rs.add (clicked_regionview);
5338 if (time_stretch (rs, percentage) == 0) {
5339 session->commit_reversible_command ();
5344 Editor::mouse_brush_insert_region (RegionView* rv, nframes64_t pos)
5346 /* no brushing without a useful snap setting */
5349 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
5352 switch (snap_mode) {
5354 return; /* can't work because it allows region to be placed anywhere */
5359 switch (snap_type) {
5367 /* don't brush a copy over the original */
5369 if (pos == rv->region()->position()) {
5373 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*>(&arv->get_time_axis_view());
5375 if (atv == 0 || !atv->is_audio_track()) {
5379 boost::shared_ptr<Playlist> playlist = atv->playlist();
5380 double speed = atv->get_diskstream()->speed();
5382 XMLNode &before = playlist->get_state();
5383 playlist->add_region (boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (arv->audio_region())), (nframes64_t) (pos * speed));
5384 XMLNode &after = playlist->get_state();
5385 session->add_command(new MementoCommand<Playlist>(*playlist.get(), &before, &after));
5387 // playlist is frozen, so we have to update manually
5389 playlist->Modified(); /* EMIT SIGNAL */
5393 Editor::track_height_step_timeout ()
5395 if (get_microseconds() - last_track_height_step_timestamp < 250000) {
5396 current_stepping_trackview = 0;