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;
81 pointer_window = canvas_window->get_pointer (x, y, mask);
83 if (pointer_window == track_canvas.get_bin_window()) {
85 track_canvas.window_to_world (x, y, wx, wy);
86 in_track_canvas = true;
89 in_track_canvas = false;
91 if (pointer_window == time_canvas.get_bin_window()) {
92 time_canvas.window_to_world (x, y, wx, wy);
98 wx += horizontal_adjustment.get_value();
99 wy += vertical_adjustment.get_value();
102 event.type = GDK_BUTTON_RELEASE;
106 where = event_frame (&event, 0, 0);
111 Editor::event_frame (GdkEvent* event, double* pcx, double* pcy) const
125 switch (event->type) {
126 case GDK_BUTTON_RELEASE:
127 case GDK_BUTTON_PRESS:
128 case GDK_2BUTTON_PRESS:
129 case GDK_3BUTTON_PRESS:
130 track_canvas.w2c(event->button.x, event->button.y, *pcx, *pcy);
132 case GDK_MOTION_NOTIFY:
133 track_canvas.w2c(event->motion.x, event->motion.y, *pcx, *pcy);
135 case GDK_ENTER_NOTIFY:
136 case GDK_LEAVE_NOTIFY:
137 track_canvas.w2c(event->crossing.x, event->crossing.y, *pcx, *pcy);
140 case GDK_KEY_RELEASE:
141 // track_canvas.w2c(event->key.x, event->key.y, *pcx, *pcy);
144 warning << string_compose (_("Editor::event_frame() used on unhandled event type %1"), event->type) << endmsg;
148 /* note that pixel_to_frame() never returns less than zero, so even if the pixel
149 position is negative (as can be the case with motion events in particular),
150 the frame location is always positive.
153 return pixel_to_frame (*pcx);
157 Editor::mouse_mode_toggled (MouseMode m)
159 if (ignore_mouse_mode_toggle) {
165 if (mouse_select_button.get_active()) {
171 if (mouse_move_button.get_active()) {
177 if (mouse_gain_button.get_active()) {
183 if (mouse_zoom_button.get_active()) {
189 if (mouse_timefx_button.get_active()) {
195 if (mouse_audition_button.get_active()) {
206 Editor::set_mouse_mode (MouseMode m, bool force)
208 if (drag_info.item) {
212 if (!force && m == mouse_mode) {
220 if (mouse_mode != MouseRange) {
222 /* in all modes except range, hide the range selection,
223 show the object (region) selection.
226 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
227 (*i)->set_should_show_selection (true);
229 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
230 (*i)->hide_selection ();
236 in range mode,show the range selection.
239 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
240 if ((*i)->get_selected()) {
241 (*i)->show_selection (selection->time);
246 /* XXX the hack of unsetting all other buttons should go
247 away once GTK2 allows us to use regular radio buttons drawn like
248 normal buttons, rather than my silly GroupedButton hack.
251 ignore_mouse_mode_toggle = true;
253 switch (mouse_mode) {
255 mouse_select_button.set_active (true);
256 current_canvas_cursor = selector_cursor;
260 mouse_move_button.set_active (true);
261 if (Profile->get_sae()) {
262 current_canvas_cursor = timebar_cursor;
264 current_canvas_cursor = grabber_cursor;
269 mouse_gain_button.set_active (true);
270 current_canvas_cursor = cross_hair_cursor;
274 mouse_zoom_button.set_active (true);
275 current_canvas_cursor = zoom_cursor;
279 mouse_timefx_button.set_active (true);
280 current_canvas_cursor = time_fx_cursor; // just use playhead
284 mouse_audition_button.set_active (true);
285 current_canvas_cursor = speaker_cursor;
289 ignore_mouse_mode_toggle = false;
292 track_canvas.get_window()->set_cursor(*current_canvas_cursor);
297 Editor::step_mouse_mode (bool next)
299 switch (current_mouse_mode()) {
301 if (next) set_mouse_mode (MouseRange);
302 else set_mouse_mode (MouseTimeFX);
306 if (next) set_mouse_mode (MouseZoom);
307 else set_mouse_mode (MouseObject);
311 if (next) set_mouse_mode (MouseGain);
312 else set_mouse_mode (MouseRange);
316 if (next) set_mouse_mode (MouseTimeFX);
317 else set_mouse_mode (MouseZoom);
321 if (next) set_mouse_mode (MouseAudition);
322 else set_mouse_mode (MouseGain);
326 if (next) set_mouse_mode (MouseObject);
327 else set_mouse_mode (MouseTimeFX);
333 Editor::button_selection (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
335 /* in object/audition/timefx mode, any button press sets
336 the selection if the object can be selected. this is a
337 bit of hack, because we want to avoid this if the
338 mouse operation is a region alignment.
340 note: not dbl-click or triple-click
343 if (((mouse_mode != MouseObject) &&
344 (mouse_mode != MouseAudition || item_type != RegionItem) &&
345 (mouse_mode != MouseTimeFX || item_type != RegionItem) &&
346 (mouse_mode != MouseRange)) ||
348 (event->type != GDK_BUTTON_PRESS && event->type != GDK_BUTTON_RELEASE || event->button.button > 3)) {
353 if (event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) {
355 if ((event->button.state & Keyboard::RelevantModifierKeyMask) && event->button.button != 1) {
357 /* almost no selection action on modified button-2 or button-3 events */
359 if (item_type != RegionItem && event->button.button != 2) {
365 Selection::Operation op = Keyboard::selection_type (event->button.state);
366 bool press = (event->type == GDK_BUTTON_PRESS);
368 // begin_reversible_command (_("select on click"));
372 if (mouse_mode != MouseRange) {
373 set_selected_regionview_from_click (press, op, true);
374 } else if (event->type == GDK_BUTTON_PRESS) {
375 set_selected_track_as_side_effect ();
379 case RegionViewNameHighlight:
381 if (mouse_mode != MouseRange) {
382 set_selected_regionview_from_click (press, op, true);
383 } else if (event->type == GDK_BUTTON_PRESS) {
384 set_selected_track_as_side_effect ();
388 case FadeInHandleItem:
390 case FadeOutHandleItem:
392 if (mouse_mode != MouseRange) {
393 set_selected_regionview_from_click (press, op, true);
394 } else if (event->type == GDK_BUTTON_PRESS) {
395 set_selected_track_as_side_effect ();
399 case GainAutomationControlPointItem:
400 case PanAutomationControlPointItem:
401 case RedirectAutomationControlPointItem:
402 set_selected_track_as_side_effect ();
403 if (mouse_mode != MouseRange) {
404 set_selected_control_point_from_click (op, false);
409 /* for context click or range selection, select track */
410 if (event->button.button == 3) {
411 set_selected_track_as_side_effect ();
412 } else if (event->type == GDK_BUTTON_PRESS && mouse_mode == MouseRange) {
413 set_selected_track_as_side_effect ();
417 case AutomationTrackItem:
418 set_selected_track_as_side_effect (true);
426 const static double ZERO_GAIN_FRACTION = gain_to_slider_position(dB_to_coefficient(0.0));
429 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
431 track_canvas.grab_focus();
433 if (session && session->actively_recording()) {
437 button_selection (item, event, item_type);
439 if (drag_info.item == 0 &&
440 (Keyboard::is_delete_event (&event->button) ||
441 Keyboard::is_context_menu_event (&event->button) ||
442 Keyboard::is_edit_event (&event->button))) {
444 /* handled by button release */
448 switch (event->button.button) {
451 if (event->type == GDK_BUTTON_PRESS) {
453 if (drag_info.item) {
454 drag_info.item->ungrab (event->button.time);
457 /* single mouse clicks on any of these item types operate
458 independent of mouse mode, mostly because they are
459 not on the main track canvas or because we want
464 case PlayheadCursorItem:
465 start_cursor_grab (item, event);
469 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
470 hide_marker (item, event);
472 start_marker_grab (item, event);
476 case TempoMarkerItem:
477 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
478 start_tempo_marker_copy_grab (item, event);
480 start_tempo_marker_grab (item, event);
484 case MeterMarkerItem:
485 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
486 start_meter_marker_copy_grab (item, event);
488 start_meter_marker_grab (item, event);
498 case RangeMarkerBarItem:
499 start_range_markerbar_op (item, event, CreateRangeMarker);
503 case CdMarkerBarItem:
504 start_range_markerbar_op (item, event, CreateCDMarker);
508 case TransportMarkerBarItem:
509 start_range_markerbar_op (item, event, CreateTransportMarker);
518 switch (mouse_mode) {
521 case StartSelectionTrimItem:
522 start_selection_op (item, event, SelectionStartTrim);
525 case EndSelectionTrimItem:
526 start_selection_op (item, event, SelectionEndTrim);
530 if (Keyboard::modifier_state_contains
531 (event->button.state, Keyboard::ModifierMask(Keyboard::SecondaryModifier))) {
532 // contains and not equals because I can't use alt as a modifier alone.
533 start_selection_grab (item, event);
534 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
535 /* grab selection for moving */
536 start_selection_op (item, event, SelectionMove);
538 /* this was debated, but decided the more common action was to
539 make a new selection */
540 start_selection_op (item, event, CreateSelection);
545 start_selection_op (item, event, CreateSelection);
551 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)) &&
552 event->type == GDK_BUTTON_PRESS) {
554 start_rubberband_select (item, event);
556 } else if (event->type == GDK_BUTTON_PRESS) {
559 case FadeInHandleItem:
560 start_fade_in_grab (item, event);
563 case FadeOutHandleItem:
564 start_fade_out_grab (item, event);
568 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
569 start_region_copy_grab (item, event);
570 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
571 start_region_brush_grab (item, event);
573 start_region_grab (item, event);
577 case RegionViewNameHighlight:
578 start_trim (item, event);
583 /* rename happens on edit clicks */
584 start_trim (clicked_regionview->get_name_highlight(), event);
588 case GainAutomationControlPointItem:
589 case PanAutomationControlPointItem:
590 case RedirectAutomationControlPointItem:
591 start_control_point_grab (item, event);
595 case GainAutomationLineItem:
596 case PanAutomationLineItem:
597 case RedirectAutomationLineItem:
598 start_line_grab_from_line (item, event);
603 case AutomationTrackItem:
604 start_rubberband_select (item, event);
607 /* <CMT Additions> */
608 case ImageFrameHandleStartItem:
609 imageframe_start_handle_op(item, event) ;
612 case ImageFrameHandleEndItem:
613 imageframe_end_handle_op(item, event) ;
616 case MarkerViewHandleStartItem:
617 markerview_item_start_handle_op(item, event) ;
620 case MarkerViewHandleEndItem:
621 markerview_item_end_handle_op(item, event) ;
624 /* </CMT Additions> */
626 /* <CMT Additions> */
628 start_markerview_grab(item, event) ;
631 start_imageframe_grab(item, event) ;
633 /* </CMT Additions> */
649 // start_line_grab_from_regionview (item, event);
652 case GainControlPointItem:
653 start_control_point_grab (item, event);
657 start_line_grab_from_line (item, event);
660 case GainAutomationControlPointItem:
661 case PanAutomationControlPointItem:
662 case RedirectAutomationControlPointItem:
663 start_control_point_grab (item, event);
674 case GainAutomationControlPointItem:
675 case PanAutomationControlPointItem:
676 case RedirectAutomationControlPointItem:
677 start_control_point_grab (item, event);
680 case GainAutomationLineItem:
681 case PanAutomationLineItem:
682 case RedirectAutomationLineItem:
683 start_line_grab_from_line (item, event);
687 // XXX need automation mode to identify which
689 // start_line_grab_from_regionview (item, event);
699 if (event->type == GDK_BUTTON_PRESS) {
700 start_mouse_zoom (item, event);
707 if (item_type == RegionItem) {
708 start_time_fx (item, event);
715 scrub_reverse_distance = 0;
716 last_scrub_x = event->button.x;
717 scrubbing_direction = 0;
718 track_canvas.get_window()->set_cursor (*transparent_cursor);
719 /* rest handled in motion & release */
728 switch (mouse_mode) {
730 if (event->type == GDK_BUTTON_PRESS) {
733 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
734 start_region_copy_grab (item, event);
736 start_region_grab (item, event);
741 case GainAutomationControlPointItem:
742 case PanAutomationControlPointItem:
743 case RedirectAutomationControlPointItem:
744 start_control_point_grab (item, event);
755 case RegionViewNameHighlight:
756 start_trim (item, event);
761 start_trim (clicked_regionview->get_name_highlight(), event);
772 if (event->type == GDK_BUTTON_PRESS) {
773 /* relax till release */
780 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
781 temporal_zoom_session();
783 temporal_zoom_to_frame (true, event_frame(event));
806 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
808 nframes_t where = event_frame (event, 0, 0);
810 /* no action if we're recording */
812 if (session && session->actively_recording()) {
816 /* first, see if we're finishing a drag ... */
818 if (drag_info.item) {
819 if (end_grab (item, event)) {
820 /* grab dragged, so do nothing else */
825 button_selection (item, event, item_type);
827 /* edit events get handled here */
829 if (drag_info.item == 0 && Keyboard::is_edit_event (&event->button)) {
835 case TempoMarkerItem:
836 edit_tempo_marker (item);
839 case MeterMarkerItem:
840 edit_meter_marker (item);
844 if (clicked_regionview->name_active()) {
845 return mouse_rename_region (item, event);
855 /* context menu events get handled here */
857 if (Keyboard::is_context_menu_event (&event->button)) {
859 if (drag_info.item == 0) {
861 /* no matter which button pops up the context menu, tell the menu
862 widget to use button 1 to drive menu selection.
867 case FadeInHandleItem:
869 case FadeOutHandleItem:
870 popup_fade_context_menu (1, event->button.time, item, item_type);
874 popup_track_context_menu (1, event->button.time, item_type, false, where);
878 case RegionViewNameHighlight:
880 popup_track_context_menu (1, event->button.time, item_type, false, where);
884 popup_track_context_menu (1, event->button.time, item_type, true, where);
887 case AutomationTrackItem:
888 popup_track_context_menu (1, event->button.time, item_type, false, where);
892 case RangeMarkerBarItem:
893 case TransportMarkerBarItem:
894 case CdMarkerBarItem:
897 popup_ruler_menu (pixel_to_frame(event->button.x), item_type);
901 marker_context_menu (&event->button, item);
904 case TempoMarkerItem:
905 tm_marker_context_menu (&event->button, item);
908 case MeterMarkerItem:
909 tm_marker_context_menu (&event->button, item);
912 case CrossfadeViewItem:
913 popup_track_context_menu (1, event->button.time, item_type, false, where);
916 /* <CMT Additions> */
918 popup_imageframe_edit_menu(1, event->button.time, item, true) ;
920 case ImageFrameTimeAxisItem:
921 popup_imageframe_edit_menu(1, event->button.time, item, false) ;
924 popup_marker_time_axis_edit_menu(1, event->button.time, item, true) ;
926 case MarkerTimeAxisItem:
927 popup_marker_time_axis_edit_menu(1, event->button.time, item, false) ;
929 /* <CMT Additions> */
940 /* delete events get handled here */
942 if (drag_info.item == 0 && Keyboard::is_delete_event (&event->button)) {
945 case TempoMarkerItem:
946 remove_tempo_marker (item);
949 case MeterMarkerItem:
950 remove_meter_marker (item);
954 remove_marker (*item, event);
958 if (mouse_mode == MouseObject) {
959 remove_clicked_region ();
963 case GainControlPointItem:
964 if (mouse_mode == MouseGain) {
965 remove_gain_control_point (item, event);
969 case GainAutomationControlPointItem:
970 case PanAutomationControlPointItem:
971 case RedirectAutomationControlPointItem:
972 remove_control_point (item, event);
981 switch (event->button.button) {
985 /* see comments in button_press_handler */
986 case PlayheadCursorItem:
989 case GainAutomationLineItem:
990 case PanAutomationLineItem:
991 case RedirectAutomationLineItem:
992 case StartSelectionTrimItem:
993 case EndSelectionTrimItem:
997 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
998 snap_to (where, 0, true);
1000 mouse_add_new_marker (where);
1003 case CdMarkerBarItem:
1004 // if we get here then a dragged range wasn't done
1005 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1006 snap_to (where, 0, true);
1008 mouse_add_new_marker (where, true);
1012 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1015 mouse_add_new_tempo_event (where);
1019 mouse_add_new_meter_event (pixel_to_frame (event->button.x));
1027 switch (mouse_mode) {
1029 switch (item_type) {
1030 case AutomationTrackItem:
1031 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*>(clicked_trackview);
1033 atv->add_automation_event (item, event, where, event->button.y);
1044 // Gain only makes sense for audio regions
1046 if (!dynamic_cast<AudioRegionView*>(clicked_regionview)) {
1050 switch (item_type) {
1052 dynamic_cast<AudioRegionView*>(clicked_regionview)->add_gain_point_event (item, event);
1056 case AutomationTrackItem:
1057 dynamic_cast<AutomationTimeAxisView*>(clicked_trackview)->
1058 add_automation_event (item, event, where, event->button.y);
1068 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1069 if (scrubbing_direction == 0) {
1070 /* no drag, just a click */
1071 switch (item_type) {
1073 play_selected_region ();
1079 /* make sure we stop */
1080 session->request_transport_speed (0.0);
1094 switch (mouse_mode) {
1097 switch (item_type) {
1099 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1101 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier|Keyboard::SecondaryModifier))) {
1104 // Button2 click is unused
1117 // x_style_paste (where, 1.0);
1137 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1143 if (last_item_entered != item) {
1144 last_item_entered = item;
1145 last_item_entered_n = 0;
1148 switch (item_type) {
1149 case GainControlPointItem:
1150 if (mouse_mode == MouseGain) {
1151 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1152 cp->set_visible (true);
1156 at_y = cp->get_y ();
1157 cp->item->i2w (at_x, at_y);
1161 fraction = 1.0 - (cp->get_y() / cp->line.height());
1163 if (is_drawable() && !_scrubbing) {
1164 track_canvas.get_window()->set_cursor (*fader_cursor);
1167 last_item_entered_n++;
1168 set_verbose_canvas_cursor (cp->line.get_verbose_cursor_string (fraction), at_x, at_y);
1169 if (last_item_entered_n < 10) {
1170 show_verbose_canvas_cursor ();
1175 case GainAutomationControlPointItem:
1176 case PanAutomationControlPointItem:
1177 case RedirectAutomationControlPointItem:
1178 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1179 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1180 cp->set_visible (true);
1184 at_y = cp->get_y ();
1185 cp->item->i2w (at_x, at_y);
1189 fraction = 1.0 - (cp->get_y() / cp->line.height());
1191 set_verbose_canvas_cursor (cp->line.get_verbose_cursor_string (fraction), at_x, at_y);
1192 show_verbose_canvas_cursor ();
1194 if (is_drawable()) {
1195 track_canvas.get_window()->set_cursor (*fader_cursor);
1201 if (mouse_mode == MouseGain) {
1202 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1204 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredGainLine.get();
1205 if (is_drawable()) {
1206 track_canvas.get_window()->set_cursor (*fader_cursor);
1211 case GainAutomationLineItem:
1212 case RedirectAutomationLineItem:
1213 case PanAutomationLineItem:
1214 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1216 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1218 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredAutomationLine.get();
1220 if (is_drawable()) {
1221 track_canvas.get_window()->set_cursor (*fader_cursor);
1226 case RegionViewNameHighlight:
1227 if (is_drawable() && mouse_mode == MouseObject) {
1228 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1232 case StartSelectionTrimItem:
1233 case EndSelectionTrimItem:
1234 /* <CMT Additions> */
1235 case ImageFrameHandleStartItem:
1236 case ImageFrameHandleEndItem:
1237 case MarkerViewHandleStartItem:
1238 case MarkerViewHandleEndItem:
1239 /* </CMT Additions> */
1241 if (is_drawable()) {
1242 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1246 case PlayheadCursorItem:
1247 if (is_drawable()) {
1248 track_canvas.get_window()->set_cursor (*grabber_cursor);
1252 case RegionViewName:
1254 /* when the name is not an active item, the entire name highlight is for trimming */
1256 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1257 if (mouse_mode == MouseObject && is_drawable()) {
1258 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1264 case AutomationTrackItem:
1265 if (is_drawable()) {
1266 Gdk::Cursor *cursor;
1267 switch (mouse_mode) {
1269 cursor = selector_cursor;
1272 cursor = zoom_cursor;
1275 cursor = cross_hair_cursor;
1279 track_canvas.get_window()->set_cursor (*cursor);
1281 AutomationTimeAxisView* atv;
1282 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1283 clear_entered_track = false;
1284 set_entered_track (atv);
1290 case RangeMarkerBarItem:
1291 case TransportMarkerBarItem:
1292 case CdMarkerBarItem:
1295 if (is_drawable()) {
1296 time_canvas.get_window()->set_cursor (*timebar_cursor);
1301 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1304 entered_marker = marker;
1305 marker->set_color_rgba (ARDOUR_UI::config()->canvasvar_EnteredMarker.get());
1307 case MeterMarkerItem:
1308 case TempoMarkerItem:
1309 if (is_drawable()) {
1310 time_canvas.get_window()->set_cursor (*timebar_cursor);
1313 case FadeInHandleItem:
1314 case FadeOutHandleItem:
1315 if (mouse_mode == MouseObject) {
1316 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1318 rect->property_fill_color_rgba() = 0;
1319 rect->property_outline_pixels() = 1;
1328 /* second pass to handle entered track status in a comprehensible way.
1331 switch (item_type) {
1333 case GainAutomationLineItem:
1334 case RedirectAutomationLineItem:
1335 case PanAutomationLineItem:
1336 case GainControlPointItem:
1337 case GainAutomationControlPointItem:
1338 case PanAutomationControlPointItem:
1339 case RedirectAutomationControlPointItem:
1340 /* these do not affect the current entered track state */
1341 clear_entered_track = false;
1344 case AutomationTrackItem:
1345 /* handled above already */
1349 set_entered_track (0);
1357 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1366 switch (item_type) {
1367 case GainControlPointItem:
1368 case GainAutomationControlPointItem:
1369 case PanAutomationControlPointItem:
1370 case RedirectAutomationControlPointItem:
1371 cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
1372 if (cp->line.npoints() > 1) {
1373 if (!cp->selected) {
1374 cp->set_visible (false);
1378 if (is_drawable()) {
1379 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1382 hide_verbose_canvas_cursor ();
1385 case RegionViewNameHighlight:
1386 case StartSelectionTrimItem:
1387 case EndSelectionTrimItem:
1388 case PlayheadCursorItem:
1389 /* <CMT Additions> */
1390 case ImageFrameHandleStartItem:
1391 case ImageFrameHandleEndItem:
1392 case MarkerViewHandleStartItem:
1393 case MarkerViewHandleEndItem:
1394 /* </CMT Additions> */
1395 if (is_drawable()) {
1396 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1401 case GainAutomationLineItem:
1402 case RedirectAutomationLineItem:
1403 case PanAutomationLineItem:
1404 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1406 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1408 line->property_fill_color_rgba() = al->get_line_color();
1410 if (is_drawable()) {
1411 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1415 case RegionViewName:
1416 /* see enter_handler() for notes */
1417 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1418 if (is_drawable() && mouse_mode == MouseObject) {
1419 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1424 case RangeMarkerBarItem:
1425 case TransportMarkerBarItem:
1426 case CdMarkerBarItem:
1430 if (is_drawable()) {
1431 time_canvas.get_window()->set_cursor (*timebar_cursor);
1436 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1440 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
1441 location_flags_changed (loc, this);
1444 case MeterMarkerItem:
1445 case TempoMarkerItem:
1447 if (is_drawable()) {
1448 time_canvas.get_window()->set_cursor (*timebar_cursor);
1453 case FadeInHandleItem:
1454 case FadeOutHandleItem:
1455 rv = static_cast<RegionView*>(item->get_data ("regionview"));
1457 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1459 rect->property_fill_color_rgba() = rv->get_fill_color();
1460 rect->property_outline_pixels() = 0;
1465 case AutomationTrackItem:
1466 if (is_drawable()) {
1467 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1468 clear_entered_track = true;
1469 Glib::signal_idle().connect (mem_fun(*this, &Editor::left_automation_track));
1481 Editor::left_automation_track ()
1483 if (clear_entered_track) {
1484 set_entered_track (0);
1485 clear_entered_track = false;
1491 Editor::motion_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type, bool from_autoscroll)
1493 if (event->motion.is_hint) {
1496 /* We call this so that MOTION_NOTIFY events continue to be
1497 delivered to the canvas. We need to do this because we set
1498 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
1499 the density of the events, at the expense of a round-trip
1500 to the server. Given that this will mostly occur on cases
1501 where DISPLAY = :0.0, and given the cost of what the motion
1502 event might do, its a good tradeoff.
1505 track_canvas.get_pointer (x, y);
1508 if (current_stepping_trackview) {
1509 /* don't keep the persistent stepped trackview if the mouse moves */
1510 current_stepping_trackview = 0;
1511 step_timeout.disconnect ();
1514 if (session && session->actively_recording()) {
1515 /* Sorry. no dragging stuff around while we record */
1519 drag_info.item_type = item_type;
1520 drag_info.last_pointer_x = drag_info.current_pointer_x;
1521 drag_info.last_pointer_y = drag_info.current_pointer_y;
1522 drag_info.current_pointer_frame = event_frame (event, &drag_info.current_pointer_x,
1523 &drag_info.current_pointer_y);
1525 switch (mouse_mode) {
1531 if (scrubbing_direction == 0) {
1533 session->request_locate (drag_info.current_pointer_frame, false);
1534 session->request_transport_speed (0.1);
1535 scrubbing_direction = 1;
1539 if (last_scrub_x > drag_info.current_pointer_x) {
1541 /* pointer moved to the left */
1543 if (scrubbing_direction > 0) {
1545 /* we reversed direction to go backwards */
1548 scrub_reverse_distance += (int) (last_scrub_x - drag_info.current_pointer_x);
1552 /* still moving to the left (backwards) */
1554 scrub_reversals = 0;
1555 scrub_reverse_distance = 0;
1557 delta = 0.01 * (last_scrub_x - drag_info.current_pointer_x);
1558 session->request_transport_speed (session->transport_speed() - delta);
1562 /* pointer moved to the right */
1564 if (scrubbing_direction < 0) {
1565 /* we reversed direction to go forward */
1568 scrub_reverse_distance += (int) (drag_info.current_pointer_x - last_scrub_x);
1571 /* still moving to the right */
1573 scrub_reversals = 0;
1574 scrub_reverse_distance = 0;
1576 delta = 0.01 * (drag_info.current_pointer_x - last_scrub_x);
1577 session->request_transport_speed (session->transport_speed() + delta);
1581 /* if there have been more than 2 opposite motion moves detected, or one that moves
1582 back more than 10 pixels, reverse direction
1585 if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
1587 if (scrubbing_direction > 0) {
1588 /* was forwards, go backwards */
1589 session->request_transport_speed (-0.1);
1590 scrubbing_direction = -1;
1592 /* was backwards, go forwards */
1593 session->request_transport_speed (0.1);
1594 scrubbing_direction = 1;
1597 scrub_reverse_distance = 0;
1598 scrub_reversals = 0;
1602 last_scrub_x = drag_info.current_pointer_x;
1609 if (!from_autoscroll && drag_info.item) {
1610 /* item != 0 is the best test i can think of for dragging.
1612 if (!drag_info.move_threshold_passed) {
1614 bool x_threshold_passed = (::llabs ((nframes64_t) (drag_info.current_pointer_x - drag_info.grab_x)) > 4LL);
1615 bool y_threshold_passed = (::llabs ((nframes64_t) (drag_info.current_pointer_y - drag_info.grab_y)) > 4LL);
1617 drag_info.move_threshold_passed = (x_threshold_passed || y_threshold_passed);
1619 // and change the initial grab loc/frame if this drag info wants us to
1621 if (drag_info.want_move_threshold && drag_info.move_threshold_passed) {
1622 drag_info.grab_frame = drag_info.current_pointer_frame;
1623 drag_info.grab_x = drag_info.current_pointer_x;
1624 drag_info.grab_y = drag_info.current_pointer_y;
1625 drag_info.last_pointer_frame = drag_info.grab_frame;
1626 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
1631 switch (item_type) {
1632 case PlayheadCursorItem:
1634 case GainControlPointItem:
1635 case RedirectAutomationControlPointItem:
1636 case GainAutomationControlPointItem:
1637 case PanAutomationControlPointItem:
1638 case TempoMarkerItem:
1639 case MeterMarkerItem:
1640 case RegionViewNameHighlight:
1641 case StartSelectionTrimItem:
1642 case EndSelectionTrimItem:
1645 case RedirectAutomationLineItem:
1646 case GainAutomationLineItem:
1647 case PanAutomationLineItem:
1648 case FadeInHandleItem:
1649 case FadeOutHandleItem:
1650 /* <CMT Additions> */
1651 case ImageFrameHandleStartItem:
1652 case ImageFrameHandleEndItem:
1653 case MarkerViewHandleStartItem:
1654 case MarkerViewHandleEndItem:
1655 /* </CMT Additions> */
1656 if (drag_info.item && (event->motion.state & Gdk::BUTTON1_MASK ||
1657 (event->motion.state & Gdk::BUTTON2_MASK))) {
1658 if (!from_autoscroll) {
1659 maybe_autoscroll (event);
1661 (this->*(drag_info.motion_callback)) (item, event);
1670 switch (mouse_mode) {
1675 if (drag_info.item && (event->motion.state & GDK_BUTTON1_MASK ||
1676 (event->motion.state & GDK_BUTTON2_MASK))) {
1677 if (!from_autoscroll) {
1678 maybe_autoscroll (event);
1680 (this->*(drag_info.motion_callback)) (item, event);
1691 track_canvas_motion (event);
1692 // drag_info.last_pointer_frame = drag_info.current_pointer_frame;
1700 Editor::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
1702 if (drag_info.item == 0) {
1703 fatal << _("programming error: start_grab called without drag item") << endmsg;
1709 cursor = grabber_cursor;
1712 // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
1714 if (event->button.button == 2) {
1715 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
1716 drag_info.y_constrained = true;
1717 drag_info.x_constrained = false;
1719 drag_info.y_constrained = false;
1720 drag_info.x_constrained = true;
1723 drag_info.x_constrained = false;
1724 drag_info.y_constrained = false;
1727 drag_info.grab_frame = event_frame (event, &drag_info.grab_x, &drag_info.grab_y);
1728 drag_info.last_pointer_frame = drag_info.grab_frame;
1729 drag_info.current_pointer_frame = drag_info.grab_frame;
1730 drag_info.current_pointer_x = drag_info.grab_x;
1731 drag_info.current_pointer_y = drag_info.grab_y;
1732 drag_info.last_pointer_x = drag_info.current_pointer_x;
1733 drag_info.last_pointer_y = drag_info.current_pointer_y;
1734 drag_info.cumulative_x_drag = 0;
1735 drag_info.cumulative_y_drag = 0;
1736 drag_info.first_move = true;
1737 drag_info.move_threshold_passed = false;
1738 drag_info.want_move_threshold = false;
1739 drag_info.pointer_frame_offset = 0;
1740 drag_info.brushing = false;
1741 drag_info.copied_location = 0;
1743 drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK,
1745 event->button.time);
1747 if (session && session->transport_rolling()) {
1748 drag_info.was_rolling = true;
1750 drag_info.was_rolling = false;
1753 switch (snap_type) {
1754 case SnapToRegionStart:
1755 case SnapToRegionEnd:
1756 case SnapToRegionSync:
1757 case SnapToRegionBoundary:
1758 build_region_boundary_cache ();
1766 Editor::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t time)
1768 drag_info.item->ungrab (0);
1769 drag_info.item = new_item;
1772 cursor = grabber_cursor;
1775 drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK, *cursor, time);
1779 Editor::end_grab (ArdourCanvas::Item* item, GdkEvent* event)
1781 bool did_drag = false;
1783 stop_canvas_autoscroll ();
1785 if (drag_info.item == 0) {
1789 drag_info.item->ungrab (event->button.time);
1791 if (drag_info.finished_callback) {
1792 drag_info.last_pointer_x = drag_info.current_pointer_x;
1793 drag_info.last_pointer_y = drag_info.current_pointer_y;
1794 (this->*(drag_info.finished_callback)) (item, event);
1797 did_drag = !drag_info.first_move;
1799 hide_verbose_canvas_cursor();
1802 drag_info.copy = false;
1803 drag_info.motion_callback = 0;
1804 drag_info.finished_callback = 0;
1805 drag_info.last_trackview = 0;
1806 drag_info.last_frame_position = 0;
1807 drag_info.grab_frame = 0;
1808 drag_info.last_pointer_frame = 0;
1809 drag_info.current_pointer_frame = 0;
1810 drag_info.brushing = false;
1812 if (drag_info.copied_location) {
1813 delete drag_info.copied_location;
1814 drag_info.copied_location = 0;
1821 Editor::start_fade_in_grab (ArdourCanvas::Item* item, GdkEvent* event)
1823 drag_info.item = item;
1824 drag_info.motion_callback = &Editor::fade_in_drag_motion_callback;
1825 drag_info.finished_callback = &Editor::fade_in_drag_finished_callback;
1829 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
1830 fatal << _("programming error: fade in canvas item has no regionview data pointer!") << endmsg;
1834 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1836 drag_info.pointer_frame_offset = drag_info.grab_frame - ((nframes_t) arv->audio_region()->fade_in().back()->when + arv->region()->position());
1840 Editor::fade_in_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1842 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1844 nframes_t fade_length;
1846 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1847 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1853 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1857 if (pos < (arv->region()->position() + 64)) {
1858 fade_length = 64; // this should be a minimum defined somewhere
1859 } else if (pos > arv->region()->last_frame()) {
1860 fade_length = arv->region()->length();
1862 fade_length = pos - arv->region()->position();
1864 /* mapover the region selection */
1866 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1868 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1874 tmp->reset_fade_in_shape_width (fade_length);
1877 show_verbose_duration_cursor (arv->region()->position(), arv->region()->position() + fade_length, 10);
1879 drag_info.first_move = false;
1883 Editor::fade_in_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1885 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1887 nframes_t fade_length;
1889 if (drag_info.first_move) return;
1891 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1892 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1897 if (pos < (arv->region()->position() + 64)) {
1898 fade_length = 64; // this should be a minimum defined somewhere
1899 } else if (pos > arv->region()->last_frame()) {
1900 fade_length = arv->region()->length();
1902 fade_length = pos - arv->region()->position();
1905 begin_reversible_command (_("change fade in length"));
1907 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1909 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1915 AutomationList& alist = tmp->audio_region()->fade_in();
1916 XMLNode &before = alist.get_state();
1918 tmp->audio_region()->set_fade_in_length (fade_length);
1919 tmp->audio_region()->set_fade_in_active (true);
1921 XMLNode &after = alist.get_state();
1922 session->add_command(new MementoCommand<AutomationList>(alist, &before, &after));
1925 commit_reversible_command ();
1929 Editor::start_fade_out_grab (ArdourCanvas::Item* item, GdkEvent* event)
1931 drag_info.item = item;
1932 drag_info.motion_callback = &Editor::fade_out_drag_motion_callback;
1933 drag_info.finished_callback = &Editor::fade_out_drag_finished_callback;
1937 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
1938 fatal << _("programming error: fade out canvas item has no regionview data pointer!") << endmsg;
1942 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1944 drag_info.pointer_frame_offset = drag_info.grab_frame - (arv->region()->length() - (nframes_t) arv->audio_region()->fade_out().back()->when + arv->region()->position());
1948 Editor::fade_out_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1950 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1952 nframes_t fade_length;
1954 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1955 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1960 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1964 if (pos > (arv->region()->last_frame() - 64)) {
1965 fade_length = 64; // this should really be a minimum fade defined somewhere
1967 else if (pos < arv->region()->position()) {
1968 fade_length = arv->region()->length();
1971 fade_length = arv->region()->last_frame() - pos;
1974 /* mapover the region selection */
1976 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1978 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1984 tmp->reset_fade_out_shape_width (fade_length);
1987 show_verbose_duration_cursor (arv->region()->last_frame() - fade_length, arv->region()->last_frame(), 10);
1989 drag_info.first_move = false;
1993 Editor::fade_out_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1995 if (drag_info.first_move) return;
1997 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1999 nframes_t fade_length;
2001 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2002 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2008 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2012 if (pos > (arv->region()->last_frame() - 64)) {
2013 fade_length = 64; // this should really be a minimum fade defined somewhere
2015 else if (pos < arv->region()->position()) {
2016 fade_length = arv->region()->length();
2019 fade_length = arv->region()->last_frame() - pos;
2022 begin_reversible_command (_("change fade out length"));
2024 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2026 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2032 AutomationList& alist = tmp->audio_region()->fade_out();
2033 XMLNode &before = alist.get_state();
2035 tmp->audio_region()->set_fade_out_length (fade_length);
2036 tmp->audio_region()->set_fade_out_active (true);
2038 XMLNode &after = alist.get_state();
2039 session->add_command(new MementoCommand<AutomationList>(alist, &before, &after));
2042 commit_reversible_command ();
2046 Editor::start_cursor_grab (ArdourCanvas::Item* item, GdkEvent* event)
2048 drag_info.item = item;
2049 drag_info.motion_callback = &Editor::cursor_drag_motion_callback;
2050 drag_info.finished_callback = &Editor::cursor_drag_finished_callback;
2054 if ((drag_info.data = (item->get_data ("cursor"))) == 0) {
2055 fatal << _("programming error: cursor canvas item has no cursor data pointer!") << endmsg;
2059 Cursor* cursor = (Cursor *) drag_info.data;
2061 if (cursor == playhead_cursor) {
2062 _dragging_playhead = true;
2064 if (session && drag_info.was_rolling) {
2065 session->request_stop ();
2068 if (session && session->is_auditioning()) {
2069 session->cancel_audition ();
2073 drag_info.pointer_frame_offset = drag_info.grab_frame - cursor->current_frame;
2075 show_verbose_time_cursor (cursor->current_frame, 10);
2079 Editor::cursor_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2081 Cursor* cursor = (Cursor *) drag_info.data;
2082 nframes_t adjusted_frame;
2084 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2085 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2091 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2092 if (cursor == playhead_cursor) {
2093 snap_to (adjusted_frame);
2097 if (adjusted_frame == drag_info.last_pointer_frame) return;
2099 cursor->set_position (adjusted_frame);
2101 UpdateAllTransportClocks (cursor->current_frame);
2103 show_verbose_time_cursor (cursor->current_frame, 10);
2105 drag_info.last_pointer_frame = adjusted_frame;
2106 drag_info.first_move = false;
2110 Editor::cursor_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2112 if (drag_info.first_move) return;
2114 cursor_drag_motion_callback (item, event);
2116 _dragging_playhead = false;
2118 if (item == &playhead_cursor->canvas_item) {
2120 session->request_locate (playhead_cursor->current_frame, drag_info.was_rolling);
2126 Editor::update_marker_drag_item (Location *location)
2128 double x1 = frame_to_pixel (location->start());
2129 double x2 = frame_to_pixel (location->end());
2131 if (location->is_mark()) {
2132 marker_drag_line_points.front().set_x(x1);
2133 marker_drag_line_points.back().set_x(x1);
2134 marker_drag_line->property_points() = marker_drag_line_points;
2137 range_marker_drag_rect->property_x1() = x1;
2138 range_marker_drag_rect->property_x2() = x2;
2143 Editor::start_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2147 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2148 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2154 Location *location = find_location_from_marker (marker, is_start);
2156 drag_info.item = item;
2157 drag_info.data = marker;
2158 drag_info.motion_callback = &Editor::marker_drag_motion_callback;
2159 drag_info.finished_callback = &Editor::marker_drag_finished_callback;
2163 _dragging_edit_point = true;
2165 drag_info.copied_location = new Location (*location);
2166 drag_info.pointer_frame_offset = drag_info.grab_frame - (is_start ? location->start() : location->end());
2168 update_marker_drag_item (location);
2170 if (location->is_mark()) {
2171 // marker_drag_line->show();
2172 // marker_drag_line->raise_to_top();
2174 range_marker_drag_rect->show();
2175 range_marker_drag_rect->raise_to_top();
2179 show_verbose_time_cursor (location->start(), 10);
2181 show_verbose_time_cursor (location->end(), 10);
2184 Selection::Operation op = Keyboard::selection_type (event->button.state);
2187 case Selection::Toggle:
2188 selection->toggle (marker);
2190 case Selection::Set:
2191 selection->set (marker);
2193 case Selection::Extend:
2194 selection->add (marker);
2196 case Selection::Add:
2197 selection->add (marker);
2203 Editor::marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2206 Marker* marker = (Marker *) drag_info.data;
2207 Location *real_location;
2208 Location *copy_location;
2210 bool move_both = false;
2213 if (drag_info.pointer_frame_offset <= drag_info.current_pointer_frame) {
2214 newframe = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2219 nframes_t next = newframe;
2221 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2222 snap_to (newframe, 0, true);
2225 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
2229 /* call this to find out if its the start or end */
2231 if ((real_location = find_location_from_marker (marker, is_start)) == 0) {
2235 if (real_location->locked()) {
2239 /* use the copy that we're "dragging" around */
2241 copy_location = drag_info.copied_location;
2243 f_delta = copy_location->end() - copy_location->start();
2245 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2249 if (copy_location->is_mark()) {
2252 copy_location->set_start (newframe);
2256 if (is_start) { // start-of-range marker
2259 copy_location->set_start (newframe);
2260 copy_location->set_end (newframe + f_delta);
2261 } else if (newframe < copy_location->end()) {
2262 copy_location->set_start (newframe);
2264 snap_to (next, 1, true);
2265 copy_location->set_end (next);
2266 copy_location->set_start (newframe);
2269 } else { // end marker
2272 copy_location->set_end (newframe);
2273 copy_location->set_start (newframe - f_delta);
2274 } else if (newframe > copy_location->start()) {
2275 copy_location->set_end (newframe);
2277 } else if (newframe > 0) {
2278 snap_to (next, -1, true);
2279 copy_location->set_start (next);
2280 copy_location->set_end (newframe);
2285 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
2286 drag_info.first_move = false;
2288 update_marker_drag_item (copy_location);
2290 LocationMarkers* lm = find_location_markers (real_location);
2291 lm->set_position (copy_location->start(), copy_location->end());
2292 edit_point_clock.set (copy_location->start());
2294 show_verbose_time_cursor (newframe, 10);
2298 Editor::marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2300 if (drag_info.first_move) {
2301 marker_drag_motion_callback (item, event);
2305 _dragging_edit_point = false;
2307 Marker* marker = (Marker *) drag_info.data;
2310 begin_reversible_command ( _("move marker") );
2311 XMLNode &before = session->locations()->get_state();
2313 Location * location = find_location_from_marker (marker, is_start);
2317 if (location->locked()) {
2321 if (location->is_mark()) {
2322 location->set_start (drag_info.copied_location->start());
2324 location->set (drag_info.copied_location->start(), drag_info.copied_location->end());
2328 XMLNode &after = session->locations()->get_state();
2329 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
2330 commit_reversible_command ();
2332 marker_drag_line->hide();
2333 range_marker_drag_rect->hide();
2337 Editor::start_meter_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2340 MeterMarker* meter_marker;
2342 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2343 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2347 meter_marker = dynamic_cast<MeterMarker*> (marker);
2349 MetricSection& section (meter_marker->meter());
2351 if (!section.movable()) {
2355 drag_info.item = item;
2356 drag_info.copy = false;
2357 drag_info.data = marker;
2358 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2359 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2363 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2365 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2369 Editor::start_meter_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2372 MeterMarker* meter_marker;
2374 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2375 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2379 meter_marker = dynamic_cast<MeterMarker*> (marker);
2381 // create a dummy marker for visual representation of moving the copy.
2382 // The actual copying is not done before we reach the finish callback.
2384 snprintf (name, sizeof(name), "%g/%g", meter_marker->meter().beats_per_bar(), meter_marker->meter().note_divisor ());
2385 MeterMarker* new_marker = new MeterMarker(*this, *meter_group, ARDOUR_UI::config()->canvasvar_MeterMarker.get(), name,
2386 *new MeterSection(meter_marker->meter()));
2388 drag_info.item = &new_marker->the_item();
2389 drag_info.copy = true;
2390 drag_info.data = new_marker;
2391 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2392 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2396 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2398 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2402 Editor::meter_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2404 MeterMarker* marker = (MeterMarker *) drag_info.data;
2405 nframes_t adjusted_frame;
2407 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2408 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2414 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2415 snap_to (adjusted_frame);
2418 if (adjusted_frame == drag_info.last_pointer_frame) return;
2420 marker->set_position (adjusted_frame);
2423 drag_info.last_pointer_frame = adjusted_frame;
2424 drag_info.first_move = false;
2426 show_verbose_time_cursor (adjusted_frame, 10);
2430 Editor::meter_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2432 if (drag_info.first_move) return;
2434 meter_marker_drag_motion_callback (drag_info.item, event);
2436 MeterMarker* marker = (MeterMarker *) drag_info.data;
2439 TempoMap& map (session->tempo_map());
2440 map.bbt_time (drag_info.last_pointer_frame, when);
2442 if (drag_info.copy == true) {
2443 begin_reversible_command (_("copy meter mark"));
2444 XMLNode &before = map.get_state();
2445 map.add_meter (marker->meter(), when);
2446 XMLNode &after = map.get_state();
2447 session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2448 commit_reversible_command ();
2450 // delete the dummy marker we used for visual representation of copying.
2451 // a new visual marker will show up automatically.
2454 begin_reversible_command (_("move meter mark"));
2455 XMLNode &before = map.get_state();
2456 map.move_meter (marker->meter(), when);
2457 XMLNode &after = map.get_state();
2458 session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2459 commit_reversible_command ();
2464 Editor::start_tempo_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2467 TempoMarker* tempo_marker;
2469 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2470 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2474 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2475 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2479 MetricSection& section (tempo_marker->tempo());
2481 if (!section.movable()) {
2485 drag_info.item = item;
2486 drag_info.copy = false;
2487 drag_info.data = marker;
2488 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2489 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2493 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2494 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2498 Editor::start_tempo_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2501 TempoMarker* tempo_marker;
2503 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2504 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2508 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2509 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2513 // create a dummy marker for visual representation of moving the copy.
2514 // The actual copying is not done before we reach the finish callback.
2516 snprintf (name, sizeof (name), "%.2f", tempo_marker->tempo().beats_per_minute());
2517 TempoMarker* new_marker = new TempoMarker(*this, *tempo_group, ARDOUR_UI::config()->canvasvar_TempoMarker.get(), name,
2518 *new TempoSection(tempo_marker->tempo()));
2520 drag_info.item = &new_marker->the_item();
2521 drag_info.copy = true;
2522 drag_info.data = new_marker;
2523 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2524 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2528 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2530 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2534 Editor::tempo_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2536 TempoMarker* marker = (TempoMarker *) drag_info.data;
2537 nframes_t adjusted_frame;
2539 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2540 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2546 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2547 snap_to (adjusted_frame);
2550 if (adjusted_frame == drag_info.last_pointer_frame) return;
2552 /* OK, we've moved far enough to make it worth actually move the thing. */
2554 marker->set_position (adjusted_frame);
2556 show_verbose_time_cursor (adjusted_frame, 10);
2558 drag_info.last_pointer_frame = adjusted_frame;
2559 drag_info.first_move = false;
2563 Editor::tempo_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2565 if (drag_info.first_move) return;
2567 tempo_marker_drag_motion_callback (drag_info.item, event);
2569 TempoMarker* marker = (TempoMarker *) drag_info.data;
2572 TempoMap& map (session->tempo_map());
2573 map.bbt_time (drag_info.last_pointer_frame, when);
2575 if (drag_info.copy == true) {
2576 begin_reversible_command (_("copy tempo mark"));
2577 XMLNode &before = map.get_state();
2578 map.add_tempo (marker->tempo(), when);
2579 XMLNode &after = map.get_state();
2580 session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2581 commit_reversible_command ();
2583 // delete the dummy marker we used for visual representation of copying.
2584 // a new visual marker will show up automatically.
2587 begin_reversible_command (_("move tempo mark"));
2588 XMLNode &before = map.get_state();
2589 map.move_tempo (marker->tempo(), when);
2590 XMLNode &after = map.get_state();
2591 session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2592 commit_reversible_command ();
2597 Editor::remove_gain_control_point (ArdourCanvas::Item*item, GdkEvent* event)
2599 ControlPoint* control_point;
2601 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2602 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2606 // We shouldn't remove the first or last gain point
2607 if (control_point->line.is_last_point(*control_point) ||
2608 control_point->line.is_first_point(*control_point)) {
2612 control_point->line.remove_point (*control_point);
2616 Editor::remove_control_point (ArdourCanvas::Item*item, GdkEvent* event)
2618 ControlPoint* control_point;
2620 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2621 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2625 control_point->line.remove_point (*control_point);
2629 Editor::start_control_point_grab (ArdourCanvas::Item* item, GdkEvent* event)
2631 ControlPoint* control_point;
2633 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2634 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2638 drag_info.item = item;
2639 drag_info.data = control_point;
2640 drag_info.motion_callback = &Editor::control_point_drag_motion_callback;
2641 drag_info.finished_callback = &Editor::control_point_drag_finished_callback;
2643 start_grab (event, fader_cursor);
2645 // start the grab at the center of the control point so
2646 // the point doesn't 'jump' to the mouse after the first drag
2647 drag_info.grab_x = control_point->get_x();
2648 drag_info.grab_y = control_point->get_y();
2649 control_point->line.parent_group().i2w(drag_info.grab_x, drag_info.grab_y);
2650 track_canvas.w2c(drag_info.grab_x, drag_info.grab_y,
2651 drag_info.grab_x, drag_info.grab_y);
2653 drag_info.grab_frame = pixel_to_frame(drag_info.grab_x);
2655 control_point->line.start_drag (control_point, drag_info.grab_frame, 0);
2657 float fraction = 1.0 - (control_point->get_y() / control_point->line.height());
2658 set_verbose_canvas_cursor (control_point->line.get_verbose_cursor_string (fraction),
2659 drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
2661 show_verbose_canvas_cursor ();
2665 Editor::control_point_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2667 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2669 double dx = drag_info.current_pointer_x - drag_info.last_pointer_x;
2670 double dy = drag_info.current_pointer_y - drag_info.last_pointer_y;
2672 if (event->button.state & Keyboard::SecondaryModifier) {
2677 double cx = drag_info.grab_x + drag_info.cumulative_x_drag + dx;
2678 double cy = drag_info.grab_y + drag_info.cumulative_y_drag + dy;
2680 // calculate zero crossing point. back off by .01 to stay on the
2681 // positive side of zero
2683 double zero_gain_y = (1.0 - ZERO_GAIN_FRACTION) * cp->line.height() - .01;
2684 cp->line.parent_group().i2w(_unused, zero_gain_y);
2686 // make sure we hit zero when passing through
2687 if ((cy < zero_gain_y and (cy - dy) > zero_gain_y)
2688 or (cy > zero_gain_y and (cy - dy) < zero_gain_y)) {
2692 if (drag_info.x_constrained) {
2693 cx = drag_info.grab_x;
2695 if (drag_info.y_constrained) {
2696 cy = drag_info.grab_y;
2699 drag_info.cumulative_x_drag = cx - drag_info.grab_x;
2700 drag_info.cumulative_y_drag = cy - drag_info.grab_y;
2702 cp->line.parent_group().w2i (cx, cy);
2706 cy = min ((double) cp->line.height(), cy);
2708 //translate cx to frames
2709 nframes_t cx_frames = unit_to_frame (cx);
2711 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier()) && !drag_info.x_constrained) {
2712 snap_to (cx_frames);
2715 float fraction = 1.0 - (cy / cp->line.height());
2719 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) {
2725 cp->line.point_drag (*cp, cx_frames , fraction, push);
2727 set_verbose_canvas_cursor_text (cp->line.get_verbose_cursor_string (fraction));
2729 drag_info.first_move = false;
2733 Editor::control_point_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2735 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2737 if (drag_info.first_move) {
2741 if ((event->type == GDK_BUTTON_RELEASE) && (event->button.button == 1) && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2742 reset_point_selection ();
2746 control_point_drag_motion_callback (item, event);
2748 cp->line.end_drag (cp);
2752 Editor::start_line_grab_from_regionview (ArdourCanvas::Item* item, GdkEvent* event)
2754 switch (mouse_mode) {
2756 assert(dynamic_cast<AudioRegionView*>(clicked_regionview));
2757 start_line_grab (dynamic_cast<AudioRegionView*>(clicked_regionview)->get_gain_line(), event);
2765 Editor::start_line_grab_from_line (ArdourCanvas::Item* item, GdkEvent* event)
2769 if ((al = reinterpret_cast<AutomationLine*> (item->get_data ("line"))) == 0) {
2770 fatal << _("programming error: line canvas item has no line pointer!") << endmsg;
2774 start_line_grab (al, event);
2778 Editor::start_line_grab (AutomationLine* line, GdkEvent* event)
2782 nframes_t frame_within_region;
2784 /* need to get x coordinate in terms of parent (TimeAxisItemView)
2788 cx = event->button.x;
2789 cy = event->button.y;
2790 line->parent_group().w2i (cx, cy);
2791 frame_within_region = (nframes_t) floor (cx * frames_per_unit);
2793 if (!line->control_points_adjacent (frame_within_region, current_line_drag_info.before,
2794 current_line_drag_info.after)) {
2795 /* no adjacent points */
2799 drag_info.item = &line->grab_item();
2800 drag_info.data = line;
2801 drag_info.motion_callback = &Editor::line_drag_motion_callback;
2802 drag_info.finished_callback = &Editor::line_drag_finished_callback;
2804 start_grab (event, fader_cursor);
2806 double fraction = 1.0 - (cy / line->height());
2808 line->start_drag (0, drag_info.grab_frame, fraction);
2810 set_verbose_canvas_cursor (line->get_verbose_cursor_string (fraction),
2811 drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
2812 show_verbose_canvas_cursor ();
2816 Editor::line_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2818 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
2820 double dy = drag_info.current_pointer_y - drag_info.last_pointer_y;
2822 if (event->button.state & Keyboard::SecondaryModifier) {
2826 double cx = drag_info.current_pointer_x;
2827 double cy = drag_info.grab_y + drag_info.cumulative_y_drag + dy;
2829 // calculate zero crossing point. back off by .01 to stay on the
2830 // positive side of zero
2832 double zero_gain_y = (1.0 - ZERO_GAIN_FRACTION) * line->height() - .01;
2833 line->parent_group().i2w(_unused, zero_gain_y);
2835 // make sure we hit zero when passing through
2836 if ((cy < zero_gain_y and (cy - dy) > zero_gain_y)
2837 or (cy > zero_gain_y and (cy - dy) < zero_gain_y)) {
2841 drag_info.cumulative_y_drag = cy - drag_info.grab_y;
2843 line->parent_group().w2i (cx, cy);
2846 cy = min ((double) line->height(), cy);
2849 fraction = 1.0 - (cy / line->height());
2853 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) {
2859 line->line_drag (current_line_drag_info.before, current_line_drag_info.after, fraction, push);
2861 set_verbose_canvas_cursor_text (line->get_verbose_cursor_string (fraction));
2865 Editor::line_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2867 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
2868 line_drag_motion_callback (item, event);
2873 Editor::start_region_grab (ArdourCanvas::Item* item, GdkEvent* event)
2875 if (selection->regions.empty() || clicked_regionview == 0) {
2879 drag_info.copy = false;
2880 drag_info.item = item;
2881 drag_info.data = clicked_regionview;
2883 if (Config->get_edit_mode() == Splice) {
2884 drag_info.motion_callback = &Editor::region_drag_splice_motion_callback;
2885 drag_info.finished_callback = &Editor::region_drag_splice_finished_callback;
2887 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2888 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2894 TimeAxisView* tvp = clicked_trackview;
2895 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2897 if (tv && tv->is_audio_track()) {
2898 speed = tv->get_diskstream()->speed();
2901 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2902 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2903 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2904 // we want a move threshold
2905 drag_info.want_move_threshold = true;
2907 show_verbose_time_cursor (drag_info.last_frame_position, 10);
2909 begin_reversible_command (_("move region(s)"));
2913 Editor::start_region_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2915 if (selection->regions.empty() || clicked_regionview == 0) {
2919 drag_info.copy = true;
2920 drag_info.item = item;
2921 drag_info.data = clicked_regionview;
2925 TimeAxisView* tv = &clicked_regionview->get_time_axis_view();
2926 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*>(tv);
2929 if (atv && atv->is_audio_track()) {
2930 speed = atv->get_diskstream()->speed();
2933 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2934 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2935 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2936 // we want a move threshold
2937 drag_info.want_move_threshold = true;
2938 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2939 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2940 show_verbose_time_cursor (drag_info.last_frame_position, 10);
2944 Editor::start_region_brush_grab (ArdourCanvas::Item* item, GdkEvent* event)
2946 if (selection->regions.empty() || clicked_regionview == 0 || Config->get_edit_mode() == Splice) {
2950 drag_info.copy = false;
2951 drag_info.item = item;
2952 drag_info.data = clicked_regionview;
2953 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2954 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2959 TimeAxisView* tvp = clicked_trackview;
2960 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2962 if (tv && tv->is_audio_track()) {
2963 speed = tv->get_diskstream()->speed();
2966 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2967 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2968 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2969 // we want a move threshold
2970 drag_info.want_move_threshold = true;
2971 drag_info.brushing = true;
2973 begin_reversible_command (_("Drag region brush"));
2977 Editor::possibly_copy_regions_during_grab (GdkEvent* event)
2979 if (drag_info.copy && drag_info.move_threshold_passed && drag_info.want_move_threshold) {
2981 drag_info.want_move_threshold = false; // don't copy again
2983 /* duplicate the region(s) */
2985 vector<RegionView*> new_regionviews;
2987 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
2990 AudioRegionView* arv;
2995 if ((arv = dynamic_cast<AudioRegionView*>(rv)) == 0) {
2996 /* XXX handle MIDI here */
3000 nrv = new AudioRegionView (*arv);
3001 nrv->get_canvas_group()->show ();
3003 new_regionviews.push_back (nrv);
3006 if (new_regionviews.empty()) {
3010 /* reset selection to new regionviews */
3012 selection->set (new_regionviews);
3014 /* reset drag_info data to reflect the fact that we are dragging the copies */
3016 drag_info.data = new_regionviews.front();
3018 swap_grab (new_regionviews.front()->get_canvas_group (), 0, event->motion.time);
3023 Editor::check_region_drag_possible (AudioTimeAxisView** tv)
3025 /* Which trackview is this ? */
3027 TimeAxisView* tvp = trackview_by_y_position (drag_info.current_pointer_y);
3028 (*tv) = dynamic_cast<AudioTimeAxisView*>(tvp);
3030 /* The region motion is only processed if the pointer is over
3034 if (!(*tv) || !(*tv)->is_audio_track()) {
3035 /* To make sure we hide the verbose canvas cursor when the mouse is
3036 not held over and audiotrack.
3038 hide_verbose_canvas_cursor ();
3045 struct RegionSelectionByPosition {
3046 bool operator() (RegionView*a, RegionView* b) {
3047 return a->region()->position () < b->region()->position();
3052 Editor::region_drag_splice_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
3054 AudioTimeAxisView* tv;
3056 if (!check_region_drag_possible (&tv)) {
3060 if (!drag_info.move_threshold_passed) {
3066 if (drag_info.current_pointer_x - drag_info.grab_x > 0) {
3072 RegionSelection copy (selection->regions);
3074 RegionSelectionByPosition cmp;
3077 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
3079 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*> (&(*i)->get_time_axis_view());
3085 boost::shared_ptr<Playlist> playlist;
3087 if ((playlist = atv->playlist()) == 0) {
3091 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
3096 if (drag_info.current_pointer_frame < (*i)->region()->last_frame() + 1) {
3100 if (drag_info.current_pointer_frame > (*i)->region()->first_frame()) {
3106 playlist->shuffle ((*i)->region(), dir);
3108 drag_info.grab_x = drag_info.current_pointer_x;
3113 Editor::region_drag_splice_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3118 Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
3122 RegionView* rv = reinterpret_cast<RegionView*> (drag_info.data);
3123 nframes_t pending_region_position = 0;
3124 int32_t pointer_y_span = 0, canvas_pointer_y_span = 0, original_pointer_order;
3125 int32_t visible_y_high = 0, visible_y_low = 512; //high meaning higher numbered.. not the height on the screen
3126 bool clamp_y_axis = false;
3127 vector<int32_t> height_list(512) ;
3128 vector<int32_t>::iterator j;
3129 AudioTimeAxisView* tv;
3131 possibly_copy_regions_during_grab (event);
3133 if (!check_region_drag_possible (&tv)) {
3137 original_pointer_order = drag_info.last_trackview->order;
3139 /************************************************************
3141 ************************************************************/
3143 if (drag_info.brushing) {
3144 clamp_y_axis = true;
3149 if ((pointer_y_span = (drag_info.last_trackview->order - tv->order)) != 0) {
3151 int32_t children = 0, numtracks = 0;
3152 // XXX hard coding track limit, oh my, so very very bad
3153 bitset <1024> tracks (0x00);
3154 /* get a bitmask representing the visible tracks */
3156 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
3157 TimeAxisView *tracklist_timeview;
3158 tracklist_timeview = (*i);
3159 AudioTimeAxisView* atv2 = dynamic_cast<AudioTimeAxisView*>(tracklist_timeview);
3160 list<TimeAxisView*> children_list;
3162 /* zeroes are audio tracks. ones are other types. */
3164 if (!atv2->hidden()) {
3166 if (visible_y_high < atv2->order) {
3167 visible_y_high = atv2->order;
3169 if (visible_y_low > atv2->order) {
3170 visible_y_low = atv2->order;
3173 if (!atv2->is_audio_track()) {
3174 tracks = tracks |= (0x01 << atv2->order);
3177 height_list[atv2->order] = (*i)->height;
3179 if ((children_list = atv2->get_child_list()).size() > 0) {
3180 for (list<TimeAxisView*>::iterator j = children_list.begin(); j != children_list.end(); ++j) {
3181 tracks = tracks |= (0x01 << (atv2->order + children));
3182 height_list[atv2->order + children] = (*j)->height;
3190 /* find the actual span according to the canvas */
3192 canvas_pointer_y_span = pointer_y_span;
3193 if (drag_info.last_trackview->order >= tv->order) {
3195 for (y = tv->order; y < drag_info.last_trackview->order; y++) {
3196 if (height_list[y] == 0 ) {
3197 canvas_pointer_y_span--;
3202 for (y = drag_info.last_trackview->order;y <= tv->order; y++) {
3203 if ( height_list[y] == 0 ) {
3204 canvas_pointer_y_span++;
3209 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3210 RegionView* rv2 = (*i);
3211 double ix1, ix2, iy1, iy2;
3214 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3215 rv2->get_canvas_group()->i2w (ix1, iy1);
3216 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3217 RouteTimeAxisView* atv2 = dynamic_cast<RouteTimeAxisView*>(tvp2);
3219 if (atv2->order != original_pointer_order) {
3220 /* this isn't the pointer track */
3222 if (canvas_pointer_y_span > 0) {
3224 /* moving up the canvas */
3225 if ((atv2->order - canvas_pointer_y_span) >= visible_y_low) {
3227 int32_t visible_tracks = 0;
3228 while (visible_tracks < canvas_pointer_y_span ) {
3231 while (height_list[atv2->order - (visible_tracks - n)] == 0) {
3232 /* we're passing through a hidden track */
3237 if (tracks[atv2->order - (canvas_pointer_y_span - n)] != 0x00) {
3238 clamp_y_axis = true;
3242 clamp_y_axis = true;
3245 } else if (canvas_pointer_y_span < 0) {
3247 /*moving down the canvas*/
3249 if ((atv2->order - (canvas_pointer_y_span - n)) <= visible_y_high) { // we will overflow
3252 int32_t visible_tracks = 0;
3254 while (visible_tracks > canvas_pointer_y_span ) {
3257 while (height_list[atv2->order - (visible_tracks - n)] == 0) {
3261 if ( tracks[atv2->order - ( canvas_pointer_y_span - n)] != 0x00) {
3262 clamp_y_axis = true;
3267 clamp_y_axis = true;
3273 /* this is the pointer's track */
3274 if ((atv2->order - pointer_y_span) > visible_y_high) { // we will overflow
3275 clamp_y_axis = true;
3276 } else if ((atv2->order - pointer_y_span) < visible_y_low) { // we will underflow
3277 clamp_y_axis = true;
3285 } else if (drag_info.last_trackview == tv) {
3286 clamp_y_axis = true;
3290 if (!clamp_y_axis) {
3291 drag_info.last_trackview = tv;
3294 /************************************************************
3296 ************************************************************/
3298 /* compute the amount of pointer motion in frames, and where
3299 the region would be if we moved it by that much.
3302 if (drag_info.move_threshold_passed) {
3304 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
3306 nframes_t sync_frame;
3307 nframes_t sync_offset;
3310 pending_region_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
3312 sync_offset = rv->region()->sync_offset (sync_dir);
3314 /* we don't handle a sync point that lies before zero.
3316 if (sync_dir >= 0 || (sync_dir < 0 && pending_region_position >= sync_offset)) {
3317 sync_frame = pending_region_position + (sync_dir*sync_offset);
3319 /* we snap if the snap modifier is not enabled.
3322 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3323 snap_to (sync_frame);
3326 pending_region_position = rv->region()->adjust_to_sync (sync_frame);
3329 pending_region_position = drag_info.last_frame_position;
3333 pending_region_position = 0;
3336 if (pending_region_position > max_frames - rv->region()->length()) {
3337 pending_region_position = drag_info.last_frame_position;
3340 // printf ("3: pending_region_position= %lu %lu\n", pending_region_position, drag_info.last_frame_position );
3342 if (pending_region_position != drag_info.last_frame_position && !drag_info.x_constrained) {
3344 /* now compute the canvas unit distance we need to move the regionview
3345 to make it appear at the new location.
3348 if (pending_region_position > drag_info.last_frame_position) {
3349 x_delta = ((double) (pending_region_position - drag_info.last_frame_position) / frames_per_unit);
3351 x_delta = -((double) (drag_info.last_frame_position - pending_region_position) / frames_per_unit);
3354 drag_info.last_frame_position = pending_region_position;
3361 /* threshold not passed */
3366 /*************************************************************
3368 ************************************************************/
3370 if (x_delta == 0 && (pointer_y_span == 0)) {
3371 /* haven't reached next snap point, and we're not switching
3372 trackviews. nothing to do.
3379 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3381 RegionView* rv2 = (*i);
3383 // If any regionview is at zero, we need to know so we can stop further leftward motion.
3385 double ix1, ix2, iy1, iy2;
3386 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3387 rv2->get_canvas_group()->i2w (ix1, iy1);
3396 /*************************************************************
3398 ************************************************************/
3402 if (drag_info.first_move) {
3403 if (drag_info.move_threshold_passed) {
3414 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
3415 const list<RegionView*>& layered_regions = selection->regions.by_layer();
3417 for (list<RegionView*>::const_iterator i = layered_regions.begin(); i != layered_regions.end(); ++i) {
3419 RegionView* rv = (*i);
3420 double ix1, ix2, iy1, iy2;
3421 int32_t temp_pointer_y_span = pointer_y_span;
3423 /* get item BBox, which will be relative to parent. so we have
3424 to query on a child, then convert to world coordinates using
3428 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3429 rv->get_canvas_group()->i2w (ix1, iy1);
3430 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3431 AudioTimeAxisView* canvas_atv = dynamic_cast<AudioTimeAxisView*>(tvp2);
3432 AudioTimeAxisView* temp_atv;
3434 if ((pointer_y_span != 0) && !clamp_y_axis) {
3437 for (j = height_list.begin(); j!= height_list.end(); j++) {
3438 if (x == canvas_atv->order) {
3439 /* we found the track the region is on */
3440 if (x != original_pointer_order) {
3441 /*this isn't from the same track we're dragging from */
3442 temp_pointer_y_span = canvas_pointer_y_span;
3444 while (temp_pointer_y_span > 0) {
3445 /* we're moving up canvas-wise,
3446 so we need to find the next track height
3448 if (j != height_list.begin()) {
3451 if (x != original_pointer_order) {
3452 /* we're not from the dragged track, so ignore hidden tracks. */
3454 temp_pointer_y_span++;
3458 temp_pointer_y_span--;
3460 while (temp_pointer_y_span < 0) {
3462 if (x != original_pointer_order) {
3464 temp_pointer_y_span--;
3468 if (j != height_list.end()) {
3471 temp_pointer_y_span++;
3473 /* find out where we'll be when we move and set height accordingly */
3475 tvp2 = trackview_by_y_position (iy1 + y_delta);
3476 temp_atv = dynamic_cast<AudioTimeAxisView*>(tvp2);
3477 rv->set_height (temp_atv->height);
3479 /* if you un-comment the following, the region colours will follow the track colours whilst dragging,
3480 personally, i think this can confuse things, but never mind.
3483 //const GdkColor& col (temp_atv->view->get_region_color());
3484 //rv->set_color (const_cast<GdkColor&>(col));
3491 /* prevent the regionview from being moved to before
3492 the zero position on the canvas.
3497 if (-x_delta > ix1) {
3500 } else if ((x_delta > 0) && (rv->region()->last_frame() > max_frames - x_delta)) {
3501 x_delta = max_frames - rv->region()->last_frame();
3505 if (drag_info.first_move) {
3507 /* hide any dependent views */
3509 rv->get_time_axis_view().hide_dependent_views (*rv);
3511 /* this is subtle. raising the regionview itself won't help,
3512 because raise_to_top() just puts the item on the top of
3513 its parent's stack. so, we need to put the trackview canvas_display group
3514 on the top, since its parent is the whole canvas.
3517 rv->get_canvas_group()->raise_to_top();
3518 rv->get_time_axis_view().canvas_display->raise_to_top();
3519 cursor_group->raise_to_top();
3520 rv->fake_set_opaque (true);
3523 if (drag_info.brushing) {
3524 mouse_brush_insert_region (rv, pending_region_position);
3526 rv->move (x_delta, y_delta);
3529 } /* foreach region */
3533 if (drag_info.first_move && drag_info.move_threshold_passed) {
3534 cursor_group->raise_to_top();
3535 drag_info.first_move = false;
3538 if (x_delta != 0 && !drag_info.brushing) {
3539 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3544 Editor::region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3547 RegionView* rv = reinterpret_cast<RegionView *> (drag_info.data);
3548 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
3549 bool nocommit = true;
3551 RouteTimeAxisView* atv;
3552 bool regionview_y_movement;
3553 bool regionview_x_movement;
3554 vector<RegionView*> copies;
3556 /* first_move is set to false if the regionview has been moved in the
3560 if (drag_info.first_move) {
3567 /* The regionview has been moved at some stage during the grab so we need
3568 to account for any mouse movement between this event and the last one.
3571 region_drag_motion_callback (item, event);
3573 if (Config->get_edit_mode() == Splice && !pre_drag_region_selection.empty()) {
3574 selection->set (pre_drag_region_selection);
3575 pre_drag_region_selection.clear ();
3578 if (drag_info.brushing) {
3579 /* all changes were made during motion event handlers */
3581 if (drag_info.copy) {
3582 for (list<RegionView*>::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
3583 copies.push_back (*i);
3590 /* adjust for track speed */
3593 atv = dynamic_cast<AudioTimeAxisView*> (drag_info.last_trackview);
3594 if (atv && atv->get_diskstream()) {
3595 speed = atv->get_diskstream()->speed();
3598 regionview_x_movement = (drag_info.last_frame_position != (nframes_t) (rv->region()->position()/speed));
3599 regionview_y_movement = (drag_info.last_trackview != &rv->get_time_axis_view());
3601 //printf ("last_frame: %s position is %lu %g\n", rv->get_time_axis_view().name().c_str(), drag_info.last_frame_position, speed);
3602 //printf ("last_rackview: %s \n", drag_info.last_trackview->name().c_str());
3606 if (drag_info.copy) {
3607 if (drag_info.x_constrained) {
3608 op_string = _("fixed time region copy");
3610 op_string = _("region copy");
3613 if (drag_info.x_constrained) {
3614 op_string = _("fixed time region drag");
3616 op_string = _("region drag");
3620 begin_reversible_command (op_string);
3622 if (regionview_y_movement) {
3624 /* moved to a different audio track. */
3626 vector<RegionView*> new_selection;
3628 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ) {
3630 RegionView* rv = (*i);
3632 double ix1, ix2, iy1, iy2;
3634 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3635 rv->get_canvas_group()->i2w (ix1, iy1);
3636 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3637 AudioTimeAxisView* atv2 = dynamic_cast<AudioTimeAxisView*>(tvp2);
3639 boost::shared_ptr<Playlist> from_playlist = rv->region()->playlist();
3640 boost::shared_ptr<Playlist> to_playlist = atv2->playlist();
3642 where = (nframes_t) (unit_to_frame (ix1) * speed);
3643 boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region()));
3645 /* undo the previous hide_dependent_views so that xfades don't
3646 disappear on copying regions
3649 rv->get_time_axis_view().reveal_dependent_views (*rv);
3651 if (!drag_info.copy) {
3653 /* the region that used to be in the old playlist is not
3654 moved to the new one - we make a copy of it. as a result,
3655 any existing editor for the region should no longer be
3659 rv->hide_region_editor();
3660 rv->fake_set_opaque (false);
3662 session->add_command (new MementoCommand<Playlist>(*from_playlist, &from_playlist->get_state(), 0));
3663 from_playlist->remove_region ((rv->region()));
3664 session->add_command (new MementoCommand<Playlist>(*from_playlist, 0, &from_playlist->get_state()));
3668 /* the regionview we dragged around is a temporary copy, queue it for deletion */
3670 copies.push_back (rv);
3673 latest_regionviews.clear ();
3675 sigc::connection c = atv2->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3676 session->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
3677 to_playlist->add_region (new_region, where);
3678 session->add_command (new MementoCommand<Playlist>(*to_playlist, 0, &to_playlist->get_state()));
3681 if (!latest_regionviews.empty()) {
3682 new_selection.insert (new_selection.end(), latest_regionviews.begin(), latest_regionviews.end());
3685 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
3686 was selected in all of them, then removing it from the playlist will have removed all
3687 trace of it from the selection (i.e. there were N regions selected, we removed 1,
3688 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
3689 corresponding regionview, and the selection is now empty).
3691 this could have invalidated any and all iterators into the region selection.
3693 the heuristic we use here is: if the region selection is empty, break out of the loop
3694 here. if the region selection is not empty, then restart the loop because we know that
3695 we must have removed at least the region(view) we've just been working on as well as any
3696 that we processed on previous iterations.
3698 EXCEPT .... if we are doing a copy drag, then the selection hasn't been modified and
3699 we can just iterate.
3702 if (drag_info.copy) {
3705 if (selection->regions.empty()) {
3708 i = selection->regions.by_layer().begin();
3713 selection->set (new_selection);
3717 /* motion within a single track */
3719 list<RegionView*> regions = selection->regions.by_layer();
3721 if (drag_info.copy) {
3722 selection->clear_regions();
3725 for (list<RegionView*>::iterator i = regions.begin(); i != regions.end(); ++i) {
3729 if (rv->region()->locked()) {
3734 if (regionview_x_movement) {
3735 double ownspeed = 1.0;
3736 atv = dynamic_cast<AudioTimeAxisView*> (&(rv->get_time_axis_view()));
3738 if (atv && atv->get_diskstream()) {
3739 ownspeed = atv->get_diskstream()->speed();
3742 /* base the new region position on the current position of the regionview.*/
3744 double ix1, ix2, iy1, iy2;
3746 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3747 rv->get_canvas_group()->i2w (ix1, iy1);
3748 where = (nframes_t) (unit_to_frame (ix1) * ownspeed);
3752 where = rv->region()->position();
3755 boost::shared_ptr<Playlist> to_playlist = rv->region()->playlist();
3757 assert (to_playlist);
3761 session->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
3763 if (drag_info.copy) {
3765 boost::shared_ptr<Region> newregion;
3766 boost::shared_ptr<Region> ar;
3768 if ((ar = boost::dynamic_pointer_cast<AudioRegion>(rv->region())) != 0) {
3769 newregion = RegionFactory::create (ar);
3771 /* XXX MIDI HERE drobilla */
3777 latest_regionviews.clear ();
3778 sigc::connection c = atv->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3779 to_playlist->add_region (newregion, (nframes_t) (where * atv->get_diskstream()->speed()));
3782 if (!latest_regionviews.empty()) {
3783 // XXX why just the first one ? we only expect one
3784 atv->reveal_dependent_views (*latest_regionviews.front());
3785 selection->add (latest_regionviews);
3788 /* if the original region was locked, we don't care for the new one */
3790 newregion->set_locked (false);
3794 /* just change the model */
3796 rv->region()->set_position (where, (void*) this);
3802 session->add_command (new MementoCommand<Playlist>(*to_playlist, 0, &to_playlist->get_state()));
3804 if (drag_info.copy) {
3805 copies.push_back (rv);
3813 commit_reversible_command ();
3816 for (vector<RegionView*>::iterator x = copies.begin(); x != copies.end(); ++x) {
3822 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
3824 /* Either add to or set the set the region selection, unless
3825 this is an alignment click (control used)
3828 if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
3829 TimeAxisView* tv = &rv.get_time_axis_view();
3830 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*>(tv);
3832 if (atv && atv->is_audio_track()) {
3833 speed = atv->get_diskstream()->speed();
3836 nframes64_t where = get_preferred_edit_position();
3840 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
3842 align_region (rv.region(), SyncPoint, (nframes_t) (where * speed));
3844 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
3846 align_region (rv.region(), End, (nframes_t) (where * speed));
3850 align_region (rv.region(), Start, (nframes_t) (where * speed));
3857 Editor::show_verbose_time_cursor (nframes_t frame, double offset, double xpos, double ypos)
3863 nframes_t frame_rate;
3870 switch (Profile->get_small_screen() ? ARDOUR_UI::instance()->primary_clock.mode () : ARDOUR_UI::instance()->secondary_clock.mode ()) {
3871 case AudioClock::BBT:
3872 session->bbt_time (frame, bbt);
3873 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, bbt.bars, bbt.beats, bbt.ticks);
3876 case AudioClock::SMPTE:
3877 session->smpte_time (frame, smpte);
3878 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
3881 case AudioClock::MinSec:
3882 /* XXX this is copied from show_verbose_duration_cursor() */
3883 frame_rate = session->frame_rate();
3884 hours = frame / (frame_rate * 3600);
3885 frame = frame % (frame_rate * 3600);
3886 mins = frame / (frame_rate * 60);
3887 frame = frame % (frame_rate * 60);
3888 secs = (float) frame / (float) frame_rate;
3889 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
3893 snprintf (buf, sizeof(buf), "%u", frame);
3897 if (xpos >= 0 && ypos >=0) {
3898 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
3901 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
3903 show_verbose_canvas_cursor ();
3907 Editor::show_verbose_duration_cursor (nframes_t start, nframes_t end, double offset, double xpos, double ypos)
3914 nframes_t distance, frame_rate;
3916 Meter meter_at_start(session->tempo_map().meter_at(start));
3922 switch (ARDOUR_UI::instance()->secondary_clock.mode ()) {
3923 case AudioClock::BBT:
3924 session->bbt_time (start, sbbt);
3925 session->bbt_time (end, ebbt);
3928 /* XXX this computation won't work well if the
3929 user makes a selection that spans any meter changes.
3932 ebbt.bars -= sbbt.bars;
3933 if (ebbt.beats >= sbbt.beats) {
3934 ebbt.beats -= sbbt.beats;
3937 ebbt.beats = int(meter_at_start.beats_per_bar()) + ebbt.beats - sbbt.beats;
3939 if (ebbt.ticks >= sbbt.ticks) {
3940 ebbt.ticks -= sbbt.ticks;
3943 ebbt.ticks = int(Meter::ticks_per_beat) + ebbt.ticks - sbbt.ticks;
3946 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, ebbt.bars, ebbt.beats, ebbt.ticks);
3949 case AudioClock::SMPTE:
3950 session->smpte_duration (end - start, smpte);
3951 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
3954 case AudioClock::MinSec:
3955 /* XXX this stuff should be elsewhere.. */
3956 distance = end - start;
3957 frame_rate = session->frame_rate();
3958 hours = distance / (frame_rate * 3600);
3959 distance = distance % (frame_rate * 3600);
3960 mins = distance / (frame_rate * 60);
3961 distance = distance % (frame_rate * 60);
3962 secs = (float) distance / (float) frame_rate;
3963 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
3967 snprintf (buf, sizeof(buf), "%u", end - start);
3971 if (xpos >= 0 && ypos >=0) {
3972 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
3975 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
3977 show_verbose_canvas_cursor ();
3981 Editor::collect_new_region_view (RegionView* rv)
3983 latest_regionviews.push_back (rv);
3987 Editor::start_selection_grab (ArdourCanvas::Item* item, GdkEvent* event)
3989 if (clicked_regionview == 0) {
3993 /* lets try to create new Region for the selection */
3995 vector<boost::shared_ptr<AudioRegion> > new_regions;
3996 create_region_from_selection (new_regions);
3998 if (new_regions.empty()) {
4002 /* XXX fix me one day to use all new regions */
4004 boost::shared_ptr<Region> region (new_regions.front());
4006 /* add it to the current stream/playlist.
4008 tricky: the streamview for the track will add a new regionview. we will
4009 catch the signal it sends when it creates the regionview to
4010 set the regionview we want to then drag.
4013 latest_regionviews.clear();
4014 sigc::connection c = clicked_audio_trackview->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
4016 /* A selection grab currently creates two undo/redo operations, one for
4017 creating the new region and another for moving it.
4020 begin_reversible_command (_("selection grab"));
4022 boost::shared_ptr<Playlist> playlist = clicked_trackview->playlist();
4024 XMLNode *before = &(playlist->get_state());
4025 clicked_trackview->playlist()->add_region (region, selection->time[clicked_selection].start);
4026 XMLNode *after = &(playlist->get_state());
4027 session->add_command(new MementoCommand<Playlist>(*playlist, before, after));
4029 commit_reversible_command ();
4033 if (latest_regionviews.empty()) {
4034 /* something went wrong */
4038 /* we need to deselect all other regionviews, and select this one
4039 i'm ignoring undo stuff, because the region creation will take care of it
4041 selection->set (latest_regionviews);
4043 drag_info.item = latest_regionviews.front()->get_canvas_group();
4044 drag_info.data = latest_regionviews.front();
4045 drag_info.motion_callback = &Editor::region_drag_motion_callback;
4046 drag_info.finished_callback = &Editor::region_drag_finished_callback;
4050 drag_info.last_trackview = clicked_trackview;
4051 drag_info.last_frame_position = latest_regionviews.front()->region()->position();
4052 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
4054 show_verbose_time_cursor (drag_info.last_frame_position, 10);
4058 Editor::cancel_selection ()
4060 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4061 (*i)->hide_selection ();
4063 selection->clear ();
4064 clicked_selection = 0;
4068 Editor::start_selection_op (ArdourCanvas::Item* item, GdkEvent* event, SelectionOp op)
4070 nframes_t start = 0;
4077 drag_info.item = item;
4078 drag_info.motion_callback = &Editor::drag_selection;
4079 drag_info.finished_callback = &Editor::end_selection_op;
4084 case CreateSelection:
4085 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
4086 drag_info.copy = true;
4088 drag_info.copy = false;
4090 start_grab (event, selector_cursor);
4093 case SelectionStartTrim:
4094 if (clicked_trackview) {
4095 clicked_trackview->order_selection_trims (item, true);
4097 start_grab (event, trimmer_cursor);
4098 start = selection->time[clicked_selection].start;
4099 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
4102 case SelectionEndTrim:
4103 if (clicked_trackview) {
4104 clicked_trackview->order_selection_trims (item, false);
4106 start_grab (event, trimmer_cursor);
4107 end = selection->time[clicked_selection].end;
4108 drag_info.pointer_frame_offset = drag_info.grab_frame - end;
4112 start = selection->time[clicked_selection].start;
4114 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
4118 if (selection_op == SelectionMove) {
4119 show_verbose_time_cursor(start, 10);
4121 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4126 Editor::drag_selection (ArdourCanvas::Item* item, GdkEvent* event)
4128 nframes_t start = 0;
4131 nframes_t pending_position;
4133 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
4134 pending_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
4136 pending_position = 0;
4139 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4140 snap_to (pending_position);
4143 /* only alter selection if the current frame is
4144 different from the last frame position (adjusted)
4147 if (pending_position == drag_info.last_pointer_frame) return;
4149 switch (selection_op) {
4150 case CreateSelection:
4152 if (drag_info.first_move) {
4153 snap_to (drag_info.grab_frame);
4156 if (pending_position < drag_info.grab_frame) {
4157 start = pending_position;
4158 end = drag_info.grab_frame;
4160 end = pending_position;
4161 start = drag_info.grab_frame;
4164 /* first drag: Either add to the selection
4165 or create a new selection->
4168 if (drag_info.first_move) {
4170 begin_reversible_command (_("range selection"));
4172 if (drag_info.copy) {
4173 /* adding to the selection */
4174 clicked_selection = selection->add (start, end);
4175 drag_info.copy = false;
4177 /* new selection-> */
4178 clicked_selection = selection->set (clicked_trackview, start, end);
4183 case SelectionStartTrim:
4185 if (drag_info.first_move) {
4186 begin_reversible_command (_("trim selection start"));
4189 start = selection->time[clicked_selection].start;
4190 end = selection->time[clicked_selection].end;
4192 if (pending_position > end) {
4195 start = pending_position;
4199 case SelectionEndTrim:
4201 if (drag_info.first_move) {
4202 begin_reversible_command (_("trim selection end"));
4205 start = selection->time[clicked_selection].start;
4206 end = selection->time[clicked_selection].end;
4208 if (pending_position < start) {
4211 end = pending_position;
4218 if (drag_info.first_move) {
4219 begin_reversible_command (_("move selection"));
4222 start = selection->time[clicked_selection].start;
4223 end = selection->time[clicked_selection].end;
4225 length = end - start;
4227 start = pending_position;
4230 end = start + length;
4235 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
4236 start_canvas_autoscroll (1);
4240 selection->replace (clicked_selection, start, end);
4243 drag_info.last_pointer_frame = pending_position;
4244 drag_info.first_move = false;
4246 if (selection_op == SelectionMove) {
4247 show_verbose_time_cursor(start, 10);
4249 show_verbose_time_cursor(pending_position, 10);
4254 Editor::end_selection_op (ArdourCanvas::Item* item, GdkEvent* event)
4256 if (!drag_info.first_move) {
4257 drag_selection (item, event);
4258 /* XXX this is not object-oriented programming at all. ick */
4259 if (selection->time.consolidate()) {
4260 selection->TimeChanged ();
4262 commit_reversible_command ();
4264 /* just a click, no pointer movement.*/
4266 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
4268 selection->clear_time();
4273 /* XXX what happens if its a music selection? */
4274 session->set_audio_range (selection->time);
4275 stop_canvas_autoscroll ();
4279 Editor::start_trim (ArdourCanvas::Item* item, GdkEvent* event)
4282 TimeAxisView* tvp = clicked_trackview;
4283 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
4285 if (tv && tv->is_audio_track()) {
4286 speed = tv->get_diskstream()->speed();
4289 nframes_t region_start = (nframes_t) (clicked_regionview->region()->position() / speed);
4290 nframes_t region_end = (nframes_t) (clicked_regionview->region()->last_frame() / speed);
4291 nframes_t region_length = (nframes_t) (clicked_regionview->region()->length() / speed);
4293 //drag_info.item = clicked_regionview->get_name_highlight();
4294 drag_info.item = item;
4295 drag_info.motion_callback = &Editor::trim_motion_callback;
4296 drag_info.finished_callback = &Editor::trim_finished_callback;
4298 start_grab (event, trimmer_cursor);
4300 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
4301 trim_op = ContentsTrim;
4303 /* These will get overridden for a point trim.*/
4304 if (drag_info.current_pointer_frame < (region_start + region_length/2)) {
4305 /* closer to start */
4306 trim_op = StartTrim;
4307 } else if (drag_info.current_pointer_frame > (region_end - region_length/2)) {
4315 show_verbose_time_cursor(region_start, 10);
4318 show_verbose_time_cursor(region_end, 10);
4321 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4327 Editor::trim_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
4329 RegionView* rv = clicked_regionview;
4330 nframes_t frame_delta = 0;
4331 bool left_direction;
4332 bool obey_snap = !Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier());
4334 /* snap modifier works differently here..
4335 its' current state has to be passed to the
4336 various trim functions in order to work properly
4340 TimeAxisView* tvp = clicked_trackview;
4341 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4342 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
4344 if (tv && tv->is_audio_track()) {
4345 speed = tv->get_diskstream()->speed();
4348 if (drag_info.last_pointer_frame > drag_info.current_pointer_frame) {
4349 left_direction = true;
4351 left_direction = false;
4355 snap_to (drag_info.current_pointer_frame);
4358 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
4362 if (drag_info.first_move) {
4368 trim_type = "Region start trim";
4371 trim_type = "Region end trim";
4374 trim_type = "Region content trim";
4378 begin_reversible_command (trim_type);
4380 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4381 (*i)->fake_set_opaque(false);
4382 (*i)->region()->freeze ();
4384 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
4386 arv->temporarily_hide_envelope ();
4388 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4389 insert_result = motion_frozen_playlists.insert (pl);
4390 if (insert_result.second) {
4391 session->add_command(new MementoCommand<Playlist>(*pl, &pl->get_state(), 0));
4396 if (left_direction) {
4397 frame_delta = (drag_info.last_pointer_frame - drag_info.current_pointer_frame);
4399 frame_delta = (drag_info.current_pointer_frame - drag_info.last_pointer_frame);
4404 if ((left_direction == false) && (drag_info.current_pointer_frame <= rv->region()->first_frame()/speed)) {
4407 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4408 single_start_trim (**i, frame_delta, left_direction, obey_snap);
4414 if ((left_direction == true) && (drag_info.current_pointer_frame > (nframes_t) (rv->region()->last_frame()/speed))) {
4417 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4418 single_end_trim (**i, frame_delta, left_direction, obey_snap);
4425 bool swap_direction = false;
4427 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
4428 swap_direction = true;
4431 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4432 i != selection->regions.by_layer().end(); ++i)
4434 single_contents_trim (**i, frame_delta, left_direction, swap_direction, obey_snap);
4442 show_verbose_time_cursor((nframes_t) (rv->region()->position()/speed), 10);
4445 show_verbose_time_cursor((nframes_t) (rv->region()->last_frame()/speed), 10);
4448 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4452 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4453 drag_info.first_move = false;
4457 Editor::single_contents_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool swap_direction, bool obey_snap)
4459 boost::shared_ptr<Region> region (rv.region());
4461 if (region->locked()) {
4465 nframes_t new_bound;
4468 TimeAxisView* tvp = clicked_trackview;
4469 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4471 if (tv && tv->is_audio_track()) {
4472 speed = tv->get_diskstream()->speed();
4475 if (left_direction) {
4476 if (swap_direction) {
4477 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4479 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4482 if (swap_direction) {
4483 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4485 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4490 snap_to (new_bound);
4492 region->trim_start ((nframes_t) (new_bound * speed), this);
4493 rv.region_changed (StartChanged);
4497 Editor::single_start_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool obey_snap)
4499 boost::shared_ptr<Region> region (rv.region());
4501 if (region->locked()) {
4505 nframes_t new_bound;
4508 TimeAxisView* tvp = clicked_trackview;
4509 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
4511 if (tv && tv->is_audio_track()) {
4512 speed = tv->get_diskstream()->speed();
4515 if (left_direction) {
4516 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4518 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4522 snap_to (new_bound, (left_direction ? 0 : 1));
4525 region->trim_front ((nframes_t) (new_bound * speed), this);
4527 rv.region_changed (Change (LengthChanged|PositionChanged|StartChanged));
4531 Editor::single_end_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool obey_snap)
4533 boost::shared_ptr<Region> region (rv.region());
4535 if (region->locked()) {
4539 nframes_t new_bound;
4542 TimeAxisView* tvp = clicked_trackview;
4543 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
4545 if (tv && tv->is_audio_track()) {
4546 speed = tv->get_diskstream()->speed();
4549 if (left_direction) {
4550 new_bound = (nframes_t) ((region->last_frame() + 1)/speed) - frame_delta;
4552 new_bound = (nframes_t) ((region->last_frame() + 1)/speed) + frame_delta;
4556 snap_to (new_bound);
4558 region->trim_end ((nframes_t) (new_bound * speed), this);
4559 rv.region_changed (LengthChanged);
4563 Editor::trim_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
4565 if (!drag_info.first_move) {
4566 trim_motion_callback (item, event);
4568 if (!selection->selected (clicked_regionview)) {
4569 thaw_region_after_trim (*clicked_regionview);
4572 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4573 i != selection->regions.by_layer().end(); ++i)
4575 thaw_region_after_trim (**i);
4576 (*i)->fake_set_opaque (true);
4580 for (set<boost::shared_ptr<Playlist> >::iterator p = motion_frozen_playlists.begin(); p != motion_frozen_playlists.end(); ++p) {
4582 session->add_command (new MementoCommand<Playlist>(*(*p).get(), 0, &(*p)->get_state()));
4585 motion_frozen_playlists.clear ();
4587 commit_reversible_command();
4589 /* no mouse movement */
4595 Editor::point_trim (GdkEvent* event)
4597 RegionView* rv = clicked_regionview;
4598 nframes_t new_bound = drag_info.current_pointer_frame;
4600 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4601 snap_to (new_bound);
4604 /* Choose action dependant on which button was pressed */
4605 switch (event->button.button) {
4607 trim_op = StartTrim;
4608 begin_reversible_command (_("Start point trim"));
4610 if (selection->selected (rv)) {
4612 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4613 i != selection->regions.by_layer().end(); ++i)
4615 if (!(*i)->region()->locked()) {
4616 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4617 XMLNode &before = pl->get_state();
4618 (*i)->region()->trim_front (new_bound, this);
4619 XMLNode &after = pl->get_state();
4620 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4626 if (!rv->region()->locked()) {
4627 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
4628 XMLNode &before = pl->get_state();
4629 rv->region()->trim_front (new_bound, this);
4630 XMLNode &after = pl->get_state();
4631 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4635 commit_reversible_command();
4640 begin_reversible_command (_("End point trim"));
4642 if (selection->selected (rv)) {
4644 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
4646 if (!(*i)->region()->locked()) {
4647 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4648 XMLNode &before = pl->get_state();
4649 (*i)->region()->trim_end (new_bound, this);
4650 XMLNode &after = pl->get_state();
4651 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4657 if (!rv->region()->locked()) {
4658 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
4659 XMLNode &before = pl->get_state();
4660 rv->region()->trim_end (new_bound, this);
4661 XMLNode &after = pl->get_state();
4662 session->add_command (new MementoCommand<Playlist>(*pl.get(), &before, &after));
4666 commit_reversible_command();
4675 Editor::thaw_region_after_trim (RegionView& rv)
4677 boost::shared_ptr<Region> region (rv.region());
4679 if (region->locked()) {
4683 region->thaw (_("trimmed region"));
4684 XMLNode &after = region->playlist()->get_state();
4685 session->add_command (new MementoCommand<Playlist>(*(region->playlist()), 0, &after));
4687 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(&rv);
4689 arv->unhide_envelope ();
4693 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* event)
4698 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
4699 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
4703 Location* location = find_location_from_marker (marker, is_start);
4704 location->set_hidden (true, this);
4709 Editor::start_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event, RangeMarkerOp op)
4715 drag_info.item = item;
4716 drag_info.motion_callback = &Editor::drag_range_markerbar_op;
4717 drag_info.finished_callback = &Editor::end_range_markerbar_op;
4719 range_marker_op = op;
4721 if (!temp_location) {
4722 temp_location = new Location;
4726 case CreateRangeMarker:
4727 case CreateTransportMarker:
4728 case CreateCDMarker:
4730 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
4731 drag_info.copy = true;
4733 drag_info.copy = false;
4735 start_grab (event, selector_cursor);
4739 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4744 Editor::drag_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4746 nframes_t start = 0;
4748 ArdourCanvas::SimpleRect *crect;
4750 switch (range_marker_op) {
4751 case CreateRangeMarker:
4752 crect = range_bar_drag_rect;
4754 case CreateTransportMarker:
4755 crect = transport_bar_drag_rect;
4757 case CreateCDMarker:
4758 crect = cd_marker_bar_drag_rect;
4761 cerr << "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()" << endl;
4766 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4767 snap_to (drag_info.current_pointer_frame);
4770 /* only alter selection if the current frame is
4771 different from the last frame position.
4774 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4776 switch (range_marker_op) {
4777 case CreateRangeMarker:
4778 case CreateTransportMarker:
4779 case CreateCDMarker:
4780 if (drag_info.first_move) {
4781 snap_to (drag_info.grab_frame);
4784 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4785 start = drag_info.current_pointer_frame;
4786 end = drag_info.grab_frame;
4788 end = drag_info.current_pointer_frame;
4789 start = drag_info.grab_frame;
4792 /* first drag: Either add to the selection
4793 or create a new selection.
4796 if (drag_info.first_move) {
4798 temp_location->set (start, end);
4802 update_marker_drag_item (temp_location);
4803 range_marker_drag_rect->show();
4804 range_marker_drag_rect->raise_to_top();
4810 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
4811 start_canvas_autoscroll (1);
4815 temp_location->set (start, end);
4817 double x1 = frame_to_pixel (start);
4818 double x2 = frame_to_pixel (end);
4819 crect->property_x1() = x1;
4820 crect->property_x2() = x2;
4822 update_marker_drag_item (temp_location);
4825 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4826 drag_info.first_move = false;
4828 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4833 Editor::end_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4835 Location * newloc = 0;
4839 if (!drag_info.first_move) {
4840 drag_range_markerbar_op (item, event);
4842 switch (range_marker_op) {
4843 case CreateRangeMarker:
4844 case CreateCDMarker:
4846 begin_reversible_command (_("new range marker"));
4847 XMLNode &before = session->locations()->get_state();
4848 session->locations()->next_available_name(rangename,"unnamed");
4849 if (range_marker_op == CreateCDMarker) {
4850 flags = Location::IsRangeMarker|Location::IsCDMarker;
4851 cd_marker_bar_drag_rect->hide();
4854 flags = Location::IsRangeMarker;
4855 range_bar_drag_rect->hide();
4857 newloc = new Location(temp_location->start(), temp_location->end(), rangename, (Location::Flags) flags);
4858 session->locations()->add (newloc, true);
4859 XMLNode &after = session->locations()->get_state();
4860 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
4861 commit_reversible_command ();
4863 range_marker_drag_rect->hide();
4867 case CreateTransportMarker:
4868 // popup menu to pick loop or punch
4869 new_transport_marker_context_menu (&event->button, item);
4874 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
4876 if (Keyboard::no_modifier_keys_pressed (&event->button) && range_marker_op != CreateCDMarker) {
4881 start = session->locations()->first_mark_before (drag_info.grab_frame);
4882 end = session->locations()->first_mark_after (drag_info.grab_frame);
4884 if (end == max_frames) {
4885 end = session->current_end_frame ();
4889 start = session->current_start_frame ();
4892 switch (mouse_mode) {
4894 /* find the two markers on either side and then make the selection from it */
4895 select_all_within (start, end, 0.0f, FLT_MAX, track_views, Selection::Set);
4899 /* find the two markers on either side of the click and make the range out of it */
4900 selection->set (0, start, end);
4909 stop_canvas_autoscroll ();
4915 Editor::start_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4917 drag_info.item = item;
4918 drag_info.motion_callback = &Editor::drag_mouse_zoom;
4919 drag_info.finished_callback = &Editor::end_mouse_zoom;
4921 start_grab (event, zoom_cursor);
4923 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4927 Editor::drag_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4932 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4933 snap_to (drag_info.current_pointer_frame);
4935 if (drag_info.first_move) {
4936 snap_to (drag_info.grab_frame);
4940 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4942 /* base start and end on initial click position */
4943 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4944 start = drag_info.current_pointer_frame;
4945 end = drag_info.grab_frame;
4947 end = drag_info.current_pointer_frame;
4948 start = drag_info.grab_frame;
4953 if (drag_info.first_move) {
4955 zoom_rect->raise_to_top();
4958 reposition_zoom_rect(start, end);
4960 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4961 drag_info.first_move = false;
4963 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4968 Editor::end_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4970 if (!drag_info.first_move) {
4971 drag_mouse_zoom (item, event);
4973 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
4974 temporal_zoom_by_frame (drag_info.grab_frame, drag_info.last_pointer_frame, "mouse zoom");
4976 temporal_zoom_by_frame (drag_info.last_pointer_frame, drag_info.grab_frame, "mouse zoom");
4979 temporal_zoom_to_frame (false, drag_info.grab_frame);
4981 temporal_zoom_step (false);
4982 center_screen (drag_info.grab_frame);
4990 Editor::reposition_zoom_rect (nframes_t start, nframes_t end)
4992 double x1 = frame_to_pixel (start);
4993 double x2 = frame_to_pixel (end);
4994 double y2 = full_canvas_height - 1.0;
4996 zoom_rect->property_x1() = x1;
4997 zoom_rect->property_y1() = 1.0;
4998 zoom_rect->property_x2() = x2;
4999 zoom_rect->property_y2() = y2;
5003 Editor::start_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
5005 drag_info.item = item;
5006 drag_info.motion_callback = &Editor::drag_rubberband_select;
5007 drag_info.finished_callback = &Editor::end_rubberband_select;
5009 start_grab (event, cross_hair_cursor);
5011 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5015 Editor::drag_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
5022 /* use a bigger drag threshold than the default */
5024 if (abs ((int) (drag_info.current_pointer_frame - drag_info.grab_frame)) < 8) {
5028 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
5029 if (drag_info.first_move) {
5030 snap_to (drag_info.grab_frame);
5032 snap_to (drag_info.current_pointer_frame);
5035 /* base start and end on initial click position */
5037 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
5038 start = drag_info.current_pointer_frame;
5039 end = drag_info.grab_frame;
5041 end = drag_info.current_pointer_frame;
5042 start = drag_info.grab_frame;
5045 if (drag_info.current_pointer_y < drag_info.grab_y) {
5046 y1 = drag_info.current_pointer_y;
5047 y2 = drag_info.grab_y;
5049 y2 = drag_info.current_pointer_y;
5050 y1 = drag_info.grab_y;
5054 if (start != end || y1 != y2) {
5056 double x1 = frame_to_pixel (start);
5057 double x2 = frame_to_pixel (end);
5059 rubberband_rect->property_x1() = x1;
5060 rubberband_rect->property_y1() = y1;
5061 rubberband_rect->property_x2() = x2;
5062 rubberband_rect->property_y2() = y2;
5064 rubberband_rect->show();
5065 rubberband_rect->raise_to_top();
5067 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
5068 drag_info.first_move = false;
5070 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5075 Editor::end_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
5077 if (!drag_info.first_move) {
5079 drag_rubberband_select (item, event);
5082 if (drag_info.current_pointer_y < drag_info.grab_y) {
5083 y1 = drag_info.current_pointer_y;
5084 y2 = drag_info.grab_y;
5087 y2 = drag_info.current_pointer_y;
5088 y1 = drag_info.grab_y;
5092 Selection::Operation op = Keyboard::selection_type (event->button.state);
5095 begin_reversible_command (_("rubberband selection"));
5097 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
5098 commit = select_all_within (drag_info.grab_frame, drag_info.last_pointer_frame, y1, y2, track_views, op);
5100 commit = select_all_within (drag_info.last_pointer_frame, drag_info.grab_frame, y1, y2, track_views, op);
5104 commit_reversible_command ();
5108 selection->clear_tracks();
5109 selection->clear_regions();
5110 selection->clear_points ();
5111 selection->clear_lines ();
5114 rubberband_rect->hide();
5119 Editor::mouse_rename_region (ArdourCanvas::Item* item, GdkEvent* event)
5121 using namespace Gtkmm2ext;
5123 ArdourPrompter prompter (false);
5125 prompter.set_prompt (_("Name for region:"));
5126 prompter.set_initial_text (clicked_regionview->region()->name());
5127 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
5128 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
5129 prompter.show_all ();
5130 switch (prompter.run ()) {
5131 case Gtk::RESPONSE_ACCEPT:
5133 prompter.get_result(str);
5135 clicked_regionview->region()->set_name (str);
5143 Editor::start_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
5145 drag_info.item = item;
5146 drag_info.motion_callback = &Editor::time_fx_motion;
5147 drag_info.finished_callback = &Editor::end_time_fx;
5151 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5155 Editor::time_fx_motion (ArdourCanvas::Item *item, GdkEvent* event)
5157 RegionView* rv = clicked_regionview;
5159 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
5160 snap_to (drag_info.current_pointer_frame);
5163 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
5167 if (drag_info.current_pointer_frame > rv->region()->position()) {
5168 rv->get_time_axis_view().show_timestretch (rv->region()->position(), drag_info.current_pointer_frame);
5171 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
5172 drag_info.first_move = false;
5174 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5178 Editor::end_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
5180 clicked_regionview->get_time_axis_view().hide_timestretch ();
5182 if (drag_info.first_move) {
5186 if (drag_info.last_pointer_frame < clicked_regionview->region()->position()) {
5187 /* backwards drag of the left edge - not usable */
5191 nframes_t newlen = drag_info.last_pointer_frame - clicked_regionview->region()->position();
5192 #ifdef USE_RUBBERBAND
5193 float percentage = (float) ((double) newlen / (double) clicked_regionview->region()->length());
5195 float percentage = (float) ((double) newlen - (double) clicked_regionview->region()->length()) / ((double) newlen) * 100.0f;
5198 begin_reversible_command (_("timestretch"));
5200 // XXX how do timeFX on multiple regions ?
5203 rs.add (clicked_regionview);
5205 if (time_stretch (rs, percentage) == 0) {
5206 session->commit_reversible_command ();
5211 Editor::mouse_brush_insert_region (RegionView* rv, nframes_t pos)
5213 /* no brushing without a useful snap setting */
5216 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
5219 switch (snap_mode) {
5221 return; /* can't work because it allows region to be placed anywhere */
5226 switch (snap_type) {
5234 /* don't brush a copy over the original */
5236 if (pos == rv->region()->position()) {
5240 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*>(&arv->get_time_axis_view());
5242 if (atv == 0 || !atv->is_audio_track()) {
5246 boost::shared_ptr<Playlist> playlist = atv->playlist();
5247 double speed = atv->get_diskstream()->speed();
5249 XMLNode &before = playlist->get_state();
5250 playlist->add_region (boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (arv->audio_region())), (nframes_t) (pos * speed));
5251 XMLNode &after = playlist->get_state();
5252 session->add_command(new MementoCommand<Playlist>(*playlist.get(), &before, &after));
5254 // playlist is frozen, so we have to update manually
5256 playlist->Modified(); /* EMIT SIGNAL */
5260 Editor::track_height_step_timeout ()
5263 struct timeval delta;
5265 gettimeofday (&now, 0);
5266 timersub (&now, &last_track_height_step_timestamp, &delta);
5268 if (delta.tv_sec * 1000000 + delta.tv_usec > 250000) { /* milliseconds */
5269 current_stepping_trackview = 0;