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 buttongs 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 current_canvas_cursor = grabber_cursor;
265 mouse_gain_button.set_active (true);
266 current_canvas_cursor = cross_hair_cursor;
270 mouse_zoom_button.set_active (true);
271 current_canvas_cursor = zoom_cursor;
275 mouse_timefx_button.set_active (true);
276 current_canvas_cursor = time_fx_cursor; // just use playhead
280 mouse_audition_button.set_active (true);
281 current_canvas_cursor = speaker_cursor;
285 ignore_mouse_mode_toggle = false;
288 track_canvas.get_window()->set_cursor(*current_canvas_cursor);
293 Editor::step_mouse_mode (bool next)
295 switch (current_mouse_mode()) {
297 if (next) set_mouse_mode (MouseRange);
298 else set_mouse_mode (MouseTimeFX);
302 if (next) set_mouse_mode (MouseZoom);
303 else set_mouse_mode (MouseObject);
307 if (next) set_mouse_mode (MouseGain);
308 else set_mouse_mode (MouseRange);
312 if (next) set_mouse_mode (MouseTimeFX);
313 else set_mouse_mode (MouseZoom);
317 if (next) set_mouse_mode (MouseAudition);
318 else set_mouse_mode (MouseGain);
322 if (next) set_mouse_mode (MouseObject);
323 else set_mouse_mode (MouseTimeFX);
329 Editor::button_selection (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
331 /* in object/audition/timefx mode, any button press sets
332 the selection if the object can be selected. this is a
333 bit of hack, because we want to avoid this if the
334 mouse operation is a region alignment.
336 note: not dbl-click or triple-click
339 if (((mouse_mode != MouseObject) &&
340 (mouse_mode != MouseAudition || item_type != RegionItem) &&
341 (mouse_mode != MouseTimeFX || item_type != RegionItem) &&
342 (mouse_mode != MouseRange)) ||
344 (event->type != GDK_BUTTON_PRESS && event->type != GDK_BUTTON_RELEASE || event->button.button > 3)) {
349 if (event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) {
351 if ((event->button.state & Keyboard::RelevantModifierKeyMask) && event->button.button != 1) {
353 /* almost no selection action on modified button-2 or button-3 events */
355 if (item_type != RegionItem && event->button.button != 2) {
361 Selection::Operation op = Keyboard::selection_type (event->button.state);
362 bool press = (event->type == GDK_BUTTON_PRESS);
364 // begin_reversible_command (_("select on click"));
368 if (mouse_mode != MouseRange) {
369 set_selected_regionview_from_click (press, op, true);
370 } else if (event->type == GDK_BUTTON_PRESS) {
371 set_selected_track_as_side_effect ();
375 case RegionViewNameHighlight:
377 if (mouse_mode != MouseRange) {
378 set_selected_regionview_from_click (press, op, true);
379 } else if (event->type == GDK_BUTTON_PRESS) {
380 set_selected_track_as_side_effect ();
384 case FadeInHandleItem:
386 case FadeOutHandleItem:
388 if (mouse_mode != MouseRange) {
389 set_selected_regionview_from_click (press, op, true);
390 } else if (event->type == GDK_BUTTON_PRESS) {
391 set_selected_track_as_side_effect ();
395 case GainAutomationControlPointItem:
396 case PanAutomationControlPointItem:
397 case RedirectAutomationControlPointItem:
398 set_selected_track_as_side_effect ();
399 if (mouse_mode != MouseRange) {
400 set_selected_control_point_from_click (op, false);
405 /* for context click or range selection, select track */
406 if (event->button.button == 3) {
407 set_selected_track_as_side_effect ();
408 } else if (event->type == GDK_BUTTON_PRESS && mouse_mode == MouseRange) {
409 set_selected_track_as_side_effect ();
413 case AutomationTrackItem:
414 set_selected_track_as_side_effect (true);
422 const static double ZERO_GAIN_FRACTION = gain_to_slider_position(dB_to_coefficient(0.0));
425 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
427 track_canvas.grab_focus();
429 if (session && session->actively_recording()) {
433 button_selection (item, event, item_type);
435 if (drag_info.item == 0 &&
436 (Keyboard::is_delete_event (&event->button) ||
437 Keyboard::is_context_menu_event (&event->button) ||
438 Keyboard::is_edit_event (&event->button))) {
440 /* handled by button release */
444 switch (event->button.button) {
447 if (event->type == GDK_BUTTON_PRESS) {
449 if (drag_info.item) {
450 drag_info.item->ungrab (event->button.time);
453 /* single mouse clicks on any of these item types operate
454 independent of mouse mode, mostly because they are
455 not on the main track canvas or because we want
460 case PlayheadCursorItem:
461 start_cursor_grab (item, event);
465 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask(Keyboard::Control|Keyboard::Shift))) {
466 hide_marker (item, event);
468 start_marker_grab (item, event);
472 case TempoMarkerItem:
473 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
474 start_tempo_marker_copy_grab (item, event);
476 start_tempo_marker_grab (item, event);
480 case MeterMarkerItem:
481 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
482 start_meter_marker_copy_grab (item, event);
484 start_meter_marker_grab (item, event);
494 case RangeMarkerBarItem:
495 start_range_markerbar_op (item, event, CreateRangeMarker);
499 case TransportMarkerBarItem:
500 start_range_markerbar_op (item, event, CreateTransportMarker);
509 switch (mouse_mode) {
512 case StartSelectionTrimItem:
513 start_selection_op (item, event, SelectionStartTrim);
516 case EndSelectionTrimItem:
517 start_selection_op (item, event, SelectionEndTrim);
521 if (Keyboard::modifier_state_contains
522 (event->button.state, Keyboard::ModifierMask(Keyboard::Alt))) {
523 // contains and not equals because I can't use alt as a modifier alone.
524 start_selection_grab (item, event);
525 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
526 /* grab selection for moving */
527 start_selection_op (item, event, SelectionMove);
529 /* this was debated, but decided the more common action was to
530 make a new selection */
531 start_selection_op (item, event, CreateSelection);
536 start_selection_op (item, event, CreateSelection);
542 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::Control|Keyboard::Alt)) &&
543 event->type == GDK_BUTTON_PRESS) {
545 start_rubberband_select (item, event);
547 } else if (event->type == GDK_BUTTON_PRESS) {
550 case FadeInHandleItem:
551 start_fade_in_grab (item, event);
554 case FadeOutHandleItem:
555 start_fade_out_grab (item, event);
559 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
560 start_region_copy_grab (item, event);
561 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
562 start_region_brush_grab (item, event);
564 start_region_grab (item, event);
568 case RegionViewNameHighlight:
569 start_trim (item, event);
574 /* rename happens on edit clicks */
575 start_trim (clicked_regionview->get_name_highlight(), event);
579 case GainAutomationControlPointItem:
580 case PanAutomationControlPointItem:
581 case RedirectAutomationControlPointItem:
582 start_control_point_grab (item, event);
586 case GainAutomationLineItem:
587 case PanAutomationLineItem:
588 case RedirectAutomationLineItem:
589 start_line_grab_from_line (item, event);
594 case AutomationTrackItem:
595 start_rubberband_select (item, event);
598 /* <CMT Additions> */
599 case ImageFrameHandleStartItem:
600 imageframe_start_handle_op(item, event) ;
603 case ImageFrameHandleEndItem:
604 imageframe_end_handle_op(item, event) ;
607 case MarkerViewHandleStartItem:
608 markerview_item_start_handle_op(item, event) ;
611 case MarkerViewHandleEndItem:
612 markerview_item_end_handle_op(item, event) ;
615 /* </CMT Additions> */
617 /* <CMT Additions> */
619 start_markerview_grab(item, event) ;
622 start_imageframe_grab(item, event) ;
624 /* </CMT Additions> */
640 // start_line_grab_from_regionview (item, event);
643 case GainControlPointItem:
644 start_control_point_grab (item, event);
648 start_line_grab_from_line (item, event);
651 case GainAutomationControlPointItem:
652 case PanAutomationControlPointItem:
653 case RedirectAutomationControlPointItem:
654 start_control_point_grab (item, event);
665 case GainAutomationControlPointItem:
666 case PanAutomationControlPointItem:
667 case RedirectAutomationControlPointItem:
668 start_control_point_grab (item, event);
671 case GainAutomationLineItem:
672 case PanAutomationLineItem:
673 case RedirectAutomationLineItem:
674 start_line_grab_from_line (item, event);
678 // XXX need automation mode to identify which
680 // start_line_grab_from_regionview (item, event);
690 if (event->type == GDK_BUTTON_PRESS) {
691 start_mouse_zoom (item, event);
698 if (item_type == RegionItem) {
699 start_time_fx (item, event);
706 scrub_reverse_distance = 0;
707 last_scrub_x = event->button.x;
708 scrubbing_direction = 0;
709 track_canvas.get_window()->set_cursor (*transparent_cursor);
710 /* rest handled in motion & release */
719 switch (mouse_mode) {
721 if (event->type == GDK_BUTTON_PRESS) {
724 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
725 start_region_copy_grab (item, event);
727 start_region_grab (item, event);
731 case GainAutomationControlPointItem:
732 case PanAutomationControlPointItem:
733 case RedirectAutomationControlPointItem:
734 start_control_point_grab (item, event);
745 case RegionViewNameHighlight:
746 start_trim (item, event);
751 start_trim (clicked_regionview->get_name_highlight(), event);
762 if (event->type == GDK_BUTTON_PRESS) {
763 /* relax till release */
770 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
771 temporal_zoom_session();
773 temporal_zoom_to_frame (true, event_frame(event));
796 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
798 nframes_t where = event_frame (event, 0, 0);
800 /* no action if we're recording */
802 if (session && session->actively_recording()) {
806 /* first, see if we're finishing a drag ... */
808 if (drag_info.item) {
809 if (end_grab (item, event)) {
810 /* grab dragged, so do nothing else */
815 button_selection (item, event, item_type);
817 /* edit events get handled here */
819 if (drag_info.item == 0 && Keyboard::is_edit_event (&event->button)) {
825 case TempoMarkerItem:
826 edit_tempo_marker (item);
829 case MeterMarkerItem:
830 edit_meter_marker (item);
834 if (clicked_regionview->name_active()) {
835 return mouse_rename_region (item, event);
845 /* context menu events get handled here */
847 if (Keyboard::is_context_menu_event (&event->button)) {
849 if (drag_info.item == 0) {
851 /* no matter which button pops up the context menu, tell the menu
852 widget to use button 1 to drive menu selection.
857 case FadeInHandleItem:
859 case FadeOutHandleItem:
860 popup_fade_context_menu (1, event->button.time, item, item_type);
864 popup_track_context_menu (1, event->button.time, item_type, false, where);
868 case RegionViewNameHighlight:
870 popup_track_context_menu (1, event->button.time, item_type, false, where);
874 popup_track_context_menu (1, event->button.time, item_type, true, where);
877 case AutomationTrackItem:
878 popup_track_context_menu (1, event->button.time, item_type, false, where);
882 case RangeMarkerBarItem:
883 case TransportMarkerBarItem:
886 popup_ruler_menu (pixel_to_frame(event->button.x), item_type);
890 marker_context_menu (&event->button, item);
893 case TempoMarkerItem:
894 tm_marker_context_menu (&event->button, item);
897 case MeterMarkerItem:
898 tm_marker_context_menu (&event->button, item);
901 case CrossfadeViewItem:
902 popup_track_context_menu (1, event->button.time, item_type, false, where);
905 /* <CMT Additions> */
907 popup_imageframe_edit_menu(1, event->button.time, item, true) ;
909 case ImageFrameTimeAxisItem:
910 popup_imageframe_edit_menu(1, event->button.time, item, false) ;
913 popup_marker_time_axis_edit_menu(1, event->button.time, item, true) ;
915 case MarkerTimeAxisItem:
916 popup_marker_time_axis_edit_menu(1, event->button.time, item, false) ;
918 /* <CMT Additions> */
929 /* delete events get handled here */
931 if (drag_info.item == 0 && Keyboard::is_delete_event (&event->button)) {
934 case TempoMarkerItem:
935 remove_tempo_marker (item);
938 case MeterMarkerItem:
939 remove_meter_marker (item);
943 remove_marker (*item, event);
947 if (mouse_mode == MouseObject) {
948 remove_clicked_region ();
952 case GainControlPointItem:
953 if (mouse_mode == MouseGain) {
954 remove_gain_control_point (item, event);
958 case GainAutomationControlPointItem:
959 case PanAutomationControlPointItem:
960 case RedirectAutomationControlPointItem:
961 remove_control_point (item, event);
970 switch (event->button.button) {
974 /* see comments in button_press_handler */
975 case PlayheadCursorItem:
978 case GainAutomationLineItem:
979 case PanAutomationLineItem:
980 case RedirectAutomationLineItem:
981 case StartSelectionTrimItem:
982 case EndSelectionTrimItem:
986 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
987 snap_to (where, 0, true);
989 mouse_add_new_marker (where);
993 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
996 mouse_add_new_tempo_event (where);
1000 mouse_add_new_meter_event (pixel_to_frame (event->button.x));
1008 switch (mouse_mode) {
1010 switch (item_type) {
1011 case AutomationTrackItem:
1012 dynamic_cast<AutomationTimeAxisView*>(clicked_trackview)->add_automation_event
1026 // Gain only makes sense for audio regions
1028 if (!dynamic_cast<AudioRegionView*>(clicked_regionview)) {
1032 switch (item_type) {
1034 dynamic_cast<AudioRegionView*>(clicked_regionview)->add_gain_point_event (item, event);
1038 case AutomationTrackItem:
1039 dynamic_cast<AutomationTimeAxisView*>(clicked_trackview)->
1040 add_automation_event (item, event, where, event->button.y);
1050 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1051 if (scrubbing_direction == 0) {
1052 /* no drag, just a click */
1053 switch (item_type) {
1055 audition_selected_region ();
1061 /* make sure we stop */
1062 session->request_transport_speed (0.0);
1076 switch (mouse_mode) {
1079 switch (item_type) {
1081 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
1083 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::Shift|Keyboard::Alt))) {
1086 // Button2 click is unused
1099 // x_style_paste (where, 1.0);
1119 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1125 switch (item_type) {
1126 case GainControlPointItem:
1127 if (mouse_mode == MouseGain) {
1128 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1129 cp->set_visible (true);
1133 at_y = cp->get_y ();
1134 cp->item->i2w (at_x, at_y);
1138 fraction = 1.0 - (cp->get_y() / cp->line.height());
1140 set_verbose_canvas_cursor (cp->line.get_verbose_cursor_string (fraction), at_x, at_y);
1141 show_verbose_canvas_cursor ();
1143 if (is_drawable()) {
1144 track_canvas.get_window()->set_cursor (*fader_cursor);
1149 case GainAutomationControlPointItem:
1150 case PanAutomationControlPointItem:
1151 case RedirectAutomationControlPointItem:
1152 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1153 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1154 cp->set_visible (true);
1158 at_y = cp->get_y ();
1159 cp->item->i2w (at_x, at_y);
1163 fraction = 1.0 - (cp->get_y() / cp->line.height());
1165 set_verbose_canvas_cursor (cp->line.get_verbose_cursor_string (fraction), at_x, at_y);
1166 show_verbose_canvas_cursor ();
1168 if (is_drawable()) {
1169 track_canvas.get_window()->set_cursor (*fader_cursor);
1175 if (mouse_mode == MouseGain) {
1176 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1178 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredGainLine.get();
1179 if (is_drawable()) {
1180 track_canvas.get_window()->set_cursor (*fader_cursor);
1185 case GainAutomationLineItem:
1186 case RedirectAutomationLineItem:
1187 case PanAutomationLineItem:
1188 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1190 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1192 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredAutomationLine.get();
1194 if (is_drawable()) {
1195 track_canvas.get_window()->set_cursor (*fader_cursor);
1200 case RegionViewNameHighlight:
1201 if (is_drawable() && mouse_mode == MouseObject) {
1202 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1206 case StartSelectionTrimItem:
1207 case EndSelectionTrimItem:
1208 /* <CMT Additions> */
1209 case ImageFrameHandleStartItem:
1210 case ImageFrameHandleEndItem:
1211 case MarkerViewHandleStartItem:
1212 case MarkerViewHandleEndItem:
1213 /* </CMT Additions> */
1215 if (is_drawable()) {
1216 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1220 case PlayheadCursorItem:
1221 if (is_drawable()) {
1222 track_canvas.get_window()->set_cursor (*grabber_cursor);
1226 case RegionViewName:
1228 /* when the name is not an active item, the entire name highlight is for trimming */
1230 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1231 if (mouse_mode == MouseObject && is_drawable()) {
1232 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1238 case AutomationTrackItem:
1239 if (is_drawable()) {
1240 Gdk::Cursor *cursor;
1241 switch (mouse_mode) {
1243 cursor = selector_cursor;
1246 cursor = zoom_cursor;
1249 cursor = cross_hair_cursor;
1253 track_canvas.get_window()->set_cursor (*cursor);
1255 AutomationTimeAxisView* atv;
1256 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1257 clear_entered_track = false;
1258 set_entered_track (atv);
1264 case RangeMarkerBarItem:
1265 case TransportMarkerBarItem:
1268 if (is_drawable()) {
1269 time_canvas.get_window()->set_cursor (*timebar_cursor);
1274 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1277 entered_marker = marker;
1278 marker->set_color_rgba (ARDOUR_UI::config()->canvasvar_EnteredMarker.get());
1280 case MeterMarkerItem:
1281 case TempoMarkerItem:
1282 if (is_drawable()) {
1283 time_canvas.get_window()->set_cursor (*timebar_cursor);
1286 case FadeInHandleItem:
1287 case FadeOutHandleItem:
1288 if (mouse_mode == MouseObject) {
1289 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1291 rect->property_fill_color_rgba() = 0;
1292 rect->property_outline_pixels() = 1;
1301 /* second pass to handle entered track status in a comprehensible way.
1304 switch (item_type) {
1306 case GainAutomationLineItem:
1307 case RedirectAutomationLineItem:
1308 case PanAutomationLineItem:
1309 case GainControlPointItem:
1310 case GainAutomationControlPointItem:
1311 case PanAutomationControlPointItem:
1312 case RedirectAutomationControlPointItem:
1313 /* these do not affect the current entered track state */
1314 clear_entered_track = false;
1317 case AutomationTrackItem:
1318 /* handled above already */
1322 set_entered_track (0);
1330 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1339 switch (item_type) {
1340 case GainControlPointItem:
1341 case GainAutomationControlPointItem:
1342 case PanAutomationControlPointItem:
1343 case RedirectAutomationControlPointItem:
1344 cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
1345 if (cp->line.npoints() > 1) {
1346 if (!cp->selected) {
1347 cp->set_visible (false);
1351 if (is_drawable()) {
1352 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1355 hide_verbose_canvas_cursor ();
1358 case RegionViewNameHighlight:
1359 case StartSelectionTrimItem:
1360 case EndSelectionTrimItem:
1361 case PlayheadCursorItem:
1362 /* <CMT Additions> */
1363 case ImageFrameHandleStartItem:
1364 case ImageFrameHandleEndItem:
1365 case MarkerViewHandleStartItem:
1366 case MarkerViewHandleEndItem:
1367 /* </CMT Additions> */
1368 if (is_drawable()) {
1369 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1374 case GainAutomationLineItem:
1375 case RedirectAutomationLineItem:
1376 case PanAutomationLineItem:
1377 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1379 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1381 line->property_fill_color_rgba() = al->get_line_color();
1383 if (is_drawable()) {
1384 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1388 case RegionViewName:
1389 /* see enter_handler() for notes */
1390 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1391 if (is_drawable() && mouse_mode == MouseObject) {
1392 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1397 case RangeMarkerBarItem:
1398 case TransportMarkerBarItem:
1402 if (is_drawable()) {
1403 time_canvas.get_window()->set_cursor (*timebar_cursor);
1408 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1412 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
1413 location_flags_changed (loc, this);
1416 case MeterMarkerItem:
1417 case TempoMarkerItem:
1419 if (is_drawable()) {
1420 time_canvas.get_window()->set_cursor (*timebar_cursor);
1425 case FadeInHandleItem:
1426 case FadeOutHandleItem:
1427 rv = static_cast<RegionView*>(item->get_data ("regionview"));
1429 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1431 rect->property_fill_color_rgba() = rv->get_fill_color();
1432 rect->property_outline_pixels() = 0;
1437 case AutomationTrackItem:
1438 if (is_drawable()) {
1439 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1440 clear_entered_track = true;
1441 Glib::signal_idle().connect (mem_fun(*this, &Editor::left_automation_track));
1453 Editor::left_automation_track ()
1455 if (clear_entered_track) {
1456 set_entered_track (0);
1457 clear_entered_track = false;
1463 Editor::motion_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type, bool from_autoscroll)
1467 /* We call this so that MOTION_NOTIFY events continue to be
1468 delivered to the canvas. We need to do this because we set
1469 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
1470 the density of the events, at the expense of a round-trip
1471 to the server. Given that this will mostly occur on cases
1472 where DISPLAY = :0.0, and given the cost of what the motion
1473 event might do, its a good tradeoff.
1476 track_canvas.get_pointer (x, y);
1478 if (current_stepping_trackview) {
1479 /* don't keep the persistent stepped trackview if the mouse moves */
1480 current_stepping_trackview = 0;
1481 step_timeout.disconnect ();
1484 if (session && session->actively_recording()) {
1485 /* Sorry. no dragging stuff around while we record */
1489 drag_info.item_type = item_type;
1490 drag_info.last_pointer_x = drag_info.current_pointer_x;
1491 drag_info.last_pointer_y = drag_info.current_pointer_y;
1492 drag_info.current_pointer_frame = event_frame (event, &drag_info.current_pointer_x,
1493 &drag_info.current_pointer_y);
1495 switch (mouse_mode) {
1501 if (scrubbing_direction == 0) {
1503 session->request_locate (drag_info.current_pointer_frame, false);
1504 session->request_transport_speed (0.1);
1505 scrubbing_direction = 1;
1509 if (last_scrub_x > drag_info.current_pointer_x) {
1511 /* pointer moved to the left */
1513 if (scrubbing_direction > 0) {
1515 /* we reversed direction to go backwards */
1518 scrub_reverse_distance += (int) (last_scrub_x - drag_info.current_pointer_x);
1522 /* still moving to the left (backwards) */
1524 scrub_reversals = 0;
1525 scrub_reverse_distance = 0;
1527 delta = 0.01 * (last_scrub_x - drag_info.current_pointer_x);
1528 session->request_transport_speed (session->transport_speed() - delta);
1532 /* pointer moved to the right */
1534 if (scrubbing_direction < 0) {
1535 /* we reversed direction to go forward */
1538 scrub_reverse_distance += (int) (drag_info.current_pointer_x - last_scrub_x);
1541 /* still moving to the right */
1543 scrub_reversals = 0;
1544 scrub_reverse_distance = 0;
1546 delta = 0.01 * (drag_info.current_pointer_x - last_scrub_x);
1547 session->request_transport_speed (session->transport_speed() + delta);
1551 /* if there have been more than 2 opposite motion moves detected, or one that moves
1552 back more than 10 pixels, reverse direction
1555 if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
1557 if (scrubbing_direction > 0) {
1558 /* was forwards, go backwards */
1559 session->request_transport_speed (-0.1);
1560 scrubbing_direction = -1;
1562 /* was backwards, go forwards */
1563 session->request_transport_speed (0.1);
1564 scrubbing_direction = 1;
1567 scrub_reverse_distance = 0;
1568 scrub_reversals = 0;
1572 last_scrub_x = drag_info.current_pointer_x;
1579 if (!from_autoscroll && drag_info.item) {
1580 /* item != 0 is the best test i can think of for dragging.
1582 if (!drag_info.move_threshold_passed) {
1584 bool x_threshold_passed = (::llabs ((nframes64_t) (drag_info.current_pointer_x - drag_info.grab_x)) > 4LL);
1585 bool y_threshold_passed = (::llabs ((nframes64_t) (drag_info.current_pointer_y - drag_info.grab_y)) > 4LL);
1587 drag_info.move_threshold_passed = (x_threshold_passed || y_threshold_passed);
1589 // and change the initial grab loc/frame if this drag info wants us to
1591 if (drag_info.want_move_threshold && drag_info.move_threshold_passed) {
1592 drag_info.grab_frame = drag_info.current_pointer_frame;
1593 drag_info.grab_x = drag_info.current_pointer_x;
1594 drag_info.grab_y = drag_info.current_pointer_y;
1595 drag_info.last_pointer_frame = drag_info.grab_frame;
1596 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
1601 switch (item_type) {
1602 case PlayheadCursorItem:
1604 case GainControlPointItem:
1605 case RedirectAutomationControlPointItem:
1606 case GainAutomationControlPointItem:
1607 case PanAutomationControlPointItem:
1608 case TempoMarkerItem:
1609 case MeterMarkerItem:
1610 case RegionViewNameHighlight:
1611 case StartSelectionTrimItem:
1612 case EndSelectionTrimItem:
1615 case RedirectAutomationLineItem:
1616 case GainAutomationLineItem:
1617 case PanAutomationLineItem:
1618 case FadeInHandleItem:
1619 case FadeOutHandleItem:
1620 /* <CMT Additions> */
1621 case ImageFrameHandleStartItem:
1622 case ImageFrameHandleEndItem:
1623 case MarkerViewHandleStartItem:
1624 case MarkerViewHandleEndItem:
1625 /* </CMT Additions> */
1626 if (drag_info.item && (event->motion.state & Gdk::BUTTON1_MASK ||
1627 (event->motion.state & Gdk::BUTTON2_MASK))) {
1628 if (!from_autoscroll) {
1629 maybe_autoscroll (event);
1631 (this->*(drag_info.motion_callback)) (item, event);
1640 switch (mouse_mode) {
1645 if (drag_info.item && (event->motion.state & GDK_BUTTON1_MASK ||
1646 (event->motion.state & GDK_BUTTON2_MASK))) {
1647 if (!from_autoscroll) {
1648 maybe_autoscroll (event);
1650 (this->*(drag_info.motion_callback)) (item, event);
1661 track_canvas_motion (event);
1662 // drag_info.last_pointer_frame = drag_info.current_pointer_frame;
1670 Editor::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
1672 if (drag_info.item == 0) {
1673 fatal << _("programming error: start_grab called without drag item") << endmsg;
1679 cursor = grabber_cursor;
1682 // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
1684 if (event->button.button == 2) {
1685 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Alt)) {
1686 drag_info.y_constrained = true;
1687 drag_info.x_constrained = false;
1689 drag_info.y_constrained = false;
1690 drag_info.x_constrained = true;
1693 drag_info.x_constrained = false;
1694 drag_info.y_constrained = false;
1697 drag_info.grab_frame = event_frame (event, &drag_info.grab_x, &drag_info.grab_y);
1698 drag_info.last_pointer_frame = drag_info.grab_frame;
1699 drag_info.current_pointer_frame = drag_info.grab_frame;
1700 drag_info.current_pointer_x = drag_info.grab_x;
1701 drag_info.current_pointer_y = drag_info.grab_y;
1702 drag_info.last_pointer_x = drag_info.current_pointer_x;
1703 drag_info.last_pointer_y = drag_info.current_pointer_y;
1704 drag_info.cumulative_x_drag = 0;
1705 drag_info.cumulative_y_drag = 0;
1706 drag_info.first_move = true;
1707 drag_info.move_threshold_passed = false;
1708 drag_info.want_move_threshold = false;
1709 drag_info.pointer_frame_offset = 0;
1710 drag_info.brushing = false;
1711 drag_info.copied_location = 0;
1713 drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK,
1715 event->button.time);
1717 if (session && session->transport_rolling()) {
1718 drag_info.was_rolling = true;
1720 drag_info.was_rolling = false;
1723 switch (snap_type) {
1724 case SnapToRegionStart:
1725 case SnapToRegionEnd:
1726 case SnapToRegionSync:
1727 case SnapToRegionBoundary:
1728 build_region_boundary_cache ();
1736 Editor::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t time)
1738 drag_info.item->ungrab (0);
1739 drag_info.item = new_item;
1742 cursor = grabber_cursor;
1745 drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK, *cursor, time);
1749 Editor::end_grab (ArdourCanvas::Item* item, GdkEvent* event)
1751 bool did_drag = false;
1753 stop_canvas_autoscroll ();
1755 if (drag_info.item == 0) {
1759 drag_info.item->ungrab (event->button.time);
1761 if (drag_info.finished_callback) {
1762 drag_info.last_pointer_x = drag_info.current_pointer_x;
1763 drag_info.last_pointer_y = drag_info.current_pointer_y;
1764 (this->*(drag_info.finished_callback)) (item, event);
1767 did_drag = !drag_info.first_move;
1769 hide_verbose_canvas_cursor();
1772 drag_info.copy = false;
1773 drag_info.motion_callback = 0;
1774 drag_info.finished_callback = 0;
1775 drag_info.last_trackview = 0;
1776 drag_info.last_frame_position = 0;
1777 drag_info.grab_frame = 0;
1778 drag_info.last_pointer_frame = 0;
1779 drag_info.current_pointer_frame = 0;
1780 drag_info.brushing = false;
1782 if (drag_info.copied_location) {
1783 delete drag_info.copied_location;
1784 drag_info.copied_location = 0;
1791 Editor::start_fade_in_grab (ArdourCanvas::Item* item, GdkEvent* event)
1793 drag_info.item = item;
1794 drag_info.motion_callback = &Editor::fade_in_drag_motion_callback;
1795 drag_info.finished_callback = &Editor::fade_in_drag_finished_callback;
1799 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
1800 fatal << _("programming error: fade in canvas item has no regionview data pointer!") << endmsg;
1804 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1806 drag_info.pointer_frame_offset = drag_info.grab_frame - ((nframes_t) arv->audio_region()->fade_in().back()->when + arv->region()->position());
1810 Editor::fade_in_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1812 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1814 nframes_t fade_length;
1816 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1817 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1823 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1827 if (pos < (arv->region()->position() + 64)) {
1828 fade_length = 64; // this should be a minimum defined somewhere
1829 } else if (pos > arv->region()->last_frame()) {
1830 fade_length = arv->region()->length();
1832 fade_length = pos - arv->region()->position();
1834 /* mapover the region selection */
1836 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1838 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1844 tmp->reset_fade_in_shape_width (fade_length);
1847 show_verbose_duration_cursor (arv->region()->position(), arv->region()->position() + fade_length, 10);
1849 drag_info.first_move = false;
1853 Editor::fade_in_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1855 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1857 nframes_t fade_length;
1859 if (drag_info.first_move) return;
1861 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1862 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1867 if (pos < (arv->region()->position() + 64)) {
1868 fade_length = 64; // this should be a minimum defined somewhere
1869 } else if (pos > arv->region()->last_frame()) {
1870 fade_length = arv->region()->length();
1872 fade_length = pos - arv->region()->position();
1875 begin_reversible_command (_("change fade in length"));
1877 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1879 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1885 AutomationList& alist = tmp->audio_region()->fade_in();
1886 XMLNode &before = alist.get_state();
1888 tmp->audio_region()->set_fade_in_length (fade_length);
1890 XMLNode &after = alist.get_state();
1891 session->add_command(new MementoCommand<AutomationList>(alist, &before, &after));
1894 commit_reversible_command ();
1898 Editor::start_fade_out_grab (ArdourCanvas::Item* item, GdkEvent* event)
1900 drag_info.item = item;
1901 drag_info.motion_callback = &Editor::fade_out_drag_motion_callback;
1902 drag_info.finished_callback = &Editor::fade_out_drag_finished_callback;
1906 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
1907 fatal << _("programming error: fade out canvas item has no regionview data pointer!") << endmsg;
1911 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1913 drag_info.pointer_frame_offset = drag_info.grab_frame - (arv->region()->length() - (nframes_t) arv->audio_region()->fade_out().back()->when + arv->region()->position());
1917 Editor::fade_out_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1919 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1921 nframes_t fade_length;
1923 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1924 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1929 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1933 if (pos > (arv->region()->last_frame() - 64)) {
1934 fade_length = 64; // this should really be a minimum fade defined somewhere
1936 else if (pos < arv->region()->position()) {
1937 fade_length = arv->region()->length();
1940 fade_length = arv->region()->last_frame() - pos;
1943 /* mapover the region selection */
1945 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1947 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1953 tmp->reset_fade_out_shape_width (fade_length);
1956 show_verbose_duration_cursor (arv->region()->last_frame() - fade_length, arv->region()->last_frame(), 10);
1958 drag_info.first_move = false;
1962 Editor::fade_out_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1964 if (drag_info.first_move) return;
1966 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1968 nframes_t fade_length;
1970 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1971 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1977 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1981 if (pos > (arv->region()->last_frame() - 64)) {
1982 fade_length = 64; // this should really be a minimum fade defined somewhere
1984 else if (pos < arv->region()->position()) {
1985 fade_length = arv->region()->length();
1988 fade_length = arv->region()->last_frame() - pos;
1991 begin_reversible_command (_("change fade out length"));
1993 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1995 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2001 AutomationList& alist = tmp->audio_region()->fade_out();
2002 XMLNode &before = alist.get_state();
2004 tmp->audio_region()->set_fade_out_length (fade_length);
2006 XMLNode &after = alist.get_state();
2007 session->add_command(new MementoCommand<AutomationList>(alist, &before, &after));
2010 commit_reversible_command ();
2014 Editor::start_cursor_grab (ArdourCanvas::Item* item, GdkEvent* event)
2016 drag_info.item = item;
2017 drag_info.motion_callback = &Editor::cursor_drag_motion_callback;
2018 drag_info.finished_callback = &Editor::cursor_drag_finished_callback;
2022 if ((drag_info.data = (item->get_data ("cursor"))) == 0) {
2023 fatal << _("programming error: cursor canvas item has no cursor data pointer!") << endmsg;
2027 Cursor* cursor = (Cursor *) drag_info.data;
2029 if (cursor == playhead_cursor) {
2030 _dragging_playhead = true;
2032 if (session && drag_info.was_rolling) {
2033 session->request_stop ();
2036 if (session && session->is_auditioning()) {
2037 session->cancel_audition ();
2041 drag_info.pointer_frame_offset = drag_info.grab_frame - cursor->current_frame;
2043 show_verbose_time_cursor (cursor->current_frame, 10);
2047 Editor::cursor_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2049 Cursor* cursor = (Cursor *) drag_info.data;
2050 nframes_t adjusted_frame;
2052 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2053 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2059 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2060 if (cursor == playhead_cursor) {
2061 snap_to (adjusted_frame);
2065 if (adjusted_frame == drag_info.last_pointer_frame) return;
2067 cursor->set_position (adjusted_frame);
2069 UpdateAllTransportClocks (cursor->current_frame);
2071 show_verbose_time_cursor (cursor->current_frame, 10);
2073 drag_info.last_pointer_frame = adjusted_frame;
2074 drag_info.first_move = false;
2078 Editor::cursor_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2080 if (drag_info.first_move) return;
2082 cursor_drag_motion_callback (item, event);
2084 _dragging_playhead = false;
2086 if (item == &playhead_cursor->canvas_item) {
2088 session->request_locate (playhead_cursor->current_frame, drag_info.was_rolling);
2094 Editor::update_marker_drag_item (Location *location)
2096 double x1 = frame_to_pixel (location->start());
2097 double x2 = frame_to_pixel (location->end());
2099 if (location->is_mark()) {
2100 marker_drag_line_points.front().set_x(x1);
2101 marker_drag_line_points.back().set_x(x1);
2102 marker_drag_line->property_points() = marker_drag_line_points;
2105 range_marker_drag_rect->property_x1() = x1;
2106 range_marker_drag_rect->property_x2() = x2;
2111 Editor::start_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2115 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2116 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2122 Location *location = find_location_from_marker (marker, is_start);
2124 drag_info.item = item;
2125 drag_info.data = marker;
2126 drag_info.motion_callback = &Editor::marker_drag_motion_callback;
2127 drag_info.finished_callback = &Editor::marker_drag_finished_callback;
2131 _dragging_edit_point = true;
2133 drag_info.copied_location = new Location (*location);
2134 drag_info.pointer_frame_offset = drag_info.grab_frame - (is_start ? location->start() : location->end());
2136 update_marker_drag_item (location);
2138 if (location->is_mark()) {
2139 // marker_drag_line->show();
2140 // marker_drag_line->raise_to_top();
2142 range_marker_drag_rect->show();
2143 range_marker_drag_rect->raise_to_top();
2147 show_verbose_time_cursor (location->start(), 10);
2149 show_verbose_time_cursor (location->end(), 10);
2152 Selection::Operation op = Keyboard::selection_type (event->button.state);
2155 case Selection::Toggle:
2156 selection->toggle (marker);
2158 case Selection::Set:
2159 selection->set (marker);
2161 case Selection::Extend:
2162 selection->add (marker);
2164 case Selection::Add:
2165 selection->add (marker);
2171 Editor::marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2174 Marker* marker = (Marker *) drag_info.data;
2175 Location *real_location;
2176 Location *copy_location;
2178 bool move_both = false;
2181 if (drag_info.pointer_frame_offset <= drag_info.current_pointer_frame) {
2182 newframe = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2187 nframes_t next = newframe;
2189 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2190 snap_to (newframe, 0, true);
2193 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
2197 /* call this to find out if its the start or end */
2199 if ((real_location = find_location_from_marker (marker, is_start)) == 0) {
2203 if (real_location->locked()) {
2207 /* use the copy that we're "dragging" around */
2209 copy_location = drag_info.copied_location;
2211 f_delta = copy_location->end() - copy_location->start();
2213 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
2217 if (copy_location->is_mark()) {
2220 copy_location->set_start (newframe);
2224 if (is_start) { // start-of-range marker
2227 copy_location->set_start (newframe);
2228 copy_location->set_end (newframe + f_delta);
2229 } else if (newframe < copy_location->end()) {
2230 copy_location->set_start (newframe);
2232 snap_to (next, 1, true);
2233 copy_location->set_end (next);
2234 copy_location->set_start (newframe);
2237 } else { // end marker
2240 copy_location->set_end (newframe);
2241 copy_location->set_start (newframe - f_delta);
2242 } else if (newframe > copy_location->start()) {
2243 copy_location->set_end (newframe);
2245 } else if (newframe > 0) {
2246 snap_to (next, -1, true);
2247 copy_location->set_start (next);
2248 copy_location->set_end (newframe);
2253 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
2254 drag_info.first_move = false;
2256 update_marker_drag_item (copy_location);
2258 LocationMarkers* lm = find_location_markers (real_location);
2259 lm->set_position (copy_location->start(), copy_location->end());
2260 edit_point_clock.set (copy_location->start());
2262 show_verbose_time_cursor (newframe, 10);
2266 Editor::marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2268 if (drag_info.first_move) {
2269 marker_drag_motion_callback (item, event);
2273 _dragging_edit_point = false;
2275 Marker* marker = (Marker *) drag_info.data;
2278 begin_reversible_command ( _("move marker") );
2279 XMLNode &before = session->locations()->get_state();
2281 Location * location = find_location_from_marker (marker, is_start);
2285 if (location->locked()) {
2289 if (location->is_mark()) {
2290 location->set_start (drag_info.copied_location->start());
2292 location->set (drag_info.copied_location->start(), drag_info.copied_location->end());
2296 XMLNode &after = session->locations()->get_state();
2297 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
2298 commit_reversible_command ();
2300 marker_drag_line->hide();
2301 range_marker_drag_rect->hide();
2305 Editor::start_meter_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2308 MeterMarker* meter_marker;
2310 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2311 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2315 meter_marker = dynamic_cast<MeterMarker*> (marker);
2317 MetricSection& section (meter_marker->meter());
2319 if (!section.movable()) {
2323 drag_info.item = item;
2324 drag_info.copy = false;
2325 drag_info.data = marker;
2326 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2327 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2331 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2333 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2337 Editor::start_meter_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2340 MeterMarker* meter_marker;
2342 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2343 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2347 meter_marker = dynamic_cast<MeterMarker*> (marker);
2349 // create a dummy marker for visual representation of moving the copy.
2350 // The actual copying is not done before we reach the finish callback.
2352 snprintf (name, sizeof(name), "%g/%g", meter_marker->meter().beats_per_bar(), meter_marker->meter().note_divisor ());
2353 MeterMarker* new_marker = new MeterMarker(*this, *meter_group, ARDOUR_UI::config()->canvasvar_MeterMarker.get(), name,
2354 *new MeterSection(meter_marker->meter()));
2356 drag_info.item = &new_marker->the_item();
2357 drag_info.copy = true;
2358 drag_info.data = new_marker;
2359 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2360 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2364 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2366 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2370 Editor::meter_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2372 MeterMarker* marker = (MeterMarker *) drag_info.data;
2373 nframes_t adjusted_frame;
2375 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2376 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2382 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2383 snap_to (adjusted_frame);
2386 if (adjusted_frame == drag_info.last_pointer_frame) return;
2388 marker->set_position (adjusted_frame);
2391 drag_info.last_pointer_frame = adjusted_frame;
2392 drag_info.first_move = false;
2394 show_verbose_time_cursor (adjusted_frame, 10);
2398 Editor::meter_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2400 if (drag_info.first_move) return;
2402 meter_marker_drag_motion_callback (drag_info.item, event);
2404 MeterMarker* marker = (MeterMarker *) drag_info.data;
2407 TempoMap& map (session->tempo_map());
2408 map.bbt_time (drag_info.last_pointer_frame, when);
2410 if (drag_info.copy == true) {
2411 begin_reversible_command (_("copy meter mark"));
2412 XMLNode &before = map.get_state();
2413 map.add_meter (marker->meter(), when);
2414 XMLNode &after = map.get_state();
2415 session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2416 commit_reversible_command ();
2418 // delete the dummy marker we used for visual representation of copying.
2419 // a new visual marker will show up automatically.
2422 begin_reversible_command (_("move meter mark"));
2423 XMLNode &before = map.get_state();
2424 map.move_meter (marker->meter(), when);
2425 XMLNode &after = map.get_state();
2426 session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2427 commit_reversible_command ();
2432 Editor::start_tempo_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2435 TempoMarker* tempo_marker;
2437 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2438 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2442 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2443 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2447 MetricSection& section (tempo_marker->tempo());
2449 if (!section.movable()) {
2453 drag_info.item = item;
2454 drag_info.copy = false;
2455 drag_info.data = marker;
2456 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2457 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2461 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2462 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2466 Editor::start_tempo_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2469 TempoMarker* tempo_marker;
2471 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2472 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2476 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2477 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2481 // create a dummy marker for visual representation of moving the copy.
2482 // The actual copying is not done before we reach the finish callback.
2484 snprintf (name, sizeof (name), "%.2f", tempo_marker->tempo().beats_per_minute());
2485 TempoMarker* new_marker = new TempoMarker(*this, *tempo_group, ARDOUR_UI::config()->canvasvar_TempoMarker.get(), name,
2486 *new TempoSection(tempo_marker->tempo()));
2488 drag_info.item = &new_marker->the_item();
2489 drag_info.copy = true;
2490 drag_info.data = new_marker;
2491 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2492 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2496 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2498 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2502 Editor::tempo_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2504 TempoMarker* marker = (TempoMarker *) drag_info.data;
2505 nframes_t adjusted_frame;
2507 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2508 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2514 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2515 snap_to (adjusted_frame);
2518 if (adjusted_frame == drag_info.last_pointer_frame) return;
2520 /* OK, we've moved far enough to make it worth actually move the thing. */
2522 marker->set_position (adjusted_frame);
2524 show_verbose_time_cursor (adjusted_frame, 10);
2526 drag_info.last_pointer_frame = adjusted_frame;
2527 drag_info.first_move = false;
2531 Editor::tempo_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2533 if (drag_info.first_move) return;
2535 tempo_marker_drag_motion_callback (drag_info.item, event);
2537 TempoMarker* marker = (TempoMarker *) drag_info.data;
2540 TempoMap& map (session->tempo_map());
2541 map.bbt_time (drag_info.last_pointer_frame, when);
2543 if (drag_info.copy == true) {
2544 begin_reversible_command (_("copy tempo mark"));
2545 XMLNode &before = map.get_state();
2546 map.add_tempo (marker->tempo(), when);
2547 XMLNode &after = map.get_state();
2548 session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2549 commit_reversible_command ();
2551 // delete the dummy marker we used for visual representation of copying.
2552 // a new visual marker will show up automatically.
2555 begin_reversible_command (_("move tempo mark"));
2556 XMLNode &before = map.get_state();
2557 map.move_tempo (marker->tempo(), when);
2558 XMLNode &after = map.get_state();
2559 session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2560 commit_reversible_command ();
2565 Editor::remove_gain_control_point (ArdourCanvas::Item*item, GdkEvent* event)
2567 ControlPoint* control_point;
2569 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2570 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2574 // We shouldn't remove the first or last gain point
2575 if (control_point->line.is_last_point(*control_point) ||
2576 control_point->line.is_first_point(*control_point)) {
2580 control_point->line.remove_point (*control_point);
2584 Editor::remove_control_point (ArdourCanvas::Item*item, GdkEvent* event)
2586 ControlPoint* control_point;
2588 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2589 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2593 control_point->line.remove_point (*control_point);
2597 Editor::start_control_point_grab (ArdourCanvas::Item* item, GdkEvent* event)
2599 ControlPoint* control_point;
2601 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2602 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2606 drag_info.item = item;
2607 drag_info.data = control_point;
2608 drag_info.motion_callback = &Editor::control_point_drag_motion_callback;
2609 drag_info.finished_callback = &Editor::control_point_drag_finished_callback;
2611 start_grab (event, fader_cursor);
2613 // start the grab at the center of the control point so
2614 // the point doesn't 'jump' to the mouse after the first drag
2615 drag_info.grab_x = control_point->get_x();
2616 drag_info.grab_y = control_point->get_y();
2617 control_point->line.parent_group().i2w(drag_info.grab_x, drag_info.grab_y);
2618 track_canvas.w2c(drag_info.grab_x, drag_info.grab_y,
2619 drag_info.grab_x, drag_info.grab_y);
2621 drag_info.grab_frame = pixel_to_frame(drag_info.grab_x);
2623 control_point->line.start_drag (control_point, drag_info.grab_frame, 0);
2625 float fraction = 1.0 - (control_point->get_y() / control_point->line.height());
2626 set_verbose_canvas_cursor (control_point->line.get_verbose_cursor_string (fraction),
2627 drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
2629 show_verbose_canvas_cursor ();
2633 Editor::control_point_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2635 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2637 double dx = drag_info.current_pointer_x - drag_info.last_pointer_x;
2638 double dy = drag_info.current_pointer_y - drag_info.last_pointer_y;
2640 if (event->button.state & Keyboard::Alt) {
2645 double cx = drag_info.grab_x + drag_info.cumulative_x_drag + dx;
2646 double cy = drag_info.grab_y + drag_info.cumulative_y_drag + dy;
2648 // calculate zero crossing point. back off by .01 to stay on the
2649 // positive side of zero
2651 double zero_gain_y = (1.0 - ZERO_GAIN_FRACTION) * cp->line.height() - .01;
2652 cp->line.parent_group().i2w(_unused, zero_gain_y);
2654 // make sure we hit zero when passing through
2655 if ((cy < zero_gain_y and (cy - dy) > zero_gain_y)
2656 or (cy > zero_gain_y and (cy - dy) < zero_gain_y)) {
2660 if (drag_info.x_constrained) {
2661 cx = drag_info.grab_x;
2663 if (drag_info.y_constrained) {
2664 cy = drag_info.grab_y;
2667 drag_info.cumulative_x_drag = cx - drag_info.grab_x;
2668 drag_info.cumulative_y_drag = cy - drag_info.grab_y;
2670 cp->line.parent_group().w2i (cx, cy);
2674 cy = min ((double) cp->line.height(), cy);
2676 //translate cx to frames
2677 nframes_t cx_frames = unit_to_frame (cx);
2679 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier()) && !drag_info.x_constrained) {
2680 snap_to (cx_frames);
2683 float fraction = 1.0 - (cy / cp->line.height());
2687 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
2693 cp->line.point_drag (*cp, cx_frames , fraction, push);
2695 set_verbose_canvas_cursor_text (cp->line.get_verbose_cursor_string (fraction));
2697 drag_info.first_move = false;
2701 Editor::control_point_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2703 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2705 if (drag_info.first_move) {
2709 if ((event->type == GDK_BUTTON_RELEASE) && (event->button.button == 1) && Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
2710 reset_point_selection ();
2714 control_point_drag_motion_callback (item, event);
2716 cp->line.end_drag (cp);
2720 Editor::start_line_grab_from_regionview (ArdourCanvas::Item* item, GdkEvent* event)
2722 switch (mouse_mode) {
2724 assert(dynamic_cast<AudioRegionView*>(clicked_regionview));
2725 start_line_grab (dynamic_cast<AudioRegionView*>(clicked_regionview)->get_gain_line(), event);
2733 Editor::start_line_grab_from_line (ArdourCanvas::Item* item, GdkEvent* event)
2737 if ((al = reinterpret_cast<AutomationLine*> (item->get_data ("line"))) == 0) {
2738 fatal << _("programming error: line canvas item has no line pointer!") << endmsg;
2742 start_line_grab (al, event);
2746 Editor::start_line_grab (AutomationLine* line, GdkEvent* event)
2750 nframes_t frame_within_region;
2752 /* need to get x coordinate in terms of parent (TimeAxisItemView)
2756 cx = event->button.x;
2757 cy = event->button.y;
2758 line->parent_group().w2i (cx, cy);
2759 frame_within_region = (nframes_t) floor (cx * frames_per_unit);
2761 if (!line->control_points_adjacent (frame_within_region, current_line_drag_info.before,
2762 current_line_drag_info.after)) {
2763 /* no adjacent points */
2767 drag_info.item = &line->grab_item();
2768 drag_info.data = line;
2769 drag_info.motion_callback = &Editor::line_drag_motion_callback;
2770 drag_info.finished_callback = &Editor::line_drag_finished_callback;
2772 start_grab (event, fader_cursor);
2774 double fraction = 1.0 - (cy / line->height());
2776 line->start_drag (0, drag_info.grab_frame, fraction);
2778 set_verbose_canvas_cursor (line->get_verbose_cursor_string (fraction),
2779 drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
2780 show_verbose_canvas_cursor ();
2784 Editor::line_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2786 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
2788 double dy = drag_info.current_pointer_y - drag_info.last_pointer_y;
2790 if (event->button.state & Keyboard::Alt) {
2794 double cx = drag_info.current_pointer_x;
2795 double cy = drag_info.grab_y + drag_info.cumulative_y_drag + dy;
2797 // calculate zero crossing point. back off by .01 to stay on the
2798 // positive side of zero
2800 double zero_gain_y = (1.0 - ZERO_GAIN_FRACTION) * line->height() - .01;
2801 line->parent_group().i2w(_unused, zero_gain_y);
2803 // make sure we hit zero when passing through
2804 if ((cy < zero_gain_y and (cy - dy) > zero_gain_y)
2805 or (cy > zero_gain_y and (cy - dy) < zero_gain_y)) {
2809 drag_info.cumulative_y_drag = cy - drag_info.grab_y;
2811 line->parent_group().w2i (cx, cy);
2814 cy = min ((double) line->height(), cy);
2817 fraction = 1.0 - (cy / line->height());
2821 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
2827 line->line_drag (current_line_drag_info.before, current_line_drag_info.after, fraction, push);
2829 set_verbose_canvas_cursor_text (line->get_verbose_cursor_string (fraction));
2833 Editor::line_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2835 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
2836 line_drag_motion_callback (item, event);
2841 Editor::start_region_grab (ArdourCanvas::Item* item, GdkEvent* event)
2843 if (selection->regions.empty() || clicked_regionview == 0) {
2847 drag_info.copy = false;
2848 drag_info.item = item;
2849 drag_info.data = clicked_regionview;
2850 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2851 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2856 TimeAxisView* tvp = clicked_trackview;
2857 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2859 if (tv && tv->is_audio_track()) {
2860 speed = tv->get_diskstream()->speed();
2863 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2864 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2865 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2866 // we want a move threshold
2867 drag_info.want_move_threshold = true;
2869 show_verbose_time_cursor (drag_info.last_frame_position, 10);
2871 begin_reversible_command (_("move region(s)"));
2875 Editor::start_region_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2877 if (selection->regions.empty() || clicked_regionview == 0) {
2881 drag_info.copy = true;
2882 drag_info.item = item;
2883 drag_info.data = clicked_regionview;
2887 TimeAxisView* tv = &clicked_regionview->get_time_axis_view();
2888 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*>(tv);
2891 if (atv && atv->is_audio_track()) {
2892 speed = atv->get_diskstream()->speed();
2895 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2896 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2897 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2898 // we want a move threshold
2899 drag_info.want_move_threshold = true;
2900 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2901 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2902 show_verbose_time_cursor (drag_info.last_frame_position, 10);
2906 Editor::start_region_brush_grab (ArdourCanvas::Item* item, GdkEvent* event)
2908 if (selection->regions.empty() || clicked_regionview == 0) {
2912 drag_info.copy = false;
2913 drag_info.item = item;
2914 drag_info.data = clicked_regionview;
2915 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2916 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2921 TimeAxisView* tvp = clicked_trackview;
2922 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2924 if (tv && tv->is_audio_track()) {
2925 speed = tv->get_diskstream()->speed();
2928 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2929 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2930 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2931 // we want a move threshold
2932 drag_info.want_move_threshold = true;
2933 drag_info.brushing = true;
2935 begin_reversible_command (_("Drag region brush"));
2939 Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2943 RegionView* rv = reinterpret_cast<RegionView*> (drag_info.data);
2944 nframes_t pending_region_position = 0;
2945 int32_t pointer_y_span = 0, canvas_pointer_y_span = 0, original_pointer_order;
2946 int32_t visible_y_high = 0, visible_y_low = 512; //high meaning higher numbered.. not the height on the screen
2947 bool clamp_y_axis = false;
2948 vector<int32_t> height_list(512) ;
2949 vector<int32_t>::iterator j;
2951 if (drag_info.first_move && drag_info.move_threshold_passed && pre_drag_region_selection.empty()) {
2952 pre_drag_region_selection = selection->regions;
2953 if (Config->get_edit_mode() == Splice) {
2954 RegionSelection all_after = get_regions_after (clicked_regionview->region()->position(), selection->tracks);
2955 selection->set (all_after);
2959 if (drag_info.copy && drag_info.move_threshold_passed && drag_info.want_move_threshold) {
2961 drag_info.want_move_threshold = false; // don't copy again
2963 /* duplicate the region(s) */
2965 vector<RegionView*> new_regionviews;
2967 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
2970 AudioRegionView* arv;
2975 if ((arv = dynamic_cast<AudioRegionView*>(rv)) == 0) {
2976 /* XXX handle MIDI here */
2980 nrv = new AudioRegionView (*arv);
2981 nrv->get_canvas_group()->show ();
2983 new_regionviews.push_back (nrv);
2986 if (new_regionviews.empty()) {
2990 /* reset selection to new regionviews */
2992 selection->set (new_regionviews);
2994 /* reset drag_info data to reflect the fact that we are dragging the copies */
2996 drag_info.data = new_regionviews.front();
2998 swap_grab (new_regionviews.front()->get_canvas_group (), 0, event->motion.time);
3001 /* Which trackview is this ? */
3003 TimeAxisView* tvp = trackview_by_y_position (drag_info.current_pointer_y);
3004 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
3006 /* The region motion is only processed if the pointer is over
3010 if (!tv || !tv->is_audio_track()) {
3011 /* To make sure we hide the verbose canvas cursor when the mouse is
3012 not held over and audiotrack.
3014 hide_verbose_canvas_cursor ();
3018 original_pointer_order = drag_info.last_trackview->order;
3020 /************************************************************
3022 ************************************************************/
3024 if (drag_info.brushing) {
3025 clamp_y_axis = true;
3030 if ((pointer_y_span = (drag_info.last_trackview->order - tv->order)) != 0) {
3032 int32_t children = 0, numtracks = 0;
3033 // XXX hard coding track limit, oh my, so very very bad
3034 bitset <1024> tracks (0x00);
3035 /* get a bitmask representing the visible tracks */
3037 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
3038 TimeAxisView *tracklist_timeview;
3039 tracklist_timeview = (*i);
3040 AudioTimeAxisView* atv2 = dynamic_cast<AudioTimeAxisView*>(tracklist_timeview);
3041 list<TimeAxisView*> children_list;
3043 /* zeroes are audio tracks. ones are other types. */
3045 if (!atv2->hidden()) {
3047 if (visible_y_high < atv2->order) {
3048 visible_y_high = atv2->order;
3050 if (visible_y_low > atv2->order) {
3051 visible_y_low = atv2->order;
3054 if (!atv2->is_audio_track()) {
3055 tracks = tracks |= (0x01 << atv2->order);
3058 height_list[atv2->order] = (*i)->height;
3060 if ((children_list = atv2->get_child_list()).size() > 0) {
3061 for (list<TimeAxisView*>::iterator j = children_list.begin(); j != children_list.end(); ++j) {
3062 tracks = tracks |= (0x01 << (atv2->order + children));
3063 height_list[atv2->order + children] = (*j)->height;
3071 /* find the actual span according to the canvas */
3073 canvas_pointer_y_span = pointer_y_span;
3074 if (drag_info.last_trackview->order >= tv->order) {
3076 for (y = tv->order; y < drag_info.last_trackview->order; y++) {
3077 if (height_list[y] == 0 ) {
3078 canvas_pointer_y_span--;
3083 for (y = drag_info.last_trackview->order;y <= tv->order; y++) {
3084 if ( height_list[y] == 0 ) {
3085 canvas_pointer_y_span++;
3090 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3091 RegionView* rv2 = (*i);
3092 double ix1, ix2, iy1, iy2;
3095 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3096 rv2->get_canvas_group()->i2w (ix1, iy1);
3097 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3098 RouteTimeAxisView* atv2 = dynamic_cast<RouteTimeAxisView*>(tvp2);
3100 if (atv2->order != original_pointer_order) {
3101 /* this isn't the pointer track */
3103 if (canvas_pointer_y_span > 0) {
3105 /* moving up the canvas */
3106 if ((atv2->order - canvas_pointer_y_span) >= visible_y_low) {
3108 int32_t visible_tracks = 0;
3109 while (visible_tracks < canvas_pointer_y_span ) {
3112 while (height_list[atv2->order - (visible_tracks - n)] == 0) {
3113 /* we're passing through a hidden track */
3118 if (tracks[atv2->order - (canvas_pointer_y_span - n)] != 0x00) {
3119 clamp_y_axis = true;
3123 clamp_y_axis = true;
3126 } else if (canvas_pointer_y_span < 0) {
3128 /*moving down the canvas*/
3130 if ((atv2->order - (canvas_pointer_y_span - n)) <= visible_y_high) { // we will overflow
3133 int32_t visible_tracks = 0;
3135 while (visible_tracks > canvas_pointer_y_span ) {
3138 while (height_list[atv2->order - (visible_tracks - n)] == 0) {
3142 if ( tracks[atv2->order - ( canvas_pointer_y_span - n)] != 0x00) {
3143 clamp_y_axis = true;
3148 clamp_y_axis = true;
3154 /* this is the pointer's track */
3155 if ((atv2->order - pointer_y_span) > visible_y_high) { // we will overflow
3156 clamp_y_axis = true;
3157 } else if ((atv2->order - pointer_y_span) < visible_y_low) { // we will underflow
3158 clamp_y_axis = true;
3166 } else if (drag_info.last_trackview == tv) {
3167 clamp_y_axis = true;
3171 if (!clamp_y_axis) {
3172 drag_info.last_trackview = tv;
3175 /************************************************************
3177 ************************************************************/
3179 /* compute the amount of pointer motion in frames, and where
3180 the region would be if we moved it by that much.
3183 if (drag_info.move_threshold_passed) {
3185 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
3187 nframes_t sync_frame;
3188 nframes_t sync_offset;
3191 pending_region_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
3193 sync_offset = rv->region()->sync_offset (sync_dir);
3194 sync_frame = rv->region()->adjust_to_sync (pending_region_position);
3196 /* we snap if the snap modifier is not enabled.
3199 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3200 snap_to (sync_frame);
3203 if (sync_frame - sync_offset <= sync_frame) {
3204 pending_region_position = sync_frame - (sync_dir*sync_offset);
3206 pending_region_position = 0;
3210 pending_region_position = 0;
3213 if (pending_region_position > max_frames - rv->region()->length()) {
3214 pending_region_position = drag_info.last_frame_position;
3217 // printf ("3: pending_region_position= %lu %lu\n", pending_region_position, drag_info.last_frame_position );
3219 if (pending_region_position != drag_info.last_frame_position && !drag_info.x_constrained) {
3221 /* now compute the canvas unit distance we need to move the regiondrag_info.last_trackview->order
3222 to make it appear at the new location.
3225 if (pending_region_position > drag_info.last_frame_position) {
3226 x_delta = ((double) (pending_region_position - drag_info.last_frame_position) / frames_per_unit);
3228 x_delta = -((double) (drag_info.last_frame_position - pending_region_position) / frames_per_unit);
3231 drag_info.last_frame_position = pending_region_position;
3238 /* threshold not passed */
3243 /*************************************************************
3245 ************************************************************/
3247 if (x_delta == 0 && (pointer_y_span == 0)) {
3248 /* haven't reached next snap point, and we're not switching
3249 trackviews. nothing to do.
3256 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3258 RegionView* rv2 = (*i);
3260 // If any regionview is at zero, we need to know so we can stop further leftward motion.
3262 double ix1, ix2, iy1, iy2;
3263 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3264 rv2->get_canvas_group()->i2w (ix1, iy1);
3273 /*************************************************************
3275 ************************************************************/
3279 if (drag_info.first_move) {
3280 if (drag_info.move_threshold_passed) {
3291 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
3292 const list<RegionView*>& layered_regions = selection->regions.by_layer();
3294 for (list<RegionView*>::const_iterator i = layered_regions.begin(); i != layered_regions.end(); ++i) {
3296 RegionView* rv = (*i);
3297 double ix1, ix2, iy1, iy2;
3298 int32_t temp_pointer_y_span = pointer_y_span;
3300 /* get item BBox, which will be relative to parent. so we have
3301 to query on a child, then convert to world coordinates using
3305 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3306 rv->get_canvas_group()->i2w (ix1, iy1);
3307 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3308 AudioTimeAxisView* canvas_atv = dynamic_cast<AudioTimeAxisView*>(tvp2);
3309 AudioTimeAxisView* temp_atv;
3311 if ((pointer_y_span != 0) && !clamp_y_axis) {
3314 for (j = height_list.begin(); j!= height_list.end(); j++) {
3315 if (x == canvas_atv->order) {
3316 /* we found the track the region is on */
3317 if (x != original_pointer_order) {
3318 /*this isn't from the same track we're dragging from */
3319 temp_pointer_y_span = canvas_pointer_y_span;
3321 while (temp_pointer_y_span > 0) {
3322 /* we're moving up canvas-wise,
3323 so we need to find the next track height
3325 if (j != height_list.begin()) {
3328 if (x != original_pointer_order) {
3329 /* we're not from the dragged track, so ignore hidden tracks. */
3331 temp_pointer_y_span++;
3335 temp_pointer_y_span--;
3337 while (temp_pointer_y_span < 0) {
3339 if (x != original_pointer_order) {
3341 temp_pointer_y_span--;
3345 if (j != height_list.end()) {
3348 temp_pointer_y_span++;
3350 /* find out where we'll be when we move and set height accordingly */
3352 tvp2 = trackview_by_y_position (iy1 + y_delta);
3353 temp_atv = dynamic_cast<AudioTimeAxisView*>(tvp2);
3354 rv->set_height (temp_atv->height);
3356 /* if you un-comment the following, the region colours will follow the track colours whilst dragging,
3357 personally, i think this can confuse things, but never mind.
3360 //const GdkColor& col (temp_atv->view->get_region_color());
3361 //rv->set_color (const_cast<GdkColor&>(col));
3368 /* prevent the regionview from being moved to before
3369 the zero position on the canvas.
3374 if (-x_delta > ix1) {
3377 } else if ((x_delta > 0) && (rv->region()->last_frame() > max_frames - x_delta)) {
3378 x_delta = max_frames - rv->region()->last_frame();
3382 if (drag_info.first_move) {
3384 /* hide any dependent views */
3386 rv->get_time_axis_view().hide_dependent_views (*rv);
3388 /* this is subtle. raising the regionview itself won't help,
3389 because raise_to_top() just puts the item on the top of
3390 its parent's stack. so, we need to put the trackview canvas_display group
3391 on the top, since its parent is the whole canvas.
3394 rv->get_canvas_group()->raise_to_top();
3395 rv->get_time_axis_view().canvas_display->raise_to_top();
3396 cursor_group->raise_to_top();
3397 rv->fake_set_opaque (true);
3400 if (drag_info.brushing) {
3401 mouse_brush_insert_region (rv, pending_region_position);
3403 rv->move (x_delta, y_delta);
3406 } /* foreach region */
3410 if (drag_info.first_move && drag_info.move_threshold_passed) {
3411 cursor_group->raise_to_top();
3412 drag_info.first_move = false;
3415 if (x_delta != 0 && !drag_info.brushing) {
3416 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3421 Editor::region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3424 RegionView* rv = reinterpret_cast<RegionView *> (drag_info.data);
3425 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
3426 bool nocommit = true;
3428 RouteTimeAxisView* atv;
3429 bool regionview_y_movement;
3430 bool regionview_x_movement;
3431 vector<RegionView*> copies;
3433 /* first_move is set to false if the regionview has been moved in the
3437 if (drag_info.first_move) {
3444 /* The regionview has been moved at some stage during the grab so we need
3445 to account for any mouse movement between this event and the last one.
3448 region_drag_motion_callback (item, event);
3450 if (Config->get_edit_mode() == Splice && !pre_drag_region_selection.empty()) {
3451 selection->set (pre_drag_region_selection);
3452 pre_drag_region_selection.clear ();
3455 if (drag_info.brushing) {
3456 /* all changes were made during motion event handlers */
3458 if (drag_info.copy) {
3459 for (list<RegionView*>::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
3460 copies.push_back (*i);
3467 /* adjust for track speed */
3470 atv = dynamic_cast<AudioTimeAxisView*> (drag_info.last_trackview);
3471 if (atv && atv->get_diskstream()) {
3472 speed = atv->get_diskstream()->speed();
3475 regionview_x_movement = (drag_info.last_frame_position != (nframes_t) (rv->region()->position()/speed));
3476 regionview_y_movement = (drag_info.last_trackview != &rv->get_time_axis_view());
3478 //printf ("last_frame: %s position is %lu %g\n", rv->get_time_axis_view().name().c_str(), drag_info.last_frame_position, speed);
3479 //printf ("last_rackview: %s \n", drag_info.last_trackview->name().c_str());
3483 if (drag_info.copy) {
3484 if (drag_info.x_constrained) {
3485 op_string = _("fixed time region copy");
3487 op_string = _("region copy");
3490 if (drag_info.x_constrained) {
3491 op_string = _("fixed time region drag");
3493 op_string = _("region drag");
3497 begin_reversible_command (op_string);
3499 if (regionview_y_movement) {
3501 /* moved to a different audio track. */
3503 vector<RegionView*> new_selection;
3505 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ) {
3507 RegionView* rv = (*i);
3509 double ix1, ix2, iy1, iy2;
3511 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3512 rv->get_canvas_group()->i2w (ix1, iy1);
3513 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3514 AudioTimeAxisView* atv2 = dynamic_cast<AudioTimeAxisView*>(tvp2);
3516 boost::shared_ptr<Playlist> from_playlist = rv->region()->playlist();
3517 boost::shared_ptr<Playlist> to_playlist = atv2->playlist();
3519 where = (nframes_t) (unit_to_frame (ix1) * speed);
3520 boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region()));
3522 /* undo the previous hide_dependent_views so that xfades don't
3523 disappear on copying regions
3526 rv->get_time_axis_view().reveal_dependent_views (*rv);
3528 if (!drag_info.copy) {
3530 /* the region that used to be in the old playlist is not
3531 moved to the new one - we make a copy of it. as a result,
3532 any existing editor for the region should no longer be
3536 rv->hide_region_editor();
3537 rv->fake_set_opaque (false);
3539 session->add_command (new MementoCommand<Playlist>(*from_playlist, &from_playlist->get_state(), 0));
3540 from_playlist->remove_region ((rv->region()));
3541 session->add_command (new MementoCommand<Playlist>(*from_playlist, 0, &from_playlist->get_state()));
3545 /* the regionview we dragged around is a temporary copy, queue it for deletion */
3547 copies.push_back (rv);
3550 latest_regionviews.clear ();
3552 sigc::connection c = atv2->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3553 session->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
3554 to_playlist->add_region (new_region, where);
3555 session->add_command (new MementoCommand<Playlist>(*to_playlist, 0, &to_playlist->get_state()));
3558 if (!latest_regionviews.empty()) {
3559 new_selection.insert (new_selection.end(), latest_regionviews.begin(), latest_regionviews.end());
3562 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
3563 was selected in all of them, then removing it from the playlist will have removed all
3564 trace of it from the selection (i.e. there were N regions selected, we removed 1,
3565 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
3566 corresponding regionview, and the selection is now empty).
3568 this could have invalidated any and all iterators into the region selection.
3570 the heuristic we use here is: if the region selection is empty, break out of the loop
3571 here. if the region selection is not empty, then restart the loop because we know that
3572 we must have removed at least the region(view) we've just been working on as well as any
3573 that we processed on previous iterations.
3575 EXCEPT .... if we are doing a copy drag, then the selection hasn't been modified and
3576 we can just iterate.
3579 if (drag_info.copy) {
3582 if (selection->regions.empty()) {
3585 i = selection->regions.by_layer().begin();
3590 selection->set (new_selection);
3594 /* motion within a single track */
3596 list<RegionView*> regions = selection->regions.by_layer();
3598 if (drag_info.copy) {
3599 selection->clear_regions();
3602 for (list<RegionView*>::iterator i = regions.begin(); i != regions.end(); ++i) {
3606 if (rv->region()->locked()) {
3611 if (regionview_x_movement) {
3612 double ownspeed = 1.0;
3613 atv = dynamic_cast<AudioTimeAxisView*> (&(rv->get_time_axis_view()));
3615 if (atv && atv->get_diskstream()) {
3616 ownspeed = atv->get_diskstream()->speed();
3619 /* base the new region position on the current position of the regionview.*/
3621 double ix1, ix2, iy1, iy2;
3623 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3624 rv->get_canvas_group()->i2w (ix1, iy1);
3625 where = (nframes_t) (unit_to_frame (ix1) * ownspeed);
3629 where = rv->region()->position();
3632 boost::shared_ptr<Playlist> to_playlist = rv->region()->playlist();
3634 assert (to_playlist);
3638 session->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
3640 if (drag_info.copy) {
3642 boost::shared_ptr<Region> newregion;
3643 boost::shared_ptr<Region> ar;
3645 if ((ar = boost::dynamic_pointer_cast<AudioRegion>(rv->region())) != 0) {
3646 newregion = RegionFactory::create (ar);
3648 /* XXX MIDI HERE drobilla */
3654 latest_regionviews.clear ();
3655 sigc::connection c = atv->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3656 to_playlist->add_region (newregion, (nframes_t) (where * atv->get_diskstream()->speed()));
3659 if (!latest_regionviews.empty()) {
3660 // XXX why just the first one ? we only expect one
3661 atv->reveal_dependent_views (*latest_regionviews.front());
3662 selection->add (latest_regionviews);
3665 /* if the original region was locked, we don't care for the new one */
3667 newregion->set_locked (false);
3671 /* just change the model */
3673 rv->region()->set_position (where, (void*) this);
3679 session->add_command (new MementoCommand<Playlist>(*to_playlist, 0, &to_playlist->get_state()));
3681 if (drag_info.copy) {
3682 copies.push_back (rv);
3690 commit_reversible_command ();
3693 for (vector<RegionView*>::iterator x = copies.begin(); x != copies.end(); ++x) {
3699 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
3701 /* Either add to or set the set the region selection, unless
3702 this is an alignment click (control used)
3705 if (Keyboard::modifier_state_contains (event->state, Keyboard::Control)) {
3706 TimeAxisView* tv = &rv.get_time_axis_view();
3707 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*>(tv);
3709 if (atv && atv->is_audio_track()) {
3710 speed = atv->get_diskstream()->speed();
3713 nframes64_t where = get_preferred_edit_position();
3717 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Alt))) {
3719 align_region (rv.region(), SyncPoint, (nframes_t) (where * speed));
3721 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Shift))) {
3723 align_region (rv.region(), End, (nframes_t) (where * speed));
3727 align_region (rv.region(), Start, (nframes_t) (where * speed));
3734 Editor::show_verbose_time_cursor (nframes_t frame, double offset, double xpos, double ypos)
3740 nframes_t frame_rate;
3747 switch (Profile->get_small_screen() ? ARDOUR_UI::instance()->primary_clock.mode () : ARDOUR_UI::instance()->secondary_clock.mode ()) {
3748 case AudioClock::BBT:
3749 session->bbt_time (frame, bbt);
3750 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, bbt.bars, bbt.beats, bbt.ticks);
3753 case AudioClock::SMPTE:
3754 session->smpte_time (frame, smpte);
3755 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
3758 case AudioClock::MinSec:
3759 /* XXX this is copied from show_verbose_duration_cursor() */
3760 frame_rate = session->frame_rate();
3761 hours = frame / (frame_rate * 3600);
3762 frame = frame % (frame_rate * 3600);
3763 mins = frame / (frame_rate * 60);
3764 frame = frame % (frame_rate * 60);
3765 secs = (float) frame / (float) frame_rate;
3766 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
3770 snprintf (buf, sizeof(buf), "%u", frame);
3774 if (xpos >= 0 && ypos >=0) {
3775 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
3778 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
3780 show_verbose_canvas_cursor ();
3784 Editor::show_verbose_duration_cursor (nframes_t start, nframes_t end, double offset, double xpos, double ypos)
3791 nframes_t distance, frame_rate;
3793 Meter meter_at_start(session->tempo_map().meter_at(start));
3799 switch (ARDOUR_UI::instance()->secondary_clock.mode ()) {
3800 case AudioClock::BBT:
3801 session->bbt_time (start, sbbt);
3802 session->bbt_time (end, ebbt);
3805 /* XXX this computation won't work well if the
3806 user makes a selection that spans any meter changes.
3809 ebbt.bars -= sbbt.bars;
3810 if (ebbt.beats >= sbbt.beats) {
3811 ebbt.beats -= sbbt.beats;
3814 ebbt.beats = int(meter_at_start.beats_per_bar()) + ebbt.beats - sbbt.beats;
3816 if (ebbt.ticks >= sbbt.ticks) {
3817 ebbt.ticks -= sbbt.ticks;
3820 ebbt.ticks = int(Meter::ticks_per_beat) + ebbt.ticks - sbbt.ticks;
3823 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, ebbt.bars, ebbt.beats, ebbt.ticks);
3826 case AudioClock::SMPTE:
3827 session->smpte_duration (end - start, smpte);
3828 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
3831 case AudioClock::MinSec:
3832 /* XXX this stuff should be elsewhere.. */
3833 distance = end - start;
3834 frame_rate = session->frame_rate();
3835 hours = distance / (frame_rate * 3600);
3836 distance = distance % (frame_rate * 3600);
3837 mins = distance / (frame_rate * 60);
3838 distance = distance % (frame_rate * 60);
3839 secs = (float) distance / (float) frame_rate;
3840 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
3844 snprintf (buf, sizeof(buf), "%u", end - start);
3848 if (xpos >= 0 && ypos >=0) {
3849 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
3852 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
3854 show_verbose_canvas_cursor ();
3858 Editor::collect_new_region_view (RegionView* rv)
3860 latest_regionviews.push_back (rv);
3864 Editor::start_selection_grab (ArdourCanvas::Item* item, GdkEvent* event)
3866 if (clicked_regionview == 0) {
3870 /* lets try to create new Region for the selection */
3872 vector<boost::shared_ptr<AudioRegion> > new_regions;
3873 create_region_from_selection (new_regions);
3875 if (new_regions.empty()) {
3879 /* XXX fix me one day to use all new regions */
3881 boost::shared_ptr<Region> region (new_regions.front());
3883 /* add it to the current stream/playlist.
3885 tricky: the streamview for the track will add a new regionview. we will
3886 catch the signal it sends when it creates the regionview to
3887 set the regionview we want to then drag.
3890 latest_regionviews.clear();
3891 sigc::connection c = clicked_audio_trackview->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3893 /* A selection grab currently creates two undo/redo operations, one for
3894 creating the new region and another for moving it.
3897 begin_reversible_command (_("selection grab"));
3899 boost::shared_ptr<Playlist> playlist = clicked_trackview->playlist();
3901 XMLNode *before = &(playlist->get_state());
3902 clicked_trackview->playlist()->add_region (region, selection->time[clicked_selection].start);
3903 XMLNode *after = &(playlist->get_state());
3904 session->add_command(new MementoCommand<Playlist>(*playlist, before, after));
3906 commit_reversible_command ();
3910 if (latest_regionviews.empty()) {
3911 /* something went wrong */
3915 /* we need to deselect all other regionviews, and select this one
3916 i'm ignoring undo stuff, because the region creation will take care of it
3918 selection->set (latest_regionviews);
3920 drag_info.item = latest_regionviews.front()->get_canvas_group();
3921 drag_info.data = latest_regionviews.front();
3922 drag_info.motion_callback = &Editor::region_drag_motion_callback;
3923 drag_info.finished_callback = &Editor::region_drag_finished_callback;
3927 drag_info.last_trackview = clicked_trackview;
3928 drag_info.last_frame_position = latest_regionviews.front()->region()->position();
3929 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
3931 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3935 Editor::cancel_selection ()
3937 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
3938 (*i)->hide_selection ();
3940 begin_reversible_command (_("cancel selection"));
3941 selection->clear ();
3942 clicked_selection = 0;
3943 commit_reversible_command ();
3947 Editor::start_selection_op (ArdourCanvas::Item* item, GdkEvent* event, SelectionOp op)
3949 nframes_t start = 0;
3956 drag_info.item = item;
3957 drag_info.motion_callback = &Editor::drag_selection;
3958 drag_info.finished_callback = &Editor::end_selection_op;
3963 case CreateSelection:
3964 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
3965 drag_info.copy = true;
3967 drag_info.copy = false;
3969 start_grab (event, selector_cursor);
3972 case SelectionStartTrim:
3973 if (clicked_trackview) {
3974 clicked_trackview->order_selection_trims (item, true);
3976 start_grab (event, trimmer_cursor);
3977 start = selection->time[clicked_selection].start;
3978 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
3981 case SelectionEndTrim:
3982 if (clicked_trackview) {
3983 clicked_trackview->order_selection_trims (item, false);
3985 start_grab (event, trimmer_cursor);
3986 end = selection->time[clicked_selection].end;
3987 drag_info.pointer_frame_offset = drag_info.grab_frame - end;
3991 start = selection->time[clicked_selection].start;
3993 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
3997 if (selection_op == SelectionMove) {
3998 show_verbose_time_cursor(start, 10);
4000 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4005 Editor::drag_selection (ArdourCanvas::Item* item, GdkEvent* event)
4007 nframes_t start = 0;
4010 nframes_t pending_position;
4012 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
4013 pending_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
4015 pending_position = 0;
4018 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4019 snap_to (pending_position);
4022 /* only alter selection if the current frame is
4023 different from the last frame position (adjusted)
4026 if (pending_position == drag_info.last_pointer_frame) return;
4028 switch (selection_op) {
4029 case CreateSelection:
4031 if (drag_info.first_move) {
4032 snap_to (drag_info.grab_frame);
4035 if (pending_position < drag_info.grab_frame) {
4036 start = pending_position;
4037 end = drag_info.grab_frame;
4039 end = pending_position;
4040 start = drag_info.grab_frame;
4043 /* first drag: Either add to the selection
4044 or create a new selection->
4047 if (drag_info.first_move) {
4049 begin_reversible_command (_("range selection"));
4051 if (drag_info.copy) {
4052 /* adding to the selection */
4053 clicked_selection = selection->add (start, end);
4054 drag_info.copy = false;
4056 /* new selection-> */
4057 clicked_selection = selection->set (clicked_trackview, start, end);
4062 case SelectionStartTrim:
4064 if (drag_info.first_move) {
4065 begin_reversible_command (_("trim selection start"));
4068 start = selection->time[clicked_selection].start;
4069 end = selection->time[clicked_selection].end;
4071 if (pending_position > end) {
4074 start = pending_position;
4078 case SelectionEndTrim:
4080 if (drag_info.first_move) {
4081 begin_reversible_command (_("trim selection end"));
4084 start = selection->time[clicked_selection].start;
4085 end = selection->time[clicked_selection].end;
4087 if (pending_position < start) {
4090 end = pending_position;
4097 if (drag_info.first_move) {
4098 begin_reversible_command (_("move selection"));
4101 start = selection->time[clicked_selection].start;
4102 end = selection->time[clicked_selection].end;
4104 length = end - start;
4106 start = pending_position;
4109 end = start + length;
4114 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
4115 start_canvas_autoscroll (1);
4119 selection->replace (clicked_selection, start, end);
4122 drag_info.last_pointer_frame = pending_position;
4123 drag_info.first_move = false;
4125 if (selection_op == SelectionMove) {
4126 show_verbose_time_cursor(start, 10);
4128 show_verbose_time_cursor(pending_position, 10);
4133 Editor::end_selection_op (ArdourCanvas::Item* item, GdkEvent* event)
4135 if (!drag_info.first_move) {
4136 drag_selection (item, event);
4137 /* XXX this is not object-oriented programming at all. ick */
4138 if (selection->time.consolidate()) {
4139 selection->TimeChanged ();
4141 commit_reversible_command ();
4143 /* just a click, no pointer movement.*/
4145 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
4147 selection->clear_time();
4152 /* XXX what happens if its a music selection? */
4153 session->set_audio_range (selection->time);
4154 stop_canvas_autoscroll ();
4158 Editor::start_trim (ArdourCanvas::Item* item, GdkEvent* event)
4161 TimeAxisView* tvp = clicked_trackview;
4162 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
4164 if (tv && tv->is_audio_track()) {
4165 speed = tv->get_diskstream()->speed();
4168 nframes_t region_start = (nframes_t) (clicked_regionview->region()->position() / speed);
4169 nframes_t region_end = (nframes_t) (clicked_regionview->region()->last_frame() / speed);
4170 nframes_t region_length = (nframes_t) (clicked_regionview->region()->length() / speed);
4172 //drag_info.item = clicked_regionview->get_name_highlight();
4173 drag_info.item = item;
4174 drag_info.motion_callback = &Editor::trim_motion_callback;
4175 drag_info.finished_callback = &Editor::trim_finished_callback;
4177 start_grab (event, trimmer_cursor);
4179 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
4180 trim_op = ContentsTrim;
4182 /* These will get overridden for a point trim.*/
4183 if (drag_info.current_pointer_frame < (region_start + region_length/2)) {
4184 /* closer to start */
4185 trim_op = StartTrim;
4186 } else if (drag_info.current_pointer_frame > (region_end - region_length/2)) {
4194 show_verbose_time_cursor(region_start, 10);
4197 show_verbose_time_cursor(region_end, 10);
4200 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4206 Editor::trim_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
4208 RegionView* rv = clicked_regionview;
4209 nframes_t frame_delta = 0;
4210 bool left_direction;
4211 bool obey_snap = !Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier());
4213 /* snap modifier works differently here..
4214 its' current state has to be passed to the
4215 various trim functions in order to work properly
4219 TimeAxisView* tvp = clicked_trackview;
4220 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4221 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
4223 if (tv && tv->is_audio_track()) {
4224 speed = tv->get_diskstream()->speed();
4227 if (drag_info.last_pointer_frame > drag_info.current_pointer_frame) {
4228 left_direction = true;
4230 left_direction = false;
4234 snap_to (drag_info.current_pointer_frame);
4237 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
4241 if (drag_info.first_move) {
4247 trim_type = "Region start trim";
4250 trim_type = "Region end trim";
4253 trim_type = "Region content trim";
4257 begin_reversible_command (trim_type);
4259 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4260 (*i)->fake_set_opaque(false);
4261 (*i)->region()->freeze ();
4263 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
4265 arv->temporarily_hide_envelope ();
4267 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4268 insert_result = motion_frozen_playlists.insert (pl);
4269 if (insert_result.second) {
4270 session->add_command(new MementoCommand<Playlist>(*pl, &pl->get_state(), 0));
4275 if (left_direction) {
4276 frame_delta = (drag_info.last_pointer_frame - drag_info.current_pointer_frame);
4278 frame_delta = (drag_info.current_pointer_frame - drag_info.last_pointer_frame);
4283 if ((left_direction == false) && (drag_info.current_pointer_frame <= rv->region()->first_frame()/speed)) {
4286 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4287 single_start_trim (**i, frame_delta, left_direction, obey_snap);
4293 if ((left_direction == true) && (drag_info.current_pointer_frame > (nframes_t) (rv->region()->last_frame()/speed))) {
4296 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4297 single_end_trim (**i, frame_delta, left_direction, obey_snap);
4304 bool swap_direction = false;
4306 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
4307 swap_direction = true;
4310 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4311 i != selection->regions.by_layer().end(); ++i)
4313 single_contents_trim (**i, frame_delta, left_direction, swap_direction, obey_snap);
4321 show_verbose_time_cursor((nframes_t) (rv->region()->position()/speed), 10);
4324 show_verbose_time_cursor((nframes_t) (rv->region()->last_frame()/speed), 10);
4327 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4331 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4332 drag_info.first_move = false;
4336 Editor::single_contents_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool swap_direction, bool obey_snap)
4338 boost::shared_ptr<Region> region (rv.region());
4340 if (region->locked()) {
4344 nframes_t new_bound;
4347 TimeAxisView* tvp = clicked_trackview;
4348 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4350 if (tv && tv->is_audio_track()) {
4351 speed = tv->get_diskstream()->speed();
4354 if (left_direction) {
4355 if (swap_direction) {
4356 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4358 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4361 if (swap_direction) {
4362 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4364 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4369 snap_to (new_bound);
4371 region->trim_start ((nframes_t) (new_bound * speed), this);
4372 rv.region_changed (StartChanged);
4376 Editor::single_start_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool obey_snap)
4378 boost::shared_ptr<Region> region (rv.region());
4380 if (region->locked()) {
4384 nframes_t new_bound;
4387 TimeAxisView* tvp = clicked_trackview;
4388 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
4390 if (tv && tv->is_audio_track()) {
4391 speed = tv->get_diskstream()->speed();
4394 if (left_direction) {
4395 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4397 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4401 snap_to (new_bound, (left_direction ? 0 : 1));
4404 region->trim_front ((nframes_t) (new_bound * speed), this);
4406 rv.region_changed (Change (LengthChanged|PositionChanged|StartChanged));
4410 Editor::single_end_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool obey_snap)
4412 boost::shared_ptr<Region> region (rv.region());
4414 if (region->locked()) {
4418 nframes_t new_bound;
4421 TimeAxisView* tvp = clicked_trackview;
4422 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
4424 if (tv && tv->is_audio_track()) {
4425 speed = tv->get_diskstream()->speed();
4428 if (left_direction) {
4429 new_bound = (nframes_t) ((region->last_frame() + 1)/speed) - frame_delta;
4431 new_bound = (nframes_t) ((region->last_frame() + 1)/speed) + frame_delta;
4435 snap_to (new_bound);
4437 region->trim_end ((nframes_t) (new_bound * speed), this);
4438 rv.region_changed (LengthChanged);
4442 Editor::trim_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
4444 if (!drag_info.first_move) {
4445 trim_motion_callback (item, event);
4447 if (!selection->selected (clicked_regionview)) {
4448 thaw_region_after_trim (*clicked_regionview);
4451 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4452 i != selection->regions.by_layer().end(); ++i)
4454 thaw_region_after_trim (**i);
4455 (*i)->fake_set_opaque (true);
4459 for (set<boost::shared_ptr<Playlist> >::iterator p = motion_frozen_playlists.begin(); p != motion_frozen_playlists.end(); ++p) {
4461 session->add_command (new MementoCommand<Playlist>(*(*p).get(), 0, &(*p)->get_state()));
4464 motion_frozen_playlists.clear ();
4466 commit_reversible_command();
4468 /* no mouse movement */
4474 Editor::point_trim (GdkEvent* event)
4476 RegionView* rv = clicked_regionview;
4477 nframes_t new_bound = drag_info.current_pointer_frame;
4479 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4480 snap_to (new_bound);
4483 /* Choose action dependant on which button was pressed */
4484 switch (event->button.button) {
4486 trim_op = StartTrim;
4487 begin_reversible_command (_("Start point trim"));
4489 if (selection->selected (rv)) {
4491 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4492 i != selection->regions.by_layer().end(); ++i)
4494 if (!(*i)->region()->locked()) {
4495 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4496 XMLNode &before = pl->get_state();
4497 (*i)->region()->trim_front (new_bound, this);
4498 XMLNode &after = pl->get_state();
4499 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4505 if (!rv->region()->locked()) {
4506 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
4507 XMLNode &before = pl->get_state();
4508 rv->region()->trim_front (new_bound, this);
4509 XMLNode &after = pl->get_state();
4510 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4514 commit_reversible_command();
4519 begin_reversible_command (_("End point trim"));
4521 if (selection->selected (rv)) {
4523 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
4525 if (!(*i)->region()->locked()) {
4526 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4527 XMLNode &before = pl->get_state();
4528 (*i)->region()->trim_end (new_bound, this);
4529 XMLNode &after = pl->get_state();
4530 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4536 if (!rv->region()->locked()) {
4537 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
4538 XMLNode &before = pl->get_state();
4539 rv->region()->trim_end (new_bound, this);
4540 XMLNode &after = pl->get_state();
4541 session->add_command (new MementoCommand<Playlist>(*pl.get(), &before, &after));
4545 commit_reversible_command();
4554 Editor::thaw_region_after_trim (RegionView& rv)
4556 boost::shared_ptr<Region> region (rv.region());
4558 if (region->locked()) {
4562 region->thaw (_("trimmed region"));
4563 XMLNode &after = region->playlist()->get_state();
4564 session->add_command (new MementoCommand<Playlist>(*(region->playlist()), 0, &after));
4566 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(&rv);
4568 arv->unhide_envelope ();
4572 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* event)
4577 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
4578 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
4582 Location* location = find_location_from_marker (marker, is_start);
4583 location->set_hidden (true, this);
4588 Editor::start_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event, RangeMarkerOp op)
4594 drag_info.item = item;
4595 drag_info.motion_callback = &Editor::drag_range_markerbar_op;
4596 drag_info.finished_callback = &Editor::end_range_markerbar_op;
4598 range_marker_op = op;
4600 if (!temp_location) {
4601 temp_location = new Location;
4605 case CreateRangeMarker:
4606 case CreateTransportMarker:
4608 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
4609 drag_info.copy = true;
4611 drag_info.copy = false;
4613 start_grab (event, selector_cursor);
4617 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4622 Editor::drag_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4624 nframes_t start = 0;
4626 ArdourCanvas::SimpleRect *crect = (range_marker_op == CreateRangeMarker) ? range_bar_drag_rect: transport_bar_drag_rect;
4628 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4629 snap_to (drag_info.current_pointer_frame);
4632 /* only alter selection if the current frame is
4633 different from the last frame position.
4636 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4638 switch (range_marker_op) {
4639 case CreateRangeMarker:
4640 case CreateTransportMarker:
4641 if (drag_info.first_move) {
4642 snap_to (drag_info.grab_frame);
4645 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4646 start = drag_info.current_pointer_frame;
4647 end = drag_info.grab_frame;
4649 end = drag_info.current_pointer_frame;
4650 start = drag_info.grab_frame;
4653 /* first drag: Either add to the selection
4654 or create a new selection.
4657 if (drag_info.first_move) {
4659 temp_location->set (start, end);
4663 update_marker_drag_item (temp_location);
4664 range_marker_drag_rect->show();
4665 range_marker_drag_rect->raise_to_top();
4671 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
4672 start_canvas_autoscroll (1);
4676 temp_location->set (start, end);
4678 double x1 = frame_to_pixel (start);
4679 double x2 = frame_to_pixel (end);
4680 crect->property_x1() = x1;
4681 crect->property_x2() = x2;
4683 update_marker_drag_item (temp_location);
4686 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4687 drag_info.first_move = false;
4689 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4694 Editor::end_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4696 Location * newloc = 0;
4699 if (!drag_info.first_move) {
4700 drag_range_markerbar_op (item, event);
4702 switch (range_marker_op) {
4703 case CreateRangeMarker:
4705 begin_reversible_command (_("new range marker"));
4706 XMLNode &before = session->locations()->get_state();
4707 session->locations()->next_available_name(rangename,"unnamed");
4708 newloc = new Location(temp_location->start(), temp_location->end(), rangename, Location::IsRangeMarker);
4709 session->locations()->add (newloc, true);
4710 XMLNode &after = session->locations()->get_state();
4711 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
4712 commit_reversible_command ();
4714 range_bar_drag_rect->hide();
4715 range_marker_drag_rect->hide();
4719 case CreateTransportMarker:
4720 // popup menu to pick loop or punch
4721 new_transport_marker_context_menu (&event->button, item);
4726 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
4728 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
4733 start = session->locations()->first_mark_before (drag_info.grab_frame);
4734 end = session->locations()->first_mark_after (drag_info.grab_frame);
4736 if (end == max_frames) {
4737 end = session->current_end_frame ();
4741 start = session->current_start_frame ();
4744 switch (mouse_mode) {
4746 /* find the two markers on either side and then make the selection from it */
4747 select_all_within (start, end, 0.0f, FLT_MAX, track_views, Selection::Set);
4751 /* find the two markers on either side of the click and make the range out of it */
4752 selection->set (0, start, end);
4761 stop_canvas_autoscroll ();
4767 Editor::start_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4769 drag_info.item = item;
4770 drag_info.motion_callback = &Editor::drag_mouse_zoom;
4771 drag_info.finished_callback = &Editor::end_mouse_zoom;
4773 start_grab (event, zoom_cursor);
4775 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4779 Editor::drag_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4784 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4785 snap_to (drag_info.current_pointer_frame);
4787 if (drag_info.first_move) {
4788 snap_to (drag_info.grab_frame);
4792 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4794 /* base start and end on initial click position */
4795 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4796 start = drag_info.current_pointer_frame;
4797 end = drag_info.grab_frame;
4799 end = drag_info.current_pointer_frame;
4800 start = drag_info.grab_frame;
4805 if (drag_info.first_move) {
4807 zoom_rect->raise_to_top();
4810 reposition_zoom_rect(start, end);
4812 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4813 drag_info.first_move = false;
4815 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4820 Editor::end_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4822 if (!drag_info.first_move) {
4823 drag_mouse_zoom (item, event);
4825 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
4826 temporal_zoom_by_frame (drag_info.grab_frame, drag_info.last_pointer_frame, "mouse zoom");
4828 temporal_zoom_by_frame (drag_info.last_pointer_frame, drag_info.grab_frame, "mouse zoom");
4831 temporal_zoom_to_frame (false, drag_info.grab_frame);
4833 temporal_zoom_step (false);
4834 center_screen (drag_info.grab_frame);
4842 Editor::reposition_zoom_rect (nframes_t start, nframes_t end)
4844 double x1 = frame_to_pixel (start);
4845 double x2 = frame_to_pixel (end);
4846 double y2 = full_canvas_height - 1.0;
4848 zoom_rect->property_x1() = x1;
4849 zoom_rect->property_y1() = 1.0;
4850 zoom_rect->property_x2() = x2;
4851 zoom_rect->property_y2() = y2;
4855 Editor::start_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4857 drag_info.item = item;
4858 drag_info.motion_callback = &Editor::drag_rubberband_select;
4859 drag_info.finished_callback = &Editor::end_rubberband_select;
4861 start_grab (event, cross_hair_cursor);
4863 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4867 Editor::drag_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4874 /* use a bigger drag threshold than the default */
4876 if (abs ((int) (drag_info.current_pointer_frame - drag_info.grab_frame)) < 8) {
4880 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4881 if (drag_info.first_move) {
4882 snap_to (drag_info.grab_frame);
4884 snap_to (drag_info.current_pointer_frame);
4887 /* base start and end on initial click position */
4889 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4890 start = drag_info.current_pointer_frame;
4891 end = drag_info.grab_frame;
4893 end = drag_info.current_pointer_frame;
4894 start = drag_info.grab_frame;
4897 if (drag_info.current_pointer_y < drag_info.grab_y) {
4898 y1 = drag_info.current_pointer_y;
4899 y2 = drag_info.grab_y;
4901 y2 = drag_info.current_pointer_y;
4902 y1 = drag_info.grab_y;
4906 if (start != end || y1 != y2) {
4908 double x1 = frame_to_pixel (start);
4909 double x2 = frame_to_pixel (end);
4911 rubberband_rect->property_x1() = x1;
4912 rubberband_rect->property_y1() = y1;
4913 rubberband_rect->property_x2() = x2;
4914 rubberband_rect->property_y2() = y2;
4916 rubberband_rect->show();
4917 rubberband_rect->raise_to_top();
4919 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4920 drag_info.first_move = false;
4922 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4927 Editor::end_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4929 if (!drag_info.first_move) {
4931 drag_rubberband_select (item, event);
4934 if (drag_info.current_pointer_y < drag_info.grab_y) {
4935 y1 = drag_info.current_pointer_y;
4936 y2 = drag_info.grab_y;
4939 y2 = drag_info.current_pointer_y;
4940 y1 = drag_info.grab_y;
4944 Selection::Operation op = Keyboard::selection_type (event->button.state);
4947 begin_reversible_command (_("rubberband selection"));
4949 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
4950 commit = select_all_within (drag_info.grab_frame, drag_info.last_pointer_frame, y1, y2, track_views, op);
4952 commit = select_all_within (drag_info.last_pointer_frame, drag_info.grab_frame, y1, y2, track_views, op);
4956 commit_reversible_command ();
4960 selection->clear_tracks();
4961 selection->clear_regions();
4962 selection->clear_points ();
4963 selection->clear_lines ();
4966 rubberband_rect->hide();
4971 Editor::mouse_rename_region (ArdourCanvas::Item* item, GdkEvent* event)
4973 using namespace Gtkmm2ext;
4975 ArdourPrompter prompter (false);
4977 prompter.set_prompt (_("Name for region:"));
4978 prompter.set_initial_text (clicked_regionview->region()->name());
4979 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
4980 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
4981 prompter.show_all ();
4982 switch (prompter.run ()) {
4983 case Gtk::RESPONSE_ACCEPT:
4985 prompter.get_result(str);
4987 clicked_regionview->region()->set_name (str);
4995 Editor::start_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
4997 drag_info.item = item;
4998 drag_info.motion_callback = &Editor::time_fx_motion;
4999 drag_info.finished_callback = &Editor::end_time_fx;
5003 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5007 Editor::time_fx_motion (ArdourCanvas::Item *item, GdkEvent* event)
5009 RegionView* rv = clicked_regionview;
5011 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
5012 snap_to (drag_info.current_pointer_frame);
5015 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
5019 if (drag_info.current_pointer_frame > rv->region()->position()) {
5020 rv->get_time_axis_view().show_timestretch (rv->region()->position(), drag_info.current_pointer_frame);
5023 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
5024 drag_info.first_move = false;
5026 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5030 Editor::end_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
5032 clicked_regionview->get_time_axis_view().hide_timestretch ();
5034 if (drag_info.first_move) {
5038 if (drag_info.last_pointer_frame < clicked_regionview->region()->position()) {
5039 /* backwards drag of the left edge - not usable */
5043 nframes_t newlen = drag_info.last_pointer_frame - clicked_regionview->region()->position();
5044 float percentage = (float) ((double) newlen - (double) clicked_regionview->region()->length()) / ((double) newlen) * 100.0f;
5046 begin_reversible_command (_("timestretch"));
5048 if (run_timestretch (selection->regions, percentage) == 0) {
5049 session->commit_reversible_command ();
5054 Editor::mouse_brush_insert_region (RegionView* rv, nframes_t pos)
5056 /* no brushing without a useful snap setting */
5059 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
5062 switch (snap_mode) {
5064 return; /* can't work because it allows region to be placed anywhere */
5069 switch (snap_type) {
5077 /* don't brush a copy over the original */
5079 if (pos == rv->region()->position()) {
5083 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*>(&arv->get_time_axis_view());
5085 if (atv == 0 || !atv->is_audio_track()) {
5089 boost::shared_ptr<Playlist> playlist = atv->playlist();
5090 double speed = atv->get_diskstream()->speed();
5092 XMLNode &before = playlist->get_state();
5093 playlist->add_region (boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (arv->audio_region())), (nframes_t) (pos * speed));
5094 XMLNode &after = playlist->get_state();
5095 session->add_command(new MementoCommand<Playlist>(*playlist.get(), &before, &after));
5097 // playlist is frozen, so we have to update manually
5099 playlist->Modified(); /* EMIT SIGNAL */
5103 Editor::track_height_step_timeout ()
5106 struct timeval delta;
5108 gettimeofday (&now, 0);
5109 timersub (&now, &last_track_height_step_timestamp, &delta);
5111 if (delta.tv_sec * 1000000 + delta.tv_usec > 250000) { /* milliseconds */
5112 current_stepping_trackview = 0;