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 dynamic_cast<AutomationTimeAxisView*>(clicked_trackview)->add_automation_event
1045 // Gain only makes sense for audio regions
1047 if (!dynamic_cast<AudioRegionView*>(clicked_regionview)) {
1051 switch (item_type) {
1053 dynamic_cast<AudioRegionView*>(clicked_regionview)->add_gain_point_event (item, event);
1057 case AutomationTrackItem:
1058 dynamic_cast<AutomationTimeAxisView*>(clicked_trackview)->
1059 add_automation_event (item, event, where, event->button.y);
1069 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1070 if (scrubbing_direction == 0) {
1071 /* no drag, just a click */
1072 switch (item_type) {
1074 play_selected_region ();
1080 /* make sure we stop */
1081 session->request_transport_speed (0.0);
1095 switch (mouse_mode) {
1098 switch (item_type) {
1100 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1102 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier|Keyboard::SecondaryModifier))) {
1105 // Button2 click is unused
1118 // x_style_paste (where, 1.0);
1138 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1144 if (last_item_entered != item) {
1145 last_item_entered = item;
1146 last_item_entered_n = 0;
1149 switch (item_type) {
1150 case GainControlPointItem:
1151 if (mouse_mode == MouseGain) {
1152 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1153 cp->set_visible (true);
1157 at_y = cp->get_y ();
1158 cp->item->i2w (at_x, at_y);
1162 fraction = 1.0 - (cp->get_y() / cp->line.height());
1164 if (is_drawable() && !_scrubbing) {
1165 track_canvas.get_window()->set_cursor (*fader_cursor);
1168 last_item_entered_n++;
1169 set_verbose_canvas_cursor (cp->line.get_verbose_cursor_string (fraction), at_x, at_y);
1170 if (last_item_entered_n < 10) {
1171 show_verbose_canvas_cursor ();
1176 case GainAutomationControlPointItem:
1177 case PanAutomationControlPointItem:
1178 case RedirectAutomationControlPointItem:
1179 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1180 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1181 cp->set_visible (true);
1185 at_y = cp->get_y ();
1186 cp->item->i2w (at_x, at_y);
1190 fraction = 1.0 - (cp->get_y() / cp->line.height());
1192 set_verbose_canvas_cursor (cp->line.get_verbose_cursor_string (fraction), at_x, at_y);
1193 show_verbose_canvas_cursor ();
1195 if (is_drawable()) {
1196 track_canvas.get_window()->set_cursor (*fader_cursor);
1202 if (mouse_mode == MouseGain) {
1203 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1205 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredGainLine.get();
1206 if (is_drawable()) {
1207 track_canvas.get_window()->set_cursor (*fader_cursor);
1212 case GainAutomationLineItem:
1213 case RedirectAutomationLineItem:
1214 case PanAutomationLineItem:
1215 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1217 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1219 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredAutomationLine.get();
1221 if (is_drawable()) {
1222 track_canvas.get_window()->set_cursor (*fader_cursor);
1227 case RegionViewNameHighlight:
1228 if (is_drawable() && mouse_mode == MouseObject) {
1229 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1233 case StartSelectionTrimItem:
1234 case EndSelectionTrimItem:
1235 /* <CMT Additions> */
1236 case ImageFrameHandleStartItem:
1237 case ImageFrameHandleEndItem:
1238 case MarkerViewHandleStartItem:
1239 case MarkerViewHandleEndItem:
1240 /* </CMT Additions> */
1242 if (is_drawable()) {
1243 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1247 case PlayheadCursorItem:
1248 if (is_drawable()) {
1249 track_canvas.get_window()->set_cursor (*grabber_cursor);
1253 case RegionViewName:
1255 /* when the name is not an active item, the entire name highlight is for trimming */
1257 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1258 if (mouse_mode == MouseObject && is_drawable()) {
1259 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1265 case AutomationTrackItem:
1266 if (is_drawable()) {
1267 Gdk::Cursor *cursor;
1268 switch (mouse_mode) {
1270 cursor = selector_cursor;
1273 cursor = zoom_cursor;
1276 cursor = cross_hair_cursor;
1280 track_canvas.get_window()->set_cursor (*cursor);
1282 AutomationTimeAxisView* atv;
1283 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1284 clear_entered_track = false;
1285 set_entered_track (atv);
1291 case RangeMarkerBarItem:
1292 case TransportMarkerBarItem:
1293 case CdMarkerBarItem:
1296 if (is_drawable()) {
1297 time_canvas.get_window()->set_cursor (*timebar_cursor);
1302 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1305 entered_marker = marker;
1306 marker->set_color_rgba (ARDOUR_UI::config()->canvasvar_EnteredMarker.get());
1308 case MeterMarkerItem:
1309 case TempoMarkerItem:
1310 if (is_drawable()) {
1311 time_canvas.get_window()->set_cursor (*timebar_cursor);
1314 case FadeInHandleItem:
1315 case FadeOutHandleItem:
1316 if (mouse_mode == MouseObject) {
1317 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1319 rect->property_fill_color_rgba() = 0;
1320 rect->property_outline_pixels() = 1;
1329 /* second pass to handle entered track status in a comprehensible way.
1332 switch (item_type) {
1334 case GainAutomationLineItem:
1335 case RedirectAutomationLineItem:
1336 case PanAutomationLineItem:
1337 case GainControlPointItem:
1338 case GainAutomationControlPointItem:
1339 case PanAutomationControlPointItem:
1340 case RedirectAutomationControlPointItem:
1341 /* these do not affect the current entered track state */
1342 clear_entered_track = false;
1345 case AutomationTrackItem:
1346 /* handled above already */
1350 set_entered_track (0);
1358 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1367 switch (item_type) {
1368 case GainControlPointItem:
1369 case GainAutomationControlPointItem:
1370 case PanAutomationControlPointItem:
1371 case RedirectAutomationControlPointItem:
1372 cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
1373 if (cp->line.npoints() > 1) {
1374 if (!cp->selected) {
1375 cp->set_visible (false);
1379 if (is_drawable()) {
1380 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1383 hide_verbose_canvas_cursor ();
1386 case RegionViewNameHighlight:
1387 case StartSelectionTrimItem:
1388 case EndSelectionTrimItem:
1389 case PlayheadCursorItem:
1390 /* <CMT Additions> */
1391 case ImageFrameHandleStartItem:
1392 case ImageFrameHandleEndItem:
1393 case MarkerViewHandleStartItem:
1394 case MarkerViewHandleEndItem:
1395 /* </CMT Additions> */
1396 if (is_drawable()) {
1397 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1402 case GainAutomationLineItem:
1403 case RedirectAutomationLineItem:
1404 case PanAutomationLineItem:
1405 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1407 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1409 line->property_fill_color_rgba() = al->get_line_color();
1411 if (is_drawable()) {
1412 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1416 case RegionViewName:
1417 /* see enter_handler() for notes */
1418 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1419 if (is_drawable() && mouse_mode == MouseObject) {
1420 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1425 case RangeMarkerBarItem:
1426 case TransportMarkerBarItem:
1427 case CdMarkerBarItem:
1431 if (is_drawable()) {
1432 time_canvas.get_window()->set_cursor (*timebar_cursor);
1437 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1441 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
1442 location_flags_changed (loc, this);
1445 case MeterMarkerItem:
1446 case TempoMarkerItem:
1448 if (is_drawable()) {
1449 time_canvas.get_window()->set_cursor (*timebar_cursor);
1454 case FadeInHandleItem:
1455 case FadeOutHandleItem:
1456 rv = static_cast<RegionView*>(item->get_data ("regionview"));
1458 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1460 rect->property_fill_color_rgba() = rv->get_fill_color();
1461 rect->property_outline_pixels() = 0;
1466 case AutomationTrackItem:
1467 if (is_drawable()) {
1468 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1469 clear_entered_track = true;
1470 Glib::signal_idle().connect (mem_fun(*this, &Editor::left_automation_track));
1482 Editor::left_automation_track ()
1484 if (clear_entered_track) {
1485 set_entered_track (0);
1486 clear_entered_track = false;
1492 Editor::motion_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type, bool from_autoscroll)
1494 if (event->motion.is_hint) {
1497 /* We call this so that MOTION_NOTIFY events continue to be
1498 delivered to the canvas. We need to do this because we set
1499 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
1500 the density of the events, at the expense of a round-trip
1501 to the server. Given that this will mostly occur on cases
1502 where DISPLAY = :0.0, and given the cost of what the motion
1503 event might do, its a good tradeoff.
1506 track_canvas.get_pointer (x, y);
1509 if (current_stepping_trackview) {
1510 /* don't keep the persistent stepped trackview if the mouse moves */
1511 current_stepping_trackview = 0;
1512 step_timeout.disconnect ();
1515 if (session && session->actively_recording()) {
1516 /* Sorry. no dragging stuff around while we record */
1520 drag_info.item_type = item_type;
1521 drag_info.last_pointer_x = drag_info.current_pointer_x;
1522 drag_info.last_pointer_y = drag_info.current_pointer_y;
1523 drag_info.current_pointer_frame = event_frame (event, &drag_info.current_pointer_x,
1524 &drag_info.current_pointer_y);
1526 switch (mouse_mode) {
1532 if (scrubbing_direction == 0) {
1534 session->request_locate (drag_info.current_pointer_frame, false);
1535 session->request_transport_speed (0.1);
1536 scrubbing_direction = 1;
1540 if (last_scrub_x > drag_info.current_pointer_x) {
1542 /* pointer moved to the left */
1544 if (scrubbing_direction > 0) {
1546 /* we reversed direction to go backwards */
1549 scrub_reverse_distance += (int) (last_scrub_x - drag_info.current_pointer_x);
1553 /* still moving to the left (backwards) */
1555 scrub_reversals = 0;
1556 scrub_reverse_distance = 0;
1558 delta = 0.01 * (last_scrub_x - drag_info.current_pointer_x);
1559 session->request_transport_speed (session->transport_speed() - delta);
1563 /* pointer moved to the right */
1565 if (scrubbing_direction < 0) {
1566 /* we reversed direction to go forward */
1569 scrub_reverse_distance += (int) (drag_info.current_pointer_x - last_scrub_x);
1572 /* still moving to the right */
1574 scrub_reversals = 0;
1575 scrub_reverse_distance = 0;
1577 delta = 0.01 * (drag_info.current_pointer_x - last_scrub_x);
1578 session->request_transport_speed (session->transport_speed() + delta);
1582 /* if there have been more than 2 opposite motion moves detected, or one that moves
1583 back more than 10 pixels, reverse direction
1586 if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
1588 if (scrubbing_direction > 0) {
1589 /* was forwards, go backwards */
1590 session->request_transport_speed (-0.1);
1591 scrubbing_direction = -1;
1593 /* was backwards, go forwards */
1594 session->request_transport_speed (0.1);
1595 scrubbing_direction = 1;
1598 scrub_reverse_distance = 0;
1599 scrub_reversals = 0;
1603 last_scrub_x = drag_info.current_pointer_x;
1610 if (!from_autoscroll && drag_info.item) {
1611 /* item != 0 is the best test i can think of for dragging.
1613 if (!drag_info.move_threshold_passed) {
1615 bool x_threshold_passed = (::llabs ((nframes64_t) (drag_info.current_pointer_x - drag_info.grab_x)) > 4LL);
1616 bool y_threshold_passed = (::llabs ((nframes64_t) (drag_info.current_pointer_y - drag_info.grab_y)) > 4LL);
1618 drag_info.move_threshold_passed = (x_threshold_passed || y_threshold_passed);
1620 // and change the initial grab loc/frame if this drag info wants us to
1622 if (drag_info.want_move_threshold && drag_info.move_threshold_passed) {
1623 drag_info.grab_frame = drag_info.current_pointer_frame;
1624 drag_info.grab_x = drag_info.current_pointer_x;
1625 drag_info.grab_y = drag_info.current_pointer_y;
1626 drag_info.last_pointer_frame = drag_info.grab_frame;
1627 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
1632 switch (item_type) {
1633 case PlayheadCursorItem:
1635 case GainControlPointItem:
1636 case RedirectAutomationControlPointItem:
1637 case GainAutomationControlPointItem:
1638 case PanAutomationControlPointItem:
1639 case TempoMarkerItem:
1640 case MeterMarkerItem:
1641 case RegionViewNameHighlight:
1642 case StartSelectionTrimItem:
1643 case EndSelectionTrimItem:
1646 case RedirectAutomationLineItem:
1647 case GainAutomationLineItem:
1648 case PanAutomationLineItem:
1649 case FadeInHandleItem:
1650 case FadeOutHandleItem:
1651 /* <CMT Additions> */
1652 case ImageFrameHandleStartItem:
1653 case ImageFrameHandleEndItem:
1654 case MarkerViewHandleStartItem:
1655 case MarkerViewHandleEndItem:
1656 /* </CMT Additions> */
1657 if (drag_info.item && (event->motion.state & Gdk::BUTTON1_MASK ||
1658 (event->motion.state & Gdk::BUTTON2_MASK))) {
1659 if (!from_autoscroll) {
1660 maybe_autoscroll (event);
1662 (this->*(drag_info.motion_callback)) (item, event);
1671 switch (mouse_mode) {
1676 if (drag_info.item && (event->motion.state & GDK_BUTTON1_MASK ||
1677 (event->motion.state & GDK_BUTTON2_MASK))) {
1678 if (!from_autoscroll) {
1679 maybe_autoscroll (event);
1681 (this->*(drag_info.motion_callback)) (item, event);
1692 track_canvas_motion (event);
1693 // drag_info.last_pointer_frame = drag_info.current_pointer_frame;
1701 Editor::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
1703 if (drag_info.item == 0) {
1704 fatal << _("programming error: start_grab called without drag item") << endmsg;
1710 cursor = grabber_cursor;
1713 // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
1715 if (event->button.button == 2) {
1716 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
1717 drag_info.y_constrained = true;
1718 drag_info.x_constrained = false;
1720 drag_info.y_constrained = false;
1721 drag_info.x_constrained = true;
1724 drag_info.x_constrained = false;
1725 drag_info.y_constrained = false;
1728 drag_info.grab_frame = event_frame (event, &drag_info.grab_x, &drag_info.grab_y);
1729 drag_info.last_pointer_frame = drag_info.grab_frame;
1730 drag_info.current_pointer_frame = drag_info.grab_frame;
1731 drag_info.current_pointer_x = drag_info.grab_x;
1732 drag_info.current_pointer_y = drag_info.grab_y;
1733 drag_info.last_pointer_x = drag_info.current_pointer_x;
1734 drag_info.last_pointer_y = drag_info.current_pointer_y;
1735 drag_info.cumulative_x_drag = 0;
1736 drag_info.cumulative_y_drag = 0;
1737 drag_info.first_move = true;
1738 drag_info.move_threshold_passed = false;
1739 drag_info.want_move_threshold = false;
1740 drag_info.pointer_frame_offset = 0;
1741 drag_info.brushing = false;
1742 drag_info.copied_location = 0;
1744 drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK,
1746 event->button.time);
1748 if (session && session->transport_rolling()) {
1749 drag_info.was_rolling = true;
1751 drag_info.was_rolling = false;
1754 switch (snap_type) {
1755 case SnapToRegionStart:
1756 case SnapToRegionEnd:
1757 case SnapToRegionSync:
1758 case SnapToRegionBoundary:
1759 build_region_boundary_cache ();
1767 Editor::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t time)
1769 drag_info.item->ungrab (0);
1770 drag_info.item = new_item;
1773 cursor = grabber_cursor;
1776 drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK, *cursor, time);
1780 Editor::end_grab (ArdourCanvas::Item* item, GdkEvent* event)
1782 bool did_drag = false;
1784 stop_canvas_autoscroll ();
1786 if (drag_info.item == 0) {
1790 drag_info.item->ungrab (event->button.time);
1792 if (drag_info.finished_callback) {
1793 drag_info.last_pointer_x = drag_info.current_pointer_x;
1794 drag_info.last_pointer_y = drag_info.current_pointer_y;
1795 (this->*(drag_info.finished_callback)) (item, event);
1798 did_drag = !drag_info.first_move;
1800 hide_verbose_canvas_cursor();
1803 drag_info.copy = false;
1804 drag_info.motion_callback = 0;
1805 drag_info.finished_callback = 0;
1806 drag_info.last_trackview = 0;
1807 drag_info.last_frame_position = 0;
1808 drag_info.grab_frame = 0;
1809 drag_info.last_pointer_frame = 0;
1810 drag_info.current_pointer_frame = 0;
1811 drag_info.brushing = false;
1813 if (drag_info.copied_location) {
1814 delete drag_info.copied_location;
1815 drag_info.copied_location = 0;
1822 Editor::start_fade_in_grab (ArdourCanvas::Item* item, GdkEvent* event)
1824 drag_info.item = item;
1825 drag_info.motion_callback = &Editor::fade_in_drag_motion_callback;
1826 drag_info.finished_callback = &Editor::fade_in_drag_finished_callback;
1830 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
1831 fatal << _("programming error: fade in canvas item has no regionview data pointer!") << endmsg;
1835 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1837 drag_info.pointer_frame_offset = drag_info.grab_frame - ((nframes_t) arv->audio_region()->fade_in().back()->when + arv->region()->position());
1841 Editor::fade_in_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1843 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1845 nframes_t fade_length;
1847 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1848 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1854 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1858 if (pos < (arv->region()->position() + 64)) {
1859 fade_length = 64; // this should be a minimum defined somewhere
1860 } else if (pos > arv->region()->last_frame()) {
1861 fade_length = arv->region()->length();
1863 fade_length = pos - arv->region()->position();
1865 /* mapover the region selection */
1867 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1869 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1875 tmp->reset_fade_in_shape_width (fade_length);
1878 show_verbose_duration_cursor (arv->region()->position(), arv->region()->position() + fade_length, 10);
1880 drag_info.first_move = false;
1884 Editor::fade_in_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1886 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1888 nframes_t fade_length;
1890 if (drag_info.first_move) return;
1892 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1893 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1898 if (pos < (arv->region()->position() + 64)) {
1899 fade_length = 64; // this should be a minimum defined somewhere
1900 } else if (pos > arv->region()->last_frame()) {
1901 fade_length = arv->region()->length();
1903 fade_length = pos - arv->region()->position();
1906 begin_reversible_command (_("change fade in length"));
1908 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1910 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1916 AutomationList& alist = tmp->audio_region()->fade_in();
1917 XMLNode &before = alist.get_state();
1919 tmp->audio_region()->set_fade_in_length (fade_length);
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);
2037 XMLNode &after = alist.get_state();
2038 session->add_command(new MementoCommand<AutomationList>(alist, &before, &after));
2041 commit_reversible_command ();
2045 Editor::start_cursor_grab (ArdourCanvas::Item* item, GdkEvent* event)
2047 drag_info.item = item;
2048 drag_info.motion_callback = &Editor::cursor_drag_motion_callback;
2049 drag_info.finished_callback = &Editor::cursor_drag_finished_callback;
2053 if ((drag_info.data = (item->get_data ("cursor"))) == 0) {
2054 fatal << _("programming error: cursor canvas item has no cursor data pointer!") << endmsg;
2058 Cursor* cursor = (Cursor *) drag_info.data;
2060 if (cursor == playhead_cursor) {
2061 _dragging_playhead = true;
2063 if (session && drag_info.was_rolling) {
2064 session->request_stop ();
2067 if (session && session->is_auditioning()) {
2068 session->cancel_audition ();
2072 drag_info.pointer_frame_offset = drag_info.grab_frame - cursor->current_frame;
2074 show_verbose_time_cursor (cursor->current_frame, 10);
2078 Editor::cursor_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2080 Cursor* cursor = (Cursor *) drag_info.data;
2081 nframes_t adjusted_frame;
2083 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2084 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2090 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2091 if (cursor == playhead_cursor) {
2092 snap_to (adjusted_frame);
2096 if (adjusted_frame == drag_info.last_pointer_frame) return;
2098 cursor->set_position (adjusted_frame);
2100 UpdateAllTransportClocks (cursor->current_frame);
2102 show_verbose_time_cursor (cursor->current_frame, 10);
2104 drag_info.last_pointer_frame = adjusted_frame;
2105 drag_info.first_move = false;
2109 Editor::cursor_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2111 if (drag_info.first_move) return;
2113 cursor_drag_motion_callback (item, event);
2115 _dragging_playhead = false;
2117 if (item == &playhead_cursor->canvas_item) {
2119 session->request_locate (playhead_cursor->current_frame, drag_info.was_rolling);
2125 Editor::update_marker_drag_item (Location *location)
2127 double x1 = frame_to_pixel (location->start());
2128 double x2 = frame_to_pixel (location->end());
2130 if (location->is_mark()) {
2131 marker_drag_line_points.front().set_x(x1);
2132 marker_drag_line_points.back().set_x(x1);
2133 marker_drag_line->property_points() = marker_drag_line_points;
2136 range_marker_drag_rect->property_x1() = x1;
2137 range_marker_drag_rect->property_x2() = x2;
2142 Editor::start_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2146 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2147 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2153 Location *location = find_location_from_marker (marker, is_start);
2155 drag_info.item = item;
2156 drag_info.data = marker;
2157 drag_info.motion_callback = &Editor::marker_drag_motion_callback;
2158 drag_info.finished_callback = &Editor::marker_drag_finished_callback;
2162 _dragging_edit_point = true;
2164 drag_info.copied_location = new Location (*location);
2165 drag_info.pointer_frame_offset = drag_info.grab_frame - (is_start ? location->start() : location->end());
2167 update_marker_drag_item (location);
2169 if (location->is_mark()) {
2170 // marker_drag_line->show();
2171 // marker_drag_line->raise_to_top();
2173 range_marker_drag_rect->show();
2174 range_marker_drag_rect->raise_to_top();
2178 show_verbose_time_cursor (location->start(), 10);
2180 show_verbose_time_cursor (location->end(), 10);
2183 Selection::Operation op = Keyboard::selection_type (event->button.state);
2186 case Selection::Toggle:
2187 selection->toggle (marker);
2189 case Selection::Set:
2190 selection->set (marker);
2192 case Selection::Extend:
2193 selection->add (marker);
2195 case Selection::Add:
2196 selection->add (marker);
2202 Editor::marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2205 Marker* marker = (Marker *) drag_info.data;
2206 Location *real_location;
2207 Location *copy_location;
2209 bool move_both = false;
2212 if (drag_info.pointer_frame_offset <= drag_info.current_pointer_frame) {
2213 newframe = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2218 nframes_t next = newframe;
2220 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2221 snap_to (newframe, 0, true);
2224 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
2228 /* call this to find out if its the start or end */
2230 if ((real_location = find_location_from_marker (marker, is_start)) == 0) {
2234 if (real_location->locked()) {
2238 /* use the copy that we're "dragging" around */
2240 copy_location = drag_info.copied_location;
2242 f_delta = copy_location->end() - copy_location->start();
2244 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2248 if (copy_location->is_mark()) {
2251 copy_location->set_start (newframe);
2255 if (is_start) { // start-of-range marker
2258 copy_location->set_start (newframe);
2259 copy_location->set_end (newframe + f_delta);
2260 } else if (newframe < copy_location->end()) {
2261 copy_location->set_start (newframe);
2263 snap_to (next, 1, true);
2264 copy_location->set_end (next);
2265 copy_location->set_start (newframe);
2268 } else { // end marker
2271 copy_location->set_end (newframe);
2272 copy_location->set_start (newframe - f_delta);
2273 } else if (newframe > copy_location->start()) {
2274 copy_location->set_end (newframe);
2276 } else if (newframe > 0) {
2277 snap_to (next, -1, true);
2278 copy_location->set_start (next);
2279 copy_location->set_end (newframe);
2284 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
2285 drag_info.first_move = false;
2287 update_marker_drag_item (copy_location);
2289 LocationMarkers* lm = find_location_markers (real_location);
2290 lm->set_position (copy_location->start(), copy_location->end());
2291 edit_point_clock.set (copy_location->start());
2293 show_verbose_time_cursor (newframe, 10);
2297 Editor::marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2299 if (drag_info.first_move) {
2300 marker_drag_motion_callback (item, event);
2304 _dragging_edit_point = false;
2306 Marker* marker = (Marker *) drag_info.data;
2309 begin_reversible_command ( _("move marker") );
2310 XMLNode &before = session->locations()->get_state();
2312 Location * location = find_location_from_marker (marker, is_start);
2316 if (location->locked()) {
2320 if (location->is_mark()) {
2321 location->set_start (drag_info.copied_location->start());
2323 location->set (drag_info.copied_location->start(), drag_info.copied_location->end());
2327 XMLNode &after = session->locations()->get_state();
2328 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
2329 commit_reversible_command ();
2331 marker_drag_line->hide();
2332 range_marker_drag_rect->hide();
2336 Editor::start_meter_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2339 MeterMarker* meter_marker;
2341 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2342 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2346 meter_marker = dynamic_cast<MeterMarker*> (marker);
2348 MetricSection& section (meter_marker->meter());
2350 if (!section.movable()) {
2354 drag_info.item = item;
2355 drag_info.copy = false;
2356 drag_info.data = marker;
2357 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2358 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2362 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2364 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2368 Editor::start_meter_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2371 MeterMarker* meter_marker;
2373 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2374 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2378 meter_marker = dynamic_cast<MeterMarker*> (marker);
2380 // create a dummy marker for visual representation of moving the copy.
2381 // The actual copying is not done before we reach the finish callback.
2383 snprintf (name, sizeof(name), "%g/%g", meter_marker->meter().beats_per_bar(), meter_marker->meter().note_divisor ());
2384 MeterMarker* new_marker = new MeterMarker(*this, *meter_group, ARDOUR_UI::config()->canvasvar_MeterMarker.get(), name,
2385 *new MeterSection(meter_marker->meter()));
2387 drag_info.item = &new_marker->the_item();
2388 drag_info.copy = true;
2389 drag_info.data = new_marker;
2390 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2391 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2395 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2397 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2401 Editor::meter_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2403 MeterMarker* marker = (MeterMarker *) drag_info.data;
2404 nframes_t adjusted_frame;
2406 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2407 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2413 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2414 snap_to (adjusted_frame);
2417 if (adjusted_frame == drag_info.last_pointer_frame) return;
2419 marker->set_position (adjusted_frame);
2422 drag_info.last_pointer_frame = adjusted_frame;
2423 drag_info.first_move = false;
2425 show_verbose_time_cursor (adjusted_frame, 10);
2429 Editor::meter_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2431 if (drag_info.first_move) return;
2433 meter_marker_drag_motion_callback (drag_info.item, event);
2435 MeterMarker* marker = (MeterMarker *) drag_info.data;
2438 TempoMap& map (session->tempo_map());
2439 map.bbt_time (drag_info.last_pointer_frame, when);
2441 if (drag_info.copy == true) {
2442 begin_reversible_command (_("copy meter mark"));
2443 XMLNode &before = map.get_state();
2444 map.add_meter (marker->meter(), when);
2445 XMLNode &after = map.get_state();
2446 session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2447 commit_reversible_command ();
2449 // delete the dummy marker we used for visual representation of copying.
2450 // a new visual marker will show up automatically.
2453 begin_reversible_command (_("move meter mark"));
2454 XMLNode &before = map.get_state();
2455 map.move_meter (marker->meter(), when);
2456 XMLNode &after = map.get_state();
2457 session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2458 commit_reversible_command ();
2463 Editor::start_tempo_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2466 TempoMarker* tempo_marker;
2468 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2469 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2473 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2474 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2478 MetricSection& section (tempo_marker->tempo());
2480 if (!section.movable()) {
2484 drag_info.item = item;
2485 drag_info.copy = false;
2486 drag_info.data = marker;
2487 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2488 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2492 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2493 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2497 Editor::start_tempo_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2500 TempoMarker* tempo_marker;
2502 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2503 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2507 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2508 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2512 // create a dummy marker for visual representation of moving the copy.
2513 // The actual copying is not done before we reach the finish callback.
2515 snprintf (name, sizeof (name), "%.2f", tempo_marker->tempo().beats_per_minute());
2516 TempoMarker* new_marker = new TempoMarker(*this, *tempo_group, ARDOUR_UI::config()->canvasvar_TempoMarker.get(), name,
2517 *new TempoSection(tempo_marker->tempo()));
2519 drag_info.item = &new_marker->the_item();
2520 drag_info.copy = true;
2521 drag_info.data = new_marker;
2522 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2523 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2527 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2529 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2533 Editor::tempo_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2535 TempoMarker* marker = (TempoMarker *) drag_info.data;
2536 nframes_t adjusted_frame;
2538 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2539 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2545 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2546 snap_to (adjusted_frame);
2549 if (adjusted_frame == drag_info.last_pointer_frame) return;
2551 /* OK, we've moved far enough to make it worth actually move the thing. */
2553 marker->set_position (adjusted_frame);
2555 show_verbose_time_cursor (adjusted_frame, 10);
2557 drag_info.last_pointer_frame = adjusted_frame;
2558 drag_info.first_move = false;
2562 Editor::tempo_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2564 if (drag_info.first_move) return;
2566 tempo_marker_drag_motion_callback (drag_info.item, event);
2568 TempoMarker* marker = (TempoMarker *) drag_info.data;
2571 TempoMap& map (session->tempo_map());
2572 map.bbt_time (drag_info.last_pointer_frame, when);
2574 if (drag_info.copy == true) {
2575 begin_reversible_command (_("copy tempo mark"));
2576 XMLNode &before = map.get_state();
2577 map.add_tempo (marker->tempo(), when);
2578 XMLNode &after = map.get_state();
2579 session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2580 commit_reversible_command ();
2582 // delete the dummy marker we used for visual representation of copying.
2583 // a new visual marker will show up automatically.
2586 begin_reversible_command (_("move tempo mark"));
2587 XMLNode &before = map.get_state();
2588 map.move_tempo (marker->tempo(), when);
2589 XMLNode &after = map.get_state();
2590 session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2591 commit_reversible_command ();
2596 Editor::remove_gain_control_point (ArdourCanvas::Item*item, GdkEvent* event)
2598 ControlPoint* control_point;
2600 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2601 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2605 // We shouldn't remove the first or last gain point
2606 if (control_point->line.is_last_point(*control_point) ||
2607 control_point->line.is_first_point(*control_point)) {
2611 control_point->line.remove_point (*control_point);
2615 Editor::remove_control_point (ArdourCanvas::Item*item, GdkEvent* event)
2617 ControlPoint* control_point;
2619 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2620 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2624 control_point->line.remove_point (*control_point);
2628 Editor::start_control_point_grab (ArdourCanvas::Item* item, GdkEvent* event)
2630 ControlPoint* control_point;
2632 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2633 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2637 drag_info.item = item;
2638 drag_info.data = control_point;
2639 drag_info.motion_callback = &Editor::control_point_drag_motion_callback;
2640 drag_info.finished_callback = &Editor::control_point_drag_finished_callback;
2642 start_grab (event, fader_cursor);
2644 // start the grab at the center of the control point so
2645 // the point doesn't 'jump' to the mouse after the first drag
2646 drag_info.grab_x = control_point->get_x();
2647 drag_info.grab_y = control_point->get_y();
2648 control_point->line.parent_group().i2w(drag_info.grab_x, drag_info.grab_y);
2649 track_canvas.w2c(drag_info.grab_x, drag_info.grab_y,
2650 drag_info.grab_x, drag_info.grab_y);
2652 drag_info.grab_frame = pixel_to_frame(drag_info.grab_x);
2654 control_point->line.start_drag (control_point, drag_info.grab_frame, 0);
2656 float fraction = 1.0 - (control_point->get_y() / control_point->line.height());
2657 set_verbose_canvas_cursor (control_point->line.get_verbose_cursor_string (fraction),
2658 drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
2660 show_verbose_canvas_cursor ();
2664 Editor::control_point_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2666 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2668 double dx = drag_info.current_pointer_x - drag_info.last_pointer_x;
2669 double dy = drag_info.current_pointer_y - drag_info.last_pointer_y;
2671 if (event->button.state & Keyboard::SecondaryModifier) {
2676 double cx = drag_info.grab_x + drag_info.cumulative_x_drag + dx;
2677 double cy = drag_info.grab_y + drag_info.cumulative_y_drag + dy;
2679 // calculate zero crossing point. back off by .01 to stay on the
2680 // positive side of zero
2682 double zero_gain_y = (1.0 - ZERO_GAIN_FRACTION) * cp->line.height() - .01;
2683 cp->line.parent_group().i2w(_unused, zero_gain_y);
2685 // make sure we hit zero when passing through
2686 if ((cy < zero_gain_y and (cy - dy) > zero_gain_y)
2687 or (cy > zero_gain_y and (cy - dy) < zero_gain_y)) {
2691 if (drag_info.x_constrained) {
2692 cx = drag_info.grab_x;
2694 if (drag_info.y_constrained) {
2695 cy = drag_info.grab_y;
2698 drag_info.cumulative_x_drag = cx - drag_info.grab_x;
2699 drag_info.cumulative_y_drag = cy - drag_info.grab_y;
2701 cp->line.parent_group().w2i (cx, cy);
2705 cy = min ((double) cp->line.height(), cy);
2707 //translate cx to frames
2708 nframes_t cx_frames = unit_to_frame (cx);
2710 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier()) && !drag_info.x_constrained) {
2711 snap_to (cx_frames);
2714 float fraction = 1.0 - (cy / cp->line.height());
2718 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) {
2724 cp->line.point_drag (*cp, cx_frames , fraction, push);
2726 set_verbose_canvas_cursor_text (cp->line.get_verbose_cursor_string (fraction));
2728 drag_info.first_move = false;
2732 Editor::control_point_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2734 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2736 if (drag_info.first_move) {
2740 if ((event->type == GDK_BUTTON_RELEASE) && (event->button.button == 1) && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2741 reset_point_selection ();
2745 control_point_drag_motion_callback (item, event);
2747 cp->line.end_drag (cp);
2751 Editor::start_line_grab_from_regionview (ArdourCanvas::Item* item, GdkEvent* event)
2753 switch (mouse_mode) {
2755 assert(dynamic_cast<AudioRegionView*>(clicked_regionview));
2756 start_line_grab (dynamic_cast<AudioRegionView*>(clicked_regionview)->get_gain_line(), event);
2764 Editor::start_line_grab_from_line (ArdourCanvas::Item* item, GdkEvent* event)
2768 if ((al = reinterpret_cast<AutomationLine*> (item->get_data ("line"))) == 0) {
2769 fatal << _("programming error: line canvas item has no line pointer!") << endmsg;
2773 start_line_grab (al, event);
2777 Editor::start_line_grab (AutomationLine* line, GdkEvent* event)
2781 nframes_t frame_within_region;
2783 /* need to get x coordinate in terms of parent (TimeAxisItemView)
2787 cx = event->button.x;
2788 cy = event->button.y;
2789 line->parent_group().w2i (cx, cy);
2790 frame_within_region = (nframes_t) floor (cx * frames_per_unit);
2792 if (!line->control_points_adjacent (frame_within_region, current_line_drag_info.before,
2793 current_line_drag_info.after)) {
2794 /* no adjacent points */
2798 drag_info.item = &line->grab_item();
2799 drag_info.data = line;
2800 drag_info.motion_callback = &Editor::line_drag_motion_callback;
2801 drag_info.finished_callback = &Editor::line_drag_finished_callback;
2803 start_grab (event, fader_cursor);
2805 double fraction = 1.0 - (cy / line->height());
2807 line->start_drag (0, drag_info.grab_frame, fraction);
2809 set_verbose_canvas_cursor (line->get_verbose_cursor_string (fraction),
2810 drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
2811 show_verbose_canvas_cursor ();
2815 Editor::line_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2817 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
2819 double dy = drag_info.current_pointer_y - drag_info.last_pointer_y;
2821 if (event->button.state & Keyboard::SecondaryModifier) {
2825 double cx = drag_info.current_pointer_x;
2826 double cy = drag_info.grab_y + drag_info.cumulative_y_drag + dy;
2828 // calculate zero crossing point. back off by .01 to stay on the
2829 // positive side of zero
2831 double zero_gain_y = (1.0 - ZERO_GAIN_FRACTION) * line->height() - .01;
2832 line->parent_group().i2w(_unused, zero_gain_y);
2834 // make sure we hit zero when passing through
2835 if ((cy < zero_gain_y and (cy - dy) > zero_gain_y)
2836 or (cy > zero_gain_y and (cy - dy) < zero_gain_y)) {
2840 drag_info.cumulative_y_drag = cy - drag_info.grab_y;
2842 line->parent_group().w2i (cx, cy);
2845 cy = min ((double) line->height(), cy);
2848 fraction = 1.0 - (cy / line->height());
2852 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) {
2858 line->line_drag (current_line_drag_info.before, current_line_drag_info.after, fraction, push);
2860 set_verbose_canvas_cursor_text (line->get_verbose_cursor_string (fraction));
2864 Editor::line_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2866 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
2867 line_drag_motion_callback (item, event);
2872 Editor::start_region_grab (ArdourCanvas::Item* item, GdkEvent* event)
2874 if (selection->regions.empty() || clicked_regionview == 0) {
2878 drag_info.copy = false;
2879 drag_info.item = item;
2880 drag_info.data = clicked_regionview;
2882 if (Config->get_edit_mode() == Splice) {
2883 drag_info.motion_callback = &Editor::region_drag_splice_motion_callback;
2884 drag_info.finished_callback = &Editor::region_drag_splice_finished_callback;
2886 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2887 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2893 TimeAxisView* tvp = clicked_trackview;
2894 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2896 if (tv && tv->is_audio_track()) {
2897 speed = tv->get_diskstream()->speed();
2900 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2901 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2902 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2903 // we want a move threshold
2904 drag_info.want_move_threshold = true;
2906 show_verbose_time_cursor (drag_info.last_frame_position, 10);
2908 begin_reversible_command (_("move region(s)"));
2912 Editor::start_region_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2914 if (selection->regions.empty() || clicked_regionview == 0) {
2918 drag_info.copy = true;
2919 drag_info.item = item;
2920 drag_info.data = clicked_regionview;
2924 TimeAxisView* tv = &clicked_regionview->get_time_axis_view();
2925 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*>(tv);
2928 if (atv && atv->is_audio_track()) {
2929 speed = atv->get_diskstream()->speed();
2932 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2933 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2934 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2935 // we want a move threshold
2936 drag_info.want_move_threshold = true;
2937 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2938 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2939 show_verbose_time_cursor (drag_info.last_frame_position, 10);
2943 Editor::start_region_brush_grab (ArdourCanvas::Item* item, GdkEvent* event)
2945 if (selection->regions.empty() || clicked_regionview == 0 || Config->get_edit_mode() == Splice) {
2949 drag_info.copy = false;
2950 drag_info.item = item;
2951 drag_info.data = clicked_regionview;
2952 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2953 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2958 TimeAxisView* tvp = clicked_trackview;
2959 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2961 if (tv && tv->is_audio_track()) {
2962 speed = tv->get_diskstream()->speed();
2965 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2966 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2967 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2968 // we want a move threshold
2969 drag_info.want_move_threshold = true;
2970 drag_info.brushing = true;
2972 begin_reversible_command (_("Drag region brush"));
2976 Editor::possibly_copy_regions_during_grab (GdkEvent* event)
2978 if (drag_info.copy && drag_info.move_threshold_passed && drag_info.want_move_threshold) {
2980 drag_info.want_move_threshold = false; // don't copy again
2982 /* duplicate the region(s) */
2984 vector<RegionView*> new_regionviews;
2986 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
2989 AudioRegionView* arv;
2994 if ((arv = dynamic_cast<AudioRegionView*>(rv)) == 0) {
2995 /* XXX handle MIDI here */
2999 nrv = new AudioRegionView (*arv);
3000 nrv->get_canvas_group()->show ();
3002 new_regionviews.push_back (nrv);
3005 if (new_regionviews.empty()) {
3009 /* reset selection to new regionviews */
3011 selection->set (new_regionviews);
3013 /* reset drag_info data to reflect the fact that we are dragging the copies */
3015 drag_info.data = new_regionviews.front();
3017 swap_grab (new_regionviews.front()->get_canvas_group (), 0, event->motion.time);
3022 Editor::check_region_drag_possible (AudioTimeAxisView** tv)
3024 /* Which trackview is this ? */
3026 TimeAxisView* tvp = trackview_by_y_position (drag_info.current_pointer_y);
3027 (*tv) = dynamic_cast<AudioTimeAxisView*>(tvp);
3029 /* The region motion is only processed if the pointer is over
3033 if (!(*tv) || !(*tv)->is_audio_track()) {
3034 /* To make sure we hide the verbose canvas cursor when the mouse is
3035 not held over and audiotrack.
3037 hide_verbose_canvas_cursor ();
3044 struct RegionSelectionByPosition {
3045 bool operator() (RegionView*a, RegionView* b) {
3046 return a->region()->position () < b->region()->position();
3051 Editor::region_drag_splice_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
3053 AudioTimeAxisView* tv;
3055 if (!check_region_drag_possible (&tv)) {
3059 if (!drag_info.move_threshold_passed) {
3065 if (drag_info.current_pointer_x - drag_info.grab_x > 0) {
3071 RegionSelection copy (selection->regions);
3073 RegionSelectionByPosition cmp;
3076 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
3078 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*> (&(*i)->get_time_axis_view());
3084 boost::shared_ptr<Playlist> playlist;
3086 if ((playlist = atv->playlist()) == 0) {
3090 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
3095 if (drag_info.current_pointer_frame < (*i)->region()->last_frame() + 1) {
3099 if (drag_info.current_pointer_frame > (*i)->region()->first_frame()) {
3105 playlist->shuffle ((*i)->region(), dir);
3107 drag_info.grab_x = drag_info.current_pointer_x;
3112 Editor::region_drag_splice_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3117 Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
3121 RegionView* rv = reinterpret_cast<RegionView*> (drag_info.data);
3122 nframes_t pending_region_position = 0;
3123 int32_t pointer_y_span = 0, canvas_pointer_y_span = 0, original_pointer_order;
3124 int32_t visible_y_high = 0, visible_y_low = 512; //high meaning higher numbered.. not the height on the screen
3125 bool clamp_y_axis = false;
3126 vector<int32_t> height_list(512) ;
3127 vector<int32_t>::iterator j;
3128 AudioTimeAxisView* tv;
3130 possibly_copy_regions_during_grab (event);
3132 if (!check_region_drag_possible (&tv)) {
3136 original_pointer_order = drag_info.last_trackview->order;
3138 /************************************************************
3140 ************************************************************/
3142 if (drag_info.brushing) {
3143 clamp_y_axis = true;
3148 if ((pointer_y_span = (drag_info.last_trackview->order - tv->order)) != 0) {
3150 int32_t children = 0, numtracks = 0;
3151 // XXX hard coding track limit, oh my, so very very bad
3152 bitset <1024> tracks (0x00);
3153 /* get a bitmask representing the visible tracks */
3155 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
3156 TimeAxisView *tracklist_timeview;
3157 tracklist_timeview = (*i);
3158 AudioTimeAxisView* atv2 = dynamic_cast<AudioTimeAxisView*>(tracklist_timeview);
3159 list<TimeAxisView*> children_list;
3161 /* zeroes are audio tracks. ones are other types. */
3163 if (!atv2->hidden()) {
3165 if (visible_y_high < atv2->order) {
3166 visible_y_high = atv2->order;
3168 if (visible_y_low > atv2->order) {
3169 visible_y_low = atv2->order;
3172 if (!atv2->is_audio_track()) {
3173 tracks = tracks |= (0x01 << atv2->order);
3176 height_list[atv2->order] = (*i)->height;
3178 if ((children_list = atv2->get_child_list()).size() > 0) {
3179 for (list<TimeAxisView*>::iterator j = children_list.begin(); j != children_list.end(); ++j) {
3180 tracks = tracks |= (0x01 << (atv2->order + children));
3181 height_list[atv2->order + children] = (*j)->height;
3189 /* find the actual span according to the canvas */
3191 canvas_pointer_y_span = pointer_y_span;
3192 if (drag_info.last_trackview->order >= tv->order) {
3194 for (y = tv->order; y < drag_info.last_trackview->order; y++) {
3195 if (height_list[y] == 0 ) {
3196 canvas_pointer_y_span--;
3201 for (y = drag_info.last_trackview->order;y <= tv->order; y++) {
3202 if ( height_list[y] == 0 ) {
3203 canvas_pointer_y_span++;
3208 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3209 RegionView* rv2 = (*i);
3210 double ix1, ix2, iy1, iy2;
3213 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3214 rv2->get_canvas_group()->i2w (ix1, iy1);
3215 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3216 RouteTimeAxisView* atv2 = dynamic_cast<RouteTimeAxisView*>(tvp2);
3218 if (atv2->order != original_pointer_order) {
3219 /* this isn't the pointer track */
3221 if (canvas_pointer_y_span > 0) {
3223 /* moving up the canvas */
3224 if ((atv2->order - canvas_pointer_y_span) >= visible_y_low) {
3226 int32_t visible_tracks = 0;
3227 while (visible_tracks < canvas_pointer_y_span ) {
3230 while (height_list[atv2->order - (visible_tracks - n)] == 0) {
3231 /* we're passing through a hidden track */
3236 if (tracks[atv2->order - (canvas_pointer_y_span - n)] != 0x00) {
3237 clamp_y_axis = true;
3241 clamp_y_axis = true;
3244 } else if (canvas_pointer_y_span < 0) {
3246 /*moving down the canvas*/
3248 if ((atv2->order - (canvas_pointer_y_span - n)) <= visible_y_high) { // we will overflow
3251 int32_t visible_tracks = 0;
3253 while (visible_tracks > canvas_pointer_y_span ) {
3256 while (height_list[atv2->order - (visible_tracks - n)] == 0) {
3260 if ( tracks[atv2->order - ( canvas_pointer_y_span - n)] != 0x00) {
3261 clamp_y_axis = true;
3266 clamp_y_axis = true;
3272 /* this is the pointer's track */
3273 if ((atv2->order - pointer_y_span) > visible_y_high) { // we will overflow
3274 clamp_y_axis = true;
3275 } else if ((atv2->order - pointer_y_span) < visible_y_low) { // we will underflow
3276 clamp_y_axis = true;
3284 } else if (drag_info.last_trackview == tv) {
3285 clamp_y_axis = true;
3289 if (!clamp_y_axis) {
3290 drag_info.last_trackview = tv;
3293 /************************************************************
3295 ************************************************************/
3297 /* compute the amount of pointer motion in frames, and where
3298 the region would be if we moved it by that much.
3301 if (drag_info.move_threshold_passed) {
3303 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
3305 nframes_t sync_frame;
3306 nframes_t sync_offset;
3309 pending_region_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
3311 sync_offset = rv->region()->sync_offset (sync_dir);
3313 /* we don't handle a sync point that lies before zero.
3315 if (sync_dir >= 0 || (sync_dir < 0 && pending_region_position >= sync_offset)) {
3316 sync_frame = pending_region_position + (sync_dir*sync_offset);
3318 /* we snap if the snap modifier is not enabled.
3321 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3322 snap_to (sync_frame);
3325 pending_region_position = rv->region()->adjust_to_sync (sync_frame);
3328 pending_region_position = drag_info.last_frame_position;
3332 pending_region_position = 0;
3335 if (pending_region_position > max_frames - rv->region()->length()) {
3336 pending_region_position = drag_info.last_frame_position;
3339 // printf ("3: pending_region_position= %lu %lu\n", pending_region_position, drag_info.last_frame_position );
3341 if (pending_region_position != drag_info.last_frame_position && !drag_info.x_constrained) {
3343 /* now compute the canvas unit distance we need to move the regionview
3344 to make it appear at the new location.
3347 if (pending_region_position > drag_info.last_frame_position) {
3348 x_delta = ((double) (pending_region_position - drag_info.last_frame_position) / frames_per_unit);
3350 x_delta = -((double) (drag_info.last_frame_position - pending_region_position) / frames_per_unit);
3353 drag_info.last_frame_position = pending_region_position;
3360 /* threshold not passed */
3365 /*************************************************************
3367 ************************************************************/
3369 if (x_delta == 0 && (pointer_y_span == 0)) {
3370 /* haven't reached next snap point, and we're not switching
3371 trackviews. nothing to do.
3378 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3380 RegionView* rv2 = (*i);
3382 // If any regionview is at zero, we need to know so we can stop further leftward motion.
3384 double ix1, ix2, iy1, iy2;
3385 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3386 rv2->get_canvas_group()->i2w (ix1, iy1);
3395 /*************************************************************
3397 ************************************************************/
3401 if (drag_info.first_move) {
3402 if (drag_info.move_threshold_passed) {
3413 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
3414 const list<RegionView*>& layered_regions = selection->regions.by_layer();
3416 for (list<RegionView*>::const_iterator i = layered_regions.begin(); i != layered_regions.end(); ++i) {
3418 RegionView* rv = (*i);
3419 double ix1, ix2, iy1, iy2;
3420 int32_t temp_pointer_y_span = pointer_y_span;
3422 /* get item BBox, which will be relative to parent. so we have
3423 to query on a child, then convert to world coordinates using
3427 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3428 rv->get_canvas_group()->i2w (ix1, iy1);
3429 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3430 AudioTimeAxisView* canvas_atv = dynamic_cast<AudioTimeAxisView*>(tvp2);
3431 AudioTimeAxisView* temp_atv;
3433 if ((pointer_y_span != 0) && !clamp_y_axis) {
3436 for (j = height_list.begin(); j!= height_list.end(); j++) {
3437 if (x == canvas_atv->order) {
3438 /* we found the track the region is on */
3439 if (x != original_pointer_order) {
3440 /*this isn't from the same track we're dragging from */
3441 temp_pointer_y_span = canvas_pointer_y_span;
3443 while (temp_pointer_y_span > 0) {
3444 /* we're moving up canvas-wise,
3445 so we need to find the next track height
3447 if (j != height_list.begin()) {
3450 if (x != original_pointer_order) {
3451 /* we're not from the dragged track, so ignore hidden tracks. */
3453 temp_pointer_y_span++;
3457 temp_pointer_y_span--;
3459 while (temp_pointer_y_span < 0) {
3461 if (x != original_pointer_order) {
3463 temp_pointer_y_span--;
3467 if (j != height_list.end()) {
3470 temp_pointer_y_span++;
3472 /* find out where we'll be when we move and set height accordingly */
3474 tvp2 = trackview_by_y_position (iy1 + y_delta);
3475 temp_atv = dynamic_cast<AudioTimeAxisView*>(tvp2);
3476 rv->set_height (temp_atv->height);
3478 /* if you un-comment the following, the region colours will follow the track colours whilst dragging,
3479 personally, i think this can confuse things, but never mind.
3482 //const GdkColor& col (temp_atv->view->get_region_color());
3483 //rv->set_color (const_cast<GdkColor&>(col));
3490 /* prevent the regionview from being moved to before
3491 the zero position on the canvas.
3496 if (-x_delta > ix1) {
3499 } else if ((x_delta > 0) && (rv->region()->last_frame() > max_frames - x_delta)) {
3500 x_delta = max_frames - rv->region()->last_frame();
3504 if (drag_info.first_move) {
3506 /* hide any dependent views */
3508 rv->get_time_axis_view().hide_dependent_views (*rv);
3510 /* this is subtle. raising the regionview itself won't help,
3511 because raise_to_top() just puts the item on the top of
3512 its parent's stack. so, we need to put the trackview canvas_display group
3513 on the top, since its parent is the whole canvas.
3516 rv->get_canvas_group()->raise_to_top();
3517 rv->get_time_axis_view().canvas_display->raise_to_top();
3518 cursor_group->raise_to_top();
3519 rv->fake_set_opaque (true);
3522 if (drag_info.brushing) {
3523 mouse_brush_insert_region (rv, pending_region_position);
3525 rv->move (x_delta, y_delta);
3528 } /* foreach region */
3532 if (drag_info.first_move && drag_info.move_threshold_passed) {
3533 cursor_group->raise_to_top();
3534 drag_info.first_move = false;
3537 if (x_delta != 0 && !drag_info.brushing) {
3538 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3543 Editor::region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3546 RegionView* rv = reinterpret_cast<RegionView *> (drag_info.data);
3547 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
3548 bool nocommit = true;
3550 RouteTimeAxisView* atv;
3551 bool regionview_y_movement;
3552 bool regionview_x_movement;
3553 vector<RegionView*> copies;
3555 /* first_move is set to false if the regionview has been moved in the
3559 if (drag_info.first_move) {
3566 /* The regionview has been moved at some stage during the grab so we need
3567 to account for any mouse movement between this event and the last one.
3570 region_drag_motion_callback (item, event);
3572 if (Config->get_edit_mode() == Splice && !pre_drag_region_selection.empty()) {
3573 selection->set (pre_drag_region_selection);
3574 pre_drag_region_selection.clear ();
3577 if (drag_info.brushing) {
3578 /* all changes were made during motion event handlers */
3580 if (drag_info.copy) {
3581 for (list<RegionView*>::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
3582 copies.push_back (*i);
3589 /* adjust for track speed */
3592 atv = dynamic_cast<AudioTimeAxisView*> (drag_info.last_trackview);
3593 if (atv && atv->get_diskstream()) {
3594 speed = atv->get_diskstream()->speed();
3597 regionview_x_movement = (drag_info.last_frame_position != (nframes_t) (rv->region()->position()/speed));
3598 regionview_y_movement = (drag_info.last_trackview != &rv->get_time_axis_view());
3600 //printf ("last_frame: %s position is %lu %g\n", rv->get_time_axis_view().name().c_str(), drag_info.last_frame_position, speed);
3601 //printf ("last_rackview: %s \n", drag_info.last_trackview->name().c_str());
3605 if (drag_info.copy) {
3606 if (drag_info.x_constrained) {
3607 op_string = _("fixed time region copy");
3609 op_string = _("region copy");
3612 if (drag_info.x_constrained) {
3613 op_string = _("fixed time region drag");
3615 op_string = _("region drag");
3619 begin_reversible_command (op_string);
3621 if (regionview_y_movement) {
3623 /* moved to a different audio track. */
3625 vector<RegionView*> new_selection;
3627 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ) {
3629 RegionView* rv = (*i);
3631 double ix1, ix2, iy1, iy2;
3633 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3634 rv->get_canvas_group()->i2w (ix1, iy1);
3635 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3636 AudioTimeAxisView* atv2 = dynamic_cast<AudioTimeAxisView*>(tvp2);
3638 boost::shared_ptr<Playlist> from_playlist = rv->region()->playlist();
3639 boost::shared_ptr<Playlist> to_playlist = atv2->playlist();
3641 where = (nframes_t) (unit_to_frame (ix1) * speed);
3642 boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region()));
3644 /* undo the previous hide_dependent_views so that xfades don't
3645 disappear on copying regions
3648 rv->get_time_axis_view().reveal_dependent_views (*rv);
3650 if (!drag_info.copy) {
3652 /* the region that used to be in the old playlist is not
3653 moved to the new one - we make a copy of it. as a result,
3654 any existing editor for the region should no longer be
3658 rv->hide_region_editor();
3659 rv->fake_set_opaque (false);
3661 session->add_command (new MementoCommand<Playlist>(*from_playlist, &from_playlist->get_state(), 0));
3662 from_playlist->remove_region ((rv->region()));
3663 session->add_command (new MementoCommand<Playlist>(*from_playlist, 0, &from_playlist->get_state()));
3667 /* the regionview we dragged around is a temporary copy, queue it for deletion */
3669 copies.push_back (rv);
3672 latest_regionviews.clear ();
3674 sigc::connection c = atv2->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3675 session->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
3676 to_playlist->add_region (new_region, where);
3677 session->add_command (new MementoCommand<Playlist>(*to_playlist, 0, &to_playlist->get_state()));
3680 if (!latest_regionviews.empty()) {
3681 new_selection.insert (new_selection.end(), latest_regionviews.begin(), latest_regionviews.end());
3684 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
3685 was selected in all of them, then removing it from the playlist will have removed all
3686 trace of it from the selection (i.e. there were N regions selected, we removed 1,
3687 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
3688 corresponding regionview, and the selection is now empty).
3690 this could have invalidated any and all iterators into the region selection.
3692 the heuristic we use here is: if the region selection is empty, break out of the loop
3693 here. if the region selection is not empty, then restart the loop because we know that
3694 we must have removed at least the region(view) we've just been working on as well as any
3695 that we processed on previous iterations.
3697 EXCEPT .... if we are doing a copy drag, then the selection hasn't been modified and
3698 we can just iterate.
3701 if (drag_info.copy) {
3704 if (selection->regions.empty()) {
3707 i = selection->regions.by_layer().begin();
3712 selection->set (new_selection);
3716 /* motion within a single track */
3718 list<RegionView*> regions = selection->regions.by_layer();
3720 if (drag_info.copy) {
3721 selection->clear_regions();
3724 for (list<RegionView*>::iterator i = regions.begin(); i != regions.end(); ++i) {
3728 if (rv->region()->locked()) {
3733 if (regionview_x_movement) {
3734 double ownspeed = 1.0;
3735 atv = dynamic_cast<AudioTimeAxisView*> (&(rv->get_time_axis_view()));
3737 if (atv && atv->get_diskstream()) {
3738 ownspeed = atv->get_diskstream()->speed();
3741 /* base the new region position on the current position of the regionview.*/
3743 double ix1, ix2, iy1, iy2;
3745 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3746 rv->get_canvas_group()->i2w (ix1, iy1);
3747 where = (nframes_t) (unit_to_frame (ix1) * ownspeed);
3751 where = rv->region()->position();
3754 boost::shared_ptr<Playlist> to_playlist = rv->region()->playlist();
3756 assert (to_playlist);
3760 session->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
3762 if (drag_info.copy) {
3764 boost::shared_ptr<Region> newregion;
3765 boost::shared_ptr<Region> ar;
3767 if ((ar = boost::dynamic_pointer_cast<AudioRegion>(rv->region())) != 0) {
3768 newregion = RegionFactory::create (ar);
3770 /* XXX MIDI HERE drobilla */
3776 latest_regionviews.clear ();
3777 sigc::connection c = atv->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3778 to_playlist->add_region (newregion, (nframes_t) (where * atv->get_diskstream()->speed()));
3781 if (!latest_regionviews.empty()) {
3782 // XXX why just the first one ? we only expect one
3783 atv->reveal_dependent_views (*latest_regionviews.front());
3784 selection->add (latest_regionviews);
3787 /* if the original region was locked, we don't care for the new one */
3789 newregion->set_locked (false);
3793 /* just change the model */
3795 rv->region()->set_position (where, (void*) this);
3801 session->add_command (new MementoCommand<Playlist>(*to_playlist, 0, &to_playlist->get_state()));
3803 if (drag_info.copy) {
3804 copies.push_back (rv);
3812 commit_reversible_command ();
3815 for (vector<RegionView*>::iterator x = copies.begin(); x != copies.end(); ++x) {
3821 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
3823 /* Either add to or set the set the region selection, unless
3824 this is an alignment click (control used)
3827 if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
3828 TimeAxisView* tv = &rv.get_time_axis_view();
3829 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*>(tv);
3831 if (atv && atv->is_audio_track()) {
3832 speed = atv->get_diskstream()->speed();
3835 nframes64_t where = get_preferred_edit_position();
3839 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
3841 align_region (rv.region(), SyncPoint, (nframes_t) (where * speed));
3843 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
3845 align_region (rv.region(), End, (nframes_t) (where * speed));
3849 align_region (rv.region(), Start, (nframes_t) (where * speed));
3856 Editor::show_verbose_time_cursor (nframes_t frame, double offset, double xpos, double ypos)
3862 nframes_t frame_rate;
3869 switch (Profile->get_small_screen() ? ARDOUR_UI::instance()->primary_clock.mode () : ARDOUR_UI::instance()->secondary_clock.mode ()) {
3870 case AudioClock::BBT:
3871 session->bbt_time (frame, bbt);
3872 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, bbt.bars, bbt.beats, bbt.ticks);
3875 case AudioClock::SMPTE:
3876 session->smpte_time (frame, smpte);
3877 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
3880 case AudioClock::MinSec:
3881 /* XXX this is copied from show_verbose_duration_cursor() */
3882 frame_rate = session->frame_rate();
3883 hours = frame / (frame_rate * 3600);
3884 frame = frame % (frame_rate * 3600);
3885 mins = frame / (frame_rate * 60);
3886 frame = frame % (frame_rate * 60);
3887 secs = (float) frame / (float) frame_rate;
3888 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
3892 snprintf (buf, sizeof(buf), "%u", frame);
3896 if (xpos >= 0 && ypos >=0) {
3897 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
3900 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
3902 show_verbose_canvas_cursor ();
3906 Editor::show_verbose_duration_cursor (nframes_t start, nframes_t end, double offset, double xpos, double ypos)
3913 nframes_t distance, frame_rate;
3915 Meter meter_at_start(session->tempo_map().meter_at(start));
3921 switch (ARDOUR_UI::instance()->secondary_clock.mode ()) {
3922 case AudioClock::BBT:
3923 session->bbt_time (start, sbbt);
3924 session->bbt_time (end, ebbt);
3927 /* XXX this computation won't work well if the
3928 user makes a selection that spans any meter changes.
3931 ebbt.bars -= sbbt.bars;
3932 if (ebbt.beats >= sbbt.beats) {
3933 ebbt.beats -= sbbt.beats;
3936 ebbt.beats = int(meter_at_start.beats_per_bar()) + ebbt.beats - sbbt.beats;
3938 if (ebbt.ticks >= sbbt.ticks) {
3939 ebbt.ticks -= sbbt.ticks;
3942 ebbt.ticks = int(Meter::ticks_per_beat) + ebbt.ticks - sbbt.ticks;
3945 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, ebbt.bars, ebbt.beats, ebbt.ticks);
3948 case AudioClock::SMPTE:
3949 session->smpte_duration (end - start, smpte);
3950 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
3953 case AudioClock::MinSec:
3954 /* XXX this stuff should be elsewhere.. */
3955 distance = end - start;
3956 frame_rate = session->frame_rate();
3957 hours = distance / (frame_rate * 3600);
3958 distance = distance % (frame_rate * 3600);
3959 mins = distance / (frame_rate * 60);
3960 distance = distance % (frame_rate * 60);
3961 secs = (float) distance / (float) frame_rate;
3962 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
3966 snprintf (buf, sizeof(buf), "%u", end - start);
3970 if (xpos >= 0 && ypos >=0) {
3971 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
3974 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
3976 show_verbose_canvas_cursor ();
3980 Editor::collect_new_region_view (RegionView* rv)
3982 latest_regionviews.push_back (rv);
3986 Editor::start_selection_grab (ArdourCanvas::Item* item, GdkEvent* event)
3988 if (clicked_regionview == 0) {
3992 /* lets try to create new Region for the selection */
3994 vector<boost::shared_ptr<AudioRegion> > new_regions;
3995 create_region_from_selection (new_regions);
3997 if (new_regions.empty()) {
4001 /* XXX fix me one day to use all new regions */
4003 boost::shared_ptr<Region> region (new_regions.front());
4005 /* add it to the current stream/playlist.
4007 tricky: the streamview for the track will add a new regionview. we will
4008 catch the signal it sends when it creates the regionview to
4009 set the regionview we want to then drag.
4012 latest_regionviews.clear();
4013 sigc::connection c = clicked_audio_trackview->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
4015 /* A selection grab currently creates two undo/redo operations, one for
4016 creating the new region and another for moving it.
4019 begin_reversible_command (_("selection grab"));
4021 boost::shared_ptr<Playlist> playlist = clicked_trackview->playlist();
4023 XMLNode *before = &(playlist->get_state());
4024 clicked_trackview->playlist()->add_region (region, selection->time[clicked_selection].start);
4025 XMLNode *after = &(playlist->get_state());
4026 session->add_command(new MementoCommand<Playlist>(*playlist, before, after));
4028 commit_reversible_command ();
4032 if (latest_regionviews.empty()) {
4033 /* something went wrong */
4037 /* we need to deselect all other regionviews, and select this one
4038 i'm ignoring undo stuff, because the region creation will take care of it
4040 selection->set (latest_regionviews);
4042 drag_info.item = latest_regionviews.front()->get_canvas_group();
4043 drag_info.data = latest_regionviews.front();
4044 drag_info.motion_callback = &Editor::region_drag_motion_callback;
4045 drag_info.finished_callback = &Editor::region_drag_finished_callback;
4049 drag_info.last_trackview = clicked_trackview;
4050 drag_info.last_frame_position = latest_regionviews.front()->region()->position();
4051 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
4053 show_verbose_time_cursor (drag_info.last_frame_position, 10);
4057 Editor::cancel_selection ()
4059 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4060 (*i)->hide_selection ();
4062 selection->clear ();
4063 clicked_selection = 0;
4067 Editor::start_selection_op (ArdourCanvas::Item* item, GdkEvent* event, SelectionOp op)
4069 nframes_t start = 0;
4076 drag_info.item = item;
4077 drag_info.motion_callback = &Editor::drag_selection;
4078 drag_info.finished_callback = &Editor::end_selection_op;
4083 case CreateSelection:
4084 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
4085 drag_info.copy = true;
4087 drag_info.copy = false;
4089 start_grab (event, selector_cursor);
4092 case SelectionStartTrim:
4093 if (clicked_trackview) {
4094 clicked_trackview->order_selection_trims (item, true);
4096 start_grab (event, trimmer_cursor);
4097 start = selection->time[clicked_selection].start;
4098 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
4101 case SelectionEndTrim:
4102 if (clicked_trackview) {
4103 clicked_trackview->order_selection_trims (item, false);
4105 start_grab (event, trimmer_cursor);
4106 end = selection->time[clicked_selection].end;
4107 drag_info.pointer_frame_offset = drag_info.grab_frame - end;
4111 start = selection->time[clicked_selection].start;
4113 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
4117 if (selection_op == SelectionMove) {
4118 show_verbose_time_cursor(start, 10);
4120 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4125 Editor::drag_selection (ArdourCanvas::Item* item, GdkEvent* event)
4127 nframes_t start = 0;
4130 nframes_t pending_position;
4132 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
4133 pending_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
4135 pending_position = 0;
4138 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4139 snap_to (pending_position);
4142 /* only alter selection if the current frame is
4143 different from the last frame position (adjusted)
4146 if (pending_position == drag_info.last_pointer_frame) return;
4148 switch (selection_op) {
4149 case CreateSelection:
4151 if (drag_info.first_move) {
4152 snap_to (drag_info.grab_frame);
4155 if (pending_position < drag_info.grab_frame) {
4156 start = pending_position;
4157 end = drag_info.grab_frame;
4159 end = pending_position;
4160 start = drag_info.grab_frame;
4163 /* first drag: Either add to the selection
4164 or create a new selection->
4167 if (drag_info.first_move) {
4169 begin_reversible_command (_("range selection"));
4171 if (drag_info.copy) {
4172 /* adding to the selection */
4173 clicked_selection = selection->add (start, end);
4174 drag_info.copy = false;
4176 /* new selection-> */
4177 clicked_selection = selection->set (clicked_trackview, start, end);
4182 case SelectionStartTrim:
4184 if (drag_info.first_move) {
4185 begin_reversible_command (_("trim selection start"));
4188 start = selection->time[clicked_selection].start;
4189 end = selection->time[clicked_selection].end;
4191 if (pending_position > end) {
4194 start = pending_position;
4198 case SelectionEndTrim:
4200 if (drag_info.first_move) {
4201 begin_reversible_command (_("trim selection end"));
4204 start = selection->time[clicked_selection].start;
4205 end = selection->time[clicked_selection].end;
4207 if (pending_position < start) {
4210 end = pending_position;
4217 if (drag_info.first_move) {
4218 begin_reversible_command (_("move selection"));
4221 start = selection->time[clicked_selection].start;
4222 end = selection->time[clicked_selection].end;
4224 length = end - start;
4226 start = pending_position;
4229 end = start + length;
4234 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
4235 start_canvas_autoscroll (1);
4239 selection->replace (clicked_selection, start, end);
4242 drag_info.last_pointer_frame = pending_position;
4243 drag_info.first_move = false;
4245 if (selection_op == SelectionMove) {
4246 show_verbose_time_cursor(start, 10);
4248 show_verbose_time_cursor(pending_position, 10);
4253 Editor::end_selection_op (ArdourCanvas::Item* item, GdkEvent* event)
4255 if (!drag_info.first_move) {
4256 drag_selection (item, event);
4257 /* XXX this is not object-oriented programming at all. ick */
4258 if (selection->time.consolidate()) {
4259 selection->TimeChanged ();
4261 commit_reversible_command ();
4263 /* just a click, no pointer movement.*/
4265 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
4267 selection->clear_time();
4272 /* XXX what happens if its a music selection? */
4273 session->set_audio_range (selection->time);
4274 stop_canvas_autoscroll ();
4278 Editor::start_trim (ArdourCanvas::Item* item, GdkEvent* event)
4281 TimeAxisView* tvp = clicked_trackview;
4282 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
4284 if (tv && tv->is_audio_track()) {
4285 speed = tv->get_diskstream()->speed();
4288 nframes_t region_start = (nframes_t) (clicked_regionview->region()->position() / speed);
4289 nframes_t region_end = (nframes_t) (clicked_regionview->region()->last_frame() / speed);
4290 nframes_t region_length = (nframes_t) (clicked_regionview->region()->length() / speed);
4292 //drag_info.item = clicked_regionview->get_name_highlight();
4293 drag_info.item = item;
4294 drag_info.motion_callback = &Editor::trim_motion_callback;
4295 drag_info.finished_callback = &Editor::trim_finished_callback;
4297 start_grab (event, trimmer_cursor);
4299 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
4300 trim_op = ContentsTrim;
4302 /* These will get overridden for a point trim.*/
4303 if (drag_info.current_pointer_frame < (region_start + region_length/2)) {
4304 /* closer to start */
4305 trim_op = StartTrim;
4306 } else if (drag_info.current_pointer_frame > (region_end - region_length/2)) {
4314 show_verbose_time_cursor(region_start, 10);
4317 show_verbose_time_cursor(region_end, 10);
4320 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4326 Editor::trim_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
4328 RegionView* rv = clicked_regionview;
4329 nframes_t frame_delta = 0;
4330 bool left_direction;
4331 bool obey_snap = !Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier());
4333 /* snap modifier works differently here..
4334 its' current state has to be passed to the
4335 various trim functions in order to work properly
4339 TimeAxisView* tvp = clicked_trackview;
4340 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4341 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
4343 if (tv && tv->is_audio_track()) {
4344 speed = tv->get_diskstream()->speed();
4347 if (drag_info.last_pointer_frame > drag_info.current_pointer_frame) {
4348 left_direction = true;
4350 left_direction = false;
4354 snap_to (drag_info.current_pointer_frame);
4357 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
4361 if (drag_info.first_move) {
4367 trim_type = "Region start trim";
4370 trim_type = "Region end trim";
4373 trim_type = "Region content trim";
4377 begin_reversible_command (trim_type);
4379 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4380 (*i)->fake_set_opaque(false);
4381 (*i)->region()->freeze ();
4383 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
4385 arv->temporarily_hide_envelope ();
4387 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4388 insert_result = motion_frozen_playlists.insert (pl);
4389 if (insert_result.second) {
4390 session->add_command(new MementoCommand<Playlist>(*pl, &pl->get_state(), 0));
4395 if (left_direction) {
4396 frame_delta = (drag_info.last_pointer_frame - drag_info.current_pointer_frame);
4398 frame_delta = (drag_info.current_pointer_frame - drag_info.last_pointer_frame);
4403 if ((left_direction == false) && (drag_info.current_pointer_frame <= rv->region()->first_frame()/speed)) {
4406 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4407 single_start_trim (**i, frame_delta, left_direction, obey_snap);
4413 if ((left_direction == true) && (drag_info.current_pointer_frame > (nframes_t) (rv->region()->last_frame()/speed))) {
4416 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4417 single_end_trim (**i, frame_delta, left_direction, obey_snap);
4424 bool swap_direction = false;
4426 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
4427 swap_direction = true;
4430 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4431 i != selection->regions.by_layer().end(); ++i)
4433 single_contents_trim (**i, frame_delta, left_direction, swap_direction, obey_snap);
4441 show_verbose_time_cursor((nframes_t) (rv->region()->position()/speed), 10);
4444 show_verbose_time_cursor((nframes_t) (rv->region()->last_frame()/speed), 10);
4447 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4451 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4452 drag_info.first_move = false;
4456 Editor::single_contents_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool swap_direction, bool obey_snap)
4458 boost::shared_ptr<Region> region (rv.region());
4460 if (region->locked()) {
4464 nframes_t new_bound;
4467 TimeAxisView* tvp = clicked_trackview;
4468 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4470 if (tv && tv->is_audio_track()) {
4471 speed = tv->get_diskstream()->speed();
4474 if (left_direction) {
4475 if (swap_direction) {
4476 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4478 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4481 if (swap_direction) {
4482 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4484 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4489 snap_to (new_bound);
4491 region->trim_start ((nframes_t) (new_bound * speed), this);
4492 rv.region_changed (StartChanged);
4496 Editor::single_start_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool obey_snap)
4498 boost::shared_ptr<Region> region (rv.region());
4500 if (region->locked()) {
4504 nframes_t new_bound;
4507 TimeAxisView* tvp = clicked_trackview;
4508 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
4510 if (tv && tv->is_audio_track()) {
4511 speed = tv->get_diskstream()->speed();
4514 if (left_direction) {
4515 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4517 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4521 snap_to (new_bound, (left_direction ? 0 : 1));
4524 region->trim_front ((nframes_t) (new_bound * speed), this);
4526 rv.region_changed (Change (LengthChanged|PositionChanged|StartChanged));
4530 Editor::single_end_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool obey_snap)
4532 boost::shared_ptr<Region> region (rv.region());
4534 if (region->locked()) {
4538 nframes_t new_bound;
4541 TimeAxisView* tvp = clicked_trackview;
4542 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
4544 if (tv && tv->is_audio_track()) {
4545 speed = tv->get_diskstream()->speed();
4548 if (left_direction) {
4549 new_bound = (nframes_t) ((region->last_frame() + 1)/speed) - frame_delta;
4551 new_bound = (nframes_t) ((region->last_frame() + 1)/speed) + frame_delta;
4555 snap_to (new_bound);
4557 region->trim_end ((nframes_t) (new_bound * speed), this);
4558 rv.region_changed (LengthChanged);
4562 Editor::trim_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
4564 if (!drag_info.first_move) {
4565 trim_motion_callback (item, event);
4567 if (!selection->selected (clicked_regionview)) {
4568 thaw_region_after_trim (*clicked_regionview);
4571 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4572 i != selection->regions.by_layer().end(); ++i)
4574 thaw_region_after_trim (**i);
4575 (*i)->fake_set_opaque (true);
4579 for (set<boost::shared_ptr<Playlist> >::iterator p = motion_frozen_playlists.begin(); p != motion_frozen_playlists.end(); ++p) {
4581 session->add_command (new MementoCommand<Playlist>(*(*p).get(), 0, &(*p)->get_state()));
4584 motion_frozen_playlists.clear ();
4586 commit_reversible_command();
4588 /* no mouse movement */
4594 Editor::point_trim (GdkEvent* event)
4596 RegionView* rv = clicked_regionview;
4597 nframes_t new_bound = drag_info.current_pointer_frame;
4599 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4600 snap_to (new_bound);
4603 /* Choose action dependant on which button was pressed */
4604 switch (event->button.button) {
4606 trim_op = StartTrim;
4607 begin_reversible_command (_("Start point trim"));
4609 if (selection->selected (rv)) {
4611 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4612 i != selection->regions.by_layer().end(); ++i)
4614 if (!(*i)->region()->locked()) {
4615 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4616 XMLNode &before = pl->get_state();
4617 (*i)->region()->trim_front (new_bound, this);
4618 XMLNode &after = pl->get_state();
4619 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4625 if (!rv->region()->locked()) {
4626 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
4627 XMLNode &before = pl->get_state();
4628 rv->region()->trim_front (new_bound, this);
4629 XMLNode &after = pl->get_state();
4630 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4634 commit_reversible_command();
4639 begin_reversible_command (_("End point trim"));
4641 if (selection->selected (rv)) {
4643 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
4645 if (!(*i)->region()->locked()) {
4646 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4647 XMLNode &before = pl->get_state();
4648 (*i)->region()->trim_end (new_bound, this);
4649 XMLNode &after = pl->get_state();
4650 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4656 if (!rv->region()->locked()) {
4657 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
4658 XMLNode &before = pl->get_state();
4659 rv->region()->trim_end (new_bound, this);
4660 XMLNode &after = pl->get_state();
4661 session->add_command (new MementoCommand<Playlist>(*pl.get(), &before, &after));
4665 commit_reversible_command();
4674 Editor::thaw_region_after_trim (RegionView& rv)
4676 boost::shared_ptr<Region> region (rv.region());
4678 if (region->locked()) {
4682 region->thaw (_("trimmed region"));
4683 XMLNode &after = region->playlist()->get_state();
4684 session->add_command (new MementoCommand<Playlist>(*(region->playlist()), 0, &after));
4686 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(&rv);
4688 arv->unhide_envelope ();
4692 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* event)
4697 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
4698 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
4702 Location* location = find_location_from_marker (marker, is_start);
4703 location->set_hidden (true, this);
4708 Editor::start_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event, RangeMarkerOp op)
4714 drag_info.item = item;
4715 drag_info.motion_callback = &Editor::drag_range_markerbar_op;
4716 drag_info.finished_callback = &Editor::end_range_markerbar_op;
4718 range_marker_op = op;
4720 if (!temp_location) {
4721 temp_location = new Location;
4725 case CreateRangeMarker:
4726 case CreateTransportMarker:
4727 case CreateCDMarker:
4729 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
4730 drag_info.copy = true;
4732 drag_info.copy = false;
4734 start_grab (event, selector_cursor);
4738 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4743 Editor::drag_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4745 nframes_t start = 0;
4747 ArdourCanvas::SimpleRect *crect = (range_marker_op == CreateRangeMarker) ? range_bar_drag_rect: transport_bar_drag_rect;
4749 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4750 snap_to (drag_info.current_pointer_frame);
4753 /* only alter selection if the current frame is
4754 different from the last frame position.
4757 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4759 switch (range_marker_op) {
4760 case CreateRangeMarker:
4761 case CreateTransportMarker:
4762 case CreateCDMarker:
4763 if (drag_info.first_move) {
4764 snap_to (drag_info.grab_frame);
4767 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4768 start = drag_info.current_pointer_frame;
4769 end = drag_info.grab_frame;
4771 end = drag_info.current_pointer_frame;
4772 start = drag_info.grab_frame;
4775 /* first drag: Either add to the selection
4776 or create a new selection.
4779 if (drag_info.first_move) {
4781 temp_location->set (start, end);
4785 update_marker_drag_item (temp_location);
4786 range_marker_drag_rect->show();
4787 range_marker_drag_rect->raise_to_top();
4793 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
4794 start_canvas_autoscroll (1);
4798 temp_location->set (start, end);
4800 double x1 = frame_to_pixel (start);
4801 double x2 = frame_to_pixel (end);
4802 crect->property_x1() = x1;
4803 crect->property_x2() = x2;
4805 update_marker_drag_item (temp_location);
4808 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4809 drag_info.first_move = false;
4811 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4816 Editor::end_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4818 Location * newloc = 0;
4822 if (!drag_info.first_move) {
4823 drag_range_markerbar_op (item, event);
4825 switch (range_marker_op) {
4826 case CreateRangeMarker:
4827 case CreateCDMarker:
4829 begin_reversible_command (_("new range marker"));
4830 XMLNode &before = session->locations()->get_state();
4831 session->locations()->next_available_name(rangename,"unnamed");
4832 if (range_marker_op == CreateCDMarker) {
4833 flags = Location::IsRangeMarker|Location::IsCDMarker;
4836 flags = Location::IsRangeMarker;
4838 newloc = new Location(temp_location->start(), temp_location->end(), rangename, (Location::Flags) flags);
4839 session->locations()->add (newloc, true);
4840 XMLNode &after = session->locations()->get_state();
4841 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
4842 commit_reversible_command ();
4844 range_bar_drag_rect->hide();
4845 range_marker_drag_rect->hide();
4849 case CreateTransportMarker:
4850 // popup menu to pick loop or punch
4851 new_transport_marker_context_menu (&event->button, item);
4856 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
4858 if (Keyboard::no_modifier_keys_pressed (&event->button) && range_marker_op != CreateCDMarker) {
4863 start = session->locations()->first_mark_before (drag_info.grab_frame);
4864 end = session->locations()->first_mark_after (drag_info.grab_frame);
4866 if (end == max_frames) {
4867 end = session->current_end_frame ();
4871 start = session->current_start_frame ();
4874 switch (mouse_mode) {
4876 /* find the two markers on either side and then make the selection from it */
4877 select_all_within (start, end, 0.0f, FLT_MAX, track_views, Selection::Set);
4881 /* find the two markers on either side of the click and make the range out of it */
4882 selection->set (0, start, end);
4891 stop_canvas_autoscroll ();
4897 Editor::start_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4899 drag_info.item = item;
4900 drag_info.motion_callback = &Editor::drag_mouse_zoom;
4901 drag_info.finished_callback = &Editor::end_mouse_zoom;
4903 start_grab (event, zoom_cursor);
4905 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4909 Editor::drag_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4914 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4915 snap_to (drag_info.current_pointer_frame);
4917 if (drag_info.first_move) {
4918 snap_to (drag_info.grab_frame);
4922 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4924 /* base start and end on initial click position */
4925 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4926 start = drag_info.current_pointer_frame;
4927 end = drag_info.grab_frame;
4929 end = drag_info.current_pointer_frame;
4930 start = drag_info.grab_frame;
4935 if (drag_info.first_move) {
4937 zoom_rect->raise_to_top();
4940 reposition_zoom_rect(start, end);
4942 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4943 drag_info.first_move = false;
4945 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4950 Editor::end_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4952 if (!drag_info.first_move) {
4953 drag_mouse_zoom (item, event);
4955 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
4956 temporal_zoom_by_frame (drag_info.grab_frame, drag_info.last_pointer_frame, "mouse zoom");
4958 temporal_zoom_by_frame (drag_info.last_pointer_frame, drag_info.grab_frame, "mouse zoom");
4961 temporal_zoom_to_frame (false, drag_info.grab_frame);
4963 temporal_zoom_step (false);
4964 center_screen (drag_info.grab_frame);
4972 Editor::reposition_zoom_rect (nframes_t start, nframes_t end)
4974 double x1 = frame_to_pixel (start);
4975 double x2 = frame_to_pixel (end);
4976 double y2 = full_canvas_height - 1.0;
4978 zoom_rect->property_x1() = x1;
4979 zoom_rect->property_y1() = 1.0;
4980 zoom_rect->property_x2() = x2;
4981 zoom_rect->property_y2() = y2;
4985 Editor::start_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4987 drag_info.item = item;
4988 drag_info.motion_callback = &Editor::drag_rubberband_select;
4989 drag_info.finished_callback = &Editor::end_rubberband_select;
4991 start_grab (event, cross_hair_cursor);
4993 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4997 Editor::drag_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
5004 /* use a bigger drag threshold than the default */
5006 if (abs ((int) (drag_info.current_pointer_frame - drag_info.grab_frame)) < 8) {
5010 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
5011 if (drag_info.first_move) {
5012 snap_to (drag_info.grab_frame);
5014 snap_to (drag_info.current_pointer_frame);
5017 /* base start and end on initial click position */
5019 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
5020 start = drag_info.current_pointer_frame;
5021 end = drag_info.grab_frame;
5023 end = drag_info.current_pointer_frame;
5024 start = drag_info.grab_frame;
5027 if (drag_info.current_pointer_y < drag_info.grab_y) {
5028 y1 = drag_info.current_pointer_y;
5029 y2 = drag_info.grab_y;
5031 y2 = drag_info.current_pointer_y;
5032 y1 = drag_info.grab_y;
5036 if (start != end || y1 != y2) {
5038 double x1 = frame_to_pixel (start);
5039 double x2 = frame_to_pixel (end);
5041 rubberband_rect->property_x1() = x1;
5042 rubberband_rect->property_y1() = y1;
5043 rubberband_rect->property_x2() = x2;
5044 rubberband_rect->property_y2() = y2;
5046 rubberband_rect->show();
5047 rubberband_rect->raise_to_top();
5049 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
5050 drag_info.first_move = false;
5052 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5057 Editor::end_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
5059 if (!drag_info.first_move) {
5061 drag_rubberband_select (item, event);
5064 if (drag_info.current_pointer_y < drag_info.grab_y) {
5065 y1 = drag_info.current_pointer_y;
5066 y2 = drag_info.grab_y;
5069 y2 = drag_info.current_pointer_y;
5070 y1 = drag_info.grab_y;
5074 Selection::Operation op = Keyboard::selection_type (event->button.state);
5077 begin_reversible_command (_("rubberband selection"));
5079 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
5080 commit = select_all_within (drag_info.grab_frame, drag_info.last_pointer_frame, y1, y2, track_views, op);
5082 commit = select_all_within (drag_info.last_pointer_frame, drag_info.grab_frame, y1, y2, track_views, op);
5086 commit_reversible_command ();
5090 selection->clear_tracks();
5091 selection->clear_regions();
5092 selection->clear_points ();
5093 selection->clear_lines ();
5096 rubberband_rect->hide();
5101 Editor::mouse_rename_region (ArdourCanvas::Item* item, GdkEvent* event)
5103 using namespace Gtkmm2ext;
5105 ArdourPrompter prompter (false);
5107 prompter.set_prompt (_("Name for region:"));
5108 prompter.set_initial_text (clicked_regionview->region()->name());
5109 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
5110 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
5111 prompter.show_all ();
5112 switch (prompter.run ()) {
5113 case Gtk::RESPONSE_ACCEPT:
5115 prompter.get_result(str);
5117 clicked_regionview->region()->set_name (str);
5125 Editor::start_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
5127 drag_info.item = item;
5128 drag_info.motion_callback = &Editor::time_fx_motion;
5129 drag_info.finished_callback = &Editor::end_time_fx;
5133 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5137 Editor::time_fx_motion (ArdourCanvas::Item *item, GdkEvent* event)
5139 RegionView* rv = clicked_regionview;
5141 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
5142 snap_to (drag_info.current_pointer_frame);
5145 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
5149 if (drag_info.current_pointer_frame > rv->region()->position()) {
5150 rv->get_time_axis_view().show_timestretch (rv->region()->position(), drag_info.current_pointer_frame);
5153 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
5154 drag_info.first_move = false;
5156 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5160 Editor::end_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
5162 clicked_regionview->get_time_axis_view().hide_timestretch ();
5164 if (drag_info.first_move) {
5168 if (drag_info.last_pointer_frame < clicked_regionview->region()->position()) {
5169 /* backwards drag of the left edge - not usable */
5173 nframes_t newlen = drag_info.last_pointer_frame - clicked_regionview->region()->position();
5174 #ifdef USE_RUBBERBAND
5175 float percentage = (float) ((double) newlen / (double) clicked_regionview->region()->length());
5177 float percentage = (float) ((double) newlen - (double) clicked_regionview->region()->length()) / ((double) newlen) * 100.0f;
5180 begin_reversible_command (_("timestretch"));
5182 // XXX how do timeFX on multiple regions ?
5185 rs.add (clicked_regionview);
5187 if (time_stretch (rs, percentage) == 0) {
5188 session->commit_reversible_command ();
5193 Editor::mouse_brush_insert_region (RegionView* rv, nframes_t pos)
5195 /* no brushing without a useful snap setting */
5198 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
5201 switch (snap_mode) {
5203 return; /* can't work because it allows region to be placed anywhere */
5208 switch (snap_type) {
5216 /* don't brush a copy over the original */
5218 if (pos == rv->region()->position()) {
5222 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*>(&arv->get_time_axis_view());
5224 if (atv == 0 || !atv->is_audio_track()) {
5228 boost::shared_ptr<Playlist> playlist = atv->playlist();
5229 double speed = atv->get_diskstream()->speed();
5231 XMLNode &before = playlist->get_state();
5232 playlist->add_region (boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (arv->audio_region())), (nframes_t) (pos * speed));
5233 XMLNode &after = playlist->get_state();
5234 session->add_command(new MementoCommand<Playlist>(*playlist.get(), &before, &after));
5236 // playlist is frozen, so we have to update manually
5238 playlist->Modified(); /* EMIT SIGNAL */
5242 Editor::track_height_step_timeout ()
5245 struct timeval delta;
5247 gettimeofday (&now, 0);
5248 timersub (&now, &last_track_height_step_timestamp, &delta);
5250 if (delta.tv_sec * 1000000 + delta.tv_usec > 250000) { /* milliseconds */
5251 current_stepping_trackview = 0;