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 (Config->get_edit_mode() == Splice && drag_info.first_move && drag_info.move_threshold_passed && pre_drag_region_selection.empty()) {
2952 pre_drag_region_selection = selection->regions;
2953 RegionSelection all_after = get_regions_after (clicked_regionview->region()->position(), selection->tracks);
2954 selection->set (all_after);
2957 if (drag_info.copy && drag_info.move_threshold_passed && drag_info.want_move_threshold) {
2959 drag_info.want_move_threshold = false; // don't copy again
2961 /* duplicate the region(s) */
2963 vector<RegionView*> new_regionviews;
2965 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
2968 AudioRegionView* arv;
2973 if ((arv = dynamic_cast<AudioRegionView*>(rv)) == 0) {
2974 /* XXX handle MIDI here */
2978 nrv = new AudioRegionView (*arv);
2979 nrv->get_canvas_group()->show ();
2981 new_regionviews.push_back (nrv);
2984 if (new_regionviews.empty()) {
2988 /* reset selection to new regionviews */
2990 selection->set (new_regionviews);
2992 /* reset drag_info data to reflect the fact that we are dragging the copies */
2994 drag_info.data = new_regionviews.front();
2996 swap_grab (new_regionviews.front()->get_canvas_group (), 0, event->motion.time);
2999 /* Which trackview is this ? */
3001 TimeAxisView* tvp = trackview_by_y_position (drag_info.current_pointer_y);
3002 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
3004 /* The region motion is only processed if the pointer is over
3008 if (!tv || !tv->is_audio_track()) {
3009 /* To make sure we hide the verbose canvas cursor when the mouse is
3010 not held over and audiotrack.
3012 hide_verbose_canvas_cursor ();
3016 original_pointer_order = drag_info.last_trackview->order;
3018 /************************************************************
3020 ************************************************************/
3022 if (drag_info.brushing) {
3023 clamp_y_axis = true;
3028 if ((pointer_y_span = (drag_info.last_trackview->order - tv->order)) != 0) {
3030 /* drop any splice-induced selection madness */
3032 if (!pre_drag_region_selection.empty()) {
3033 selection->set (pre_drag_region_selection);
3034 pre_drag_region_selection.clear ();
3037 int32_t children = 0, numtracks = 0;
3038 // XXX hard coding track limit, oh my, so very very bad
3039 bitset <1024> tracks (0x00);
3040 /* get a bitmask representing the visible tracks */
3042 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
3043 TimeAxisView *tracklist_timeview;
3044 tracklist_timeview = (*i);
3045 AudioTimeAxisView* atv2 = dynamic_cast<AudioTimeAxisView*>(tracklist_timeview);
3046 list<TimeAxisView*> children_list;
3048 /* zeroes are audio tracks. ones are other types. */
3050 if (!atv2->hidden()) {
3052 if (visible_y_high < atv2->order) {
3053 visible_y_high = atv2->order;
3055 if (visible_y_low > atv2->order) {
3056 visible_y_low = atv2->order;
3059 if (!atv2->is_audio_track()) {
3060 tracks = tracks |= (0x01 << atv2->order);
3063 height_list[atv2->order] = (*i)->height;
3065 if ((children_list = atv2->get_child_list()).size() > 0) {
3066 for (list<TimeAxisView*>::iterator j = children_list.begin(); j != children_list.end(); ++j) {
3067 tracks = tracks |= (0x01 << (atv2->order + children));
3068 height_list[atv2->order + children] = (*j)->height;
3076 /* find the actual span according to the canvas */
3078 canvas_pointer_y_span = pointer_y_span;
3079 if (drag_info.last_trackview->order >= tv->order) {
3081 for (y = tv->order; y < drag_info.last_trackview->order; y++) {
3082 if (height_list[y] == 0 ) {
3083 canvas_pointer_y_span--;
3088 for (y = drag_info.last_trackview->order;y <= tv->order; y++) {
3089 if ( height_list[y] == 0 ) {
3090 canvas_pointer_y_span++;
3095 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3096 RegionView* rv2 = (*i);
3097 double ix1, ix2, iy1, iy2;
3100 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3101 rv2->get_canvas_group()->i2w (ix1, iy1);
3102 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3103 RouteTimeAxisView* atv2 = dynamic_cast<RouteTimeAxisView*>(tvp2);
3105 if (atv2->order != original_pointer_order) {
3106 /* this isn't the pointer track */
3108 if (canvas_pointer_y_span > 0) {
3110 /* moving up the canvas */
3111 if ((atv2->order - canvas_pointer_y_span) >= visible_y_low) {
3113 int32_t visible_tracks = 0;
3114 while (visible_tracks < canvas_pointer_y_span ) {
3117 while (height_list[atv2->order - (visible_tracks - n)] == 0) {
3118 /* we're passing through a hidden track */
3123 if (tracks[atv2->order - (canvas_pointer_y_span - n)] != 0x00) {
3124 clamp_y_axis = true;
3128 clamp_y_axis = true;
3131 } else if (canvas_pointer_y_span < 0) {
3133 /*moving down the canvas*/
3135 if ((atv2->order - (canvas_pointer_y_span - n)) <= visible_y_high) { // we will overflow
3138 int32_t visible_tracks = 0;
3140 while (visible_tracks > canvas_pointer_y_span ) {
3143 while (height_list[atv2->order - (visible_tracks - n)] == 0) {
3147 if ( tracks[atv2->order - ( canvas_pointer_y_span - n)] != 0x00) {
3148 clamp_y_axis = true;
3153 clamp_y_axis = true;
3159 /* this is the pointer's track */
3160 if ((atv2->order - pointer_y_span) > visible_y_high) { // we will overflow
3161 clamp_y_axis = true;
3162 } else if ((atv2->order - pointer_y_span) < visible_y_low) { // we will underflow
3163 clamp_y_axis = true;
3171 } else if (drag_info.last_trackview == tv) {
3172 clamp_y_axis = true;
3176 if (!clamp_y_axis) {
3177 drag_info.last_trackview = tv;
3180 /************************************************************
3182 ************************************************************/
3184 /* compute the amount of pointer motion in frames, and where
3185 the region would be if we moved it by that much.
3188 if (drag_info.move_threshold_passed) {
3190 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
3192 nframes_t sync_frame;
3193 nframes_t sync_offset;
3196 pending_region_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
3198 sync_offset = rv->region()->sync_offset (sync_dir);
3199 sync_frame = rv->region()->adjust_to_sync (pending_region_position);
3201 /* we snap if the snap modifier is not enabled.
3204 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3205 snap_to (sync_frame);
3208 if (sync_frame - sync_offset <= sync_frame) {
3209 pending_region_position = sync_frame - (sync_dir*sync_offset);
3211 pending_region_position = 0;
3215 pending_region_position = 0;
3218 if (pending_region_position > max_frames - rv->region()->length()) {
3219 pending_region_position = drag_info.last_frame_position;
3222 // printf ("3: pending_region_position= %lu %lu\n", pending_region_position, drag_info.last_frame_position );
3224 if (pending_region_position != drag_info.last_frame_position && !drag_info.x_constrained) {
3226 /* now compute the canvas unit distance we need to move the regiondrag_info.last_trackview->order
3227 to make it appear at the new location.
3230 if (pending_region_position > drag_info.last_frame_position) {
3231 x_delta = ((double) (pending_region_position - drag_info.last_frame_position) / frames_per_unit);
3233 x_delta = -((double) (drag_info.last_frame_position - pending_region_position) / frames_per_unit);
3236 drag_info.last_frame_position = pending_region_position;
3243 /* threshold not passed */
3248 /*************************************************************
3250 ************************************************************/
3252 if (x_delta == 0 && (pointer_y_span == 0)) {
3253 /* haven't reached next snap point, and we're not switching
3254 trackviews. nothing to do.
3261 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3263 RegionView* rv2 = (*i);
3265 // If any regionview is at zero, we need to know so we can stop further leftward motion.
3267 double ix1, ix2, iy1, iy2;
3268 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3269 rv2->get_canvas_group()->i2w (ix1, iy1);
3278 /*************************************************************
3280 ************************************************************/
3284 if (drag_info.first_move) {
3285 if (drag_info.move_threshold_passed) {
3296 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
3297 const list<RegionView*>& layered_regions = selection->regions.by_layer();
3299 for (list<RegionView*>::const_iterator i = layered_regions.begin(); i != layered_regions.end(); ++i) {
3301 RegionView* rv = (*i);
3302 double ix1, ix2, iy1, iy2;
3303 int32_t temp_pointer_y_span = pointer_y_span;
3305 /* get item BBox, which will be relative to parent. so we have
3306 to query on a child, then convert to world coordinates using
3310 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3311 rv->get_canvas_group()->i2w (ix1, iy1);
3312 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3313 AudioTimeAxisView* canvas_atv = dynamic_cast<AudioTimeAxisView*>(tvp2);
3314 AudioTimeAxisView* temp_atv;
3316 if ((pointer_y_span != 0) && !clamp_y_axis) {
3319 for (j = height_list.begin(); j!= height_list.end(); j++) {
3320 if (x == canvas_atv->order) {
3321 /* we found the track the region is on */
3322 if (x != original_pointer_order) {
3323 /*this isn't from the same track we're dragging from */
3324 temp_pointer_y_span = canvas_pointer_y_span;
3326 while (temp_pointer_y_span > 0) {
3327 /* we're moving up canvas-wise,
3328 so we need to find the next track height
3330 if (j != height_list.begin()) {
3333 if (x != original_pointer_order) {
3334 /* we're not from the dragged track, so ignore hidden tracks. */
3336 temp_pointer_y_span++;
3340 temp_pointer_y_span--;
3342 while (temp_pointer_y_span < 0) {
3344 if (x != original_pointer_order) {
3346 temp_pointer_y_span--;
3350 if (j != height_list.end()) {
3353 temp_pointer_y_span++;
3355 /* find out where we'll be when we move and set height accordingly */
3357 tvp2 = trackview_by_y_position (iy1 + y_delta);
3358 temp_atv = dynamic_cast<AudioTimeAxisView*>(tvp2);
3359 rv->set_height (temp_atv->height);
3361 /* if you un-comment the following, the region colours will follow the track colours whilst dragging,
3362 personally, i think this can confuse things, but never mind.
3365 //const GdkColor& col (temp_atv->view->get_region_color());
3366 //rv->set_color (const_cast<GdkColor&>(col));
3373 /* prevent the regionview from being moved to before
3374 the zero position on the canvas.
3379 if (-x_delta > ix1) {
3382 } else if ((x_delta > 0) && (rv->region()->last_frame() > max_frames - x_delta)) {
3383 x_delta = max_frames - rv->region()->last_frame();
3387 if (drag_info.first_move) {
3389 /* hide any dependent views */
3391 rv->get_time_axis_view().hide_dependent_views (*rv);
3393 /* this is subtle. raising the regionview itself won't help,
3394 because raise_to_top() just puts the item on the top of
3395 its parent's stack. so, we need to put the trackview canvas_display group
3396 on the top, since its parent is the whole canvas.
3399 rv->get_canvas_group()->raise_to_top();
3400 rv->get_time_axis_view().canvas_display->raise_to_top();
3401 cursor_group->raise_to_top();
3402 rv->fake_set_opaque (true);
3405 if (drag_info.brushing) {
3406 mouse_brush_insert_region (rv, pending_region_position);
3408 rv->move (x_delta, y_delta);
3411 } /* foreach region */
3415 if (drag_info.first_move && drag_info.move_threshold_passed) {
3416 cursor_group->raise_to_top();
3417 drag_info.first_move = false;
3420 if (x_delta != 0 && !drag_info.brushing) {
3421 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3426 Editor::region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3429 RegionView* rv = reinterpret_cast<RegionView *> (drag_info.data);
3430 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
3431 bool nocommit = true;
3433 RouteTimeAxisView* atv;
3434 bool regionview_y_movement;
3435 bool regionview_x_movement;
3436 vector<RegionView*> copies;
3438 /* first_move is set to false if the regionview has been moved in the
3442 if (drag_info.first_move) {
3449 /* The regionview has been moved at some stage during the grab so we need
3450 to account for any mouse movement between this event and the last one.
3453 region_drag_motion_callback (item, event);
3455 if (Config->get_edit_mode() == Splice && !pre_drag_region_selection.empty()) {
3456 selection->set (pre_drag_region_selection);
3457 pre_drag_region_selection.clear ();
3460 if (drag_info.brushing) {
3461 /* all changes were made during motion event handlers */
3463 if (drag_info.copy) {
3464 for (list<RegionView*>::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
3465 copies.push_back (*i);
3472 /* adjust for track speed */
3475 atv = dynamic_cast<AudioTimeAxisView*> (drag_info.last_trackview);
3476 if (atv && atv->get_diskstream()) {
3477 speed = atv->get_diskstream()->speed();
3480 regionview_x_movement = (drag_info.last_frame_position != (nframes_t) (rv->region()->position()/speed));
3481 regionview_y_movement = (drag_info.last_trackview != &rv->get_time_axis_view());
3483 //printf ("last_frame: %s position is %lu %g\n", rv->get_time_axis_view().name().c_str(), drag_info.last_frame_position, speed);
3484 //printf ("last_rackview: %s \n", drag_info.last_trackview->name().c_str());
3488 if (drag_info.copy) {
3489 if (drag_info.x_constrained) {
3490 op_string = _("fixed time region copy");
3492 op_string = _("region copy");
3495 if (drag_info.x_constrained) {
3496 op_string = _("fixed time region drag");
3498 op_string = _("region drag");
3502 begin_reversible_command (op_string);
3504 if (regionview_y_movement) {
3506 /* moved to a different audio track. */
3508 vector<RegionView*> new_selection;
3510 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ) {
3512 RegionView* rv = (*i);
3514 double ix1, ix2, iy1, iy2;
3516 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3517 rv->get_canvas_group()->i2w (ix1, iy1);
3518 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3519 AudioTimeAxisView* atv2 = dynamic_cast<AudioTimeAxisView*>(tvp2);
3521 boost::shared_ptr<Playlist> from_playlist = rv->region()->playlist();
3522 boost::shared_ptr<Playlist> to_playlist = atv2->playlist();
3524 where = (nframes_t) (unit_to_frame (ix1) * speed);
3525 boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region()));
3527 /* undo the previous hide_dependent_views so that xfades don't
3528 disappear on copying regions
3531 rv->get_time_axis_view().reveal_dependent_views (*rv);
3533 if (!drag_info.copy) {
3535 /* the region that used to be in the old playlist is not
3536 moved to the new one - we make a copy of it. as a result,
3537 any existing editor for the region should no longer be
3541 rv->hide_region_editor();
3542 rv->fake_set_opaque (false);
3544 session->add_command (new MementoCommand<Playlist>(*from_playlist, &from_playlist->get_state(), 0));
3545 from_playlist->remove_region ((rv->region()));
3546 session->add_command (new MementoCommand<Playlist>(*from_playlist, 0, &from_playlist->get_state()));
3550 /* the regionview we dragged around is a temporary copy, queue it for deletion */
3552 copies.push_back (rv);
3555 latest_regionviews.clear ();
3557 sigc::connection c = atv2->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3558 session->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
3559 to_playlist->add_region (new_region, where);
3560 session->add_command (new MementoCommand<Playlist>(*to_playlist, 0, &to_playlist->get_state()));
3563 if (!latest_regionviews.empty()) {
3564 new_selection.insert (new_selection.end(), latest_regionviews.begin(), latest_regionviews.end());
3567 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
3568 was selected in all of them, then removing it from the playlist will have removed all
3569 trace of it from the selection (i.e. there were N regions selected, we removed 1,
3570 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
3571 corresponding regionview, and the selection is now empty).
3573 this could have invalidated any and all iterators into the region selection.
3575 the heuristic we use here is: if the region selection is empty, break out of the loop
3576 here. if the region selection is not empty, then restart the loop because we know that
3577 we must have removed at least the region(view) we've just been working on as well as any
3578 that we processed on previous iterations.
3580 EXCEPT .... if we are doing a copy drag, then the selection hasn't been modified and
3581 we can just iterate.
3584 if (drag_info.copy) {
3587 if (selection->regions.empty()) {
3590 i = selection->regions.by_layer().begin();
3595 selection->set (new_selection);
3599 /* motion within a single track */
3601 list<RegionView*> regions = selection->regions.by_layer();
3603 if (drag_info.copy) {
3604 selection->clear_regions();
3607 for (list<RegionView*>::iterator i = regions.begin(); i != regions.end(); ++i) {
3611 if (rv->region()->locked()) {
3616 if (regionview_x_movement) {
3617 double ownspeed = 1.0;
3618 atv = dynamic_cast<AudioTimeAxisView*> (&(rv->get_time_axis_view()));
3620 if (atv && atv->get_diskstream()) {
3621 ownspeed = atv->get_diskstream()->speed();
3624 /* base the new region position on the current position of the regionview.*/
3626 double ix1, ix2, iy1, iy2;
3628 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3629 rv->get_canvas_group()->i2w (ix1, iy1);
3630 where = (nframes_t) (unit_to_frame (ix1) * ownspeed);
3634 where = rv->region()->position();
3637 boost::shared_ptr<Playlist> to_playlist = rv->region()->playlist();
3639 assert (to_playlist);
3643 session->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
3645 if (drag_info.copy) {
3647 boost::shared_ptr<Region> newregion;
3648 boost::shared_ptr<Region> ar;
3650 if ((ar = boost::dynamic_pointer_cast<AudioRegion>(rv->region())) != 0) {
3651 newregion = RegionFactory::create (ar);
3653 /* XXX MIDI HERE drobilla */
3659 latest_regionviews.clear ();
3660 sigc::connection c = atv->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3661 to_playlist->add_region (newregion, (nframes_t) (where * atv->get_diskstream()->speed()));
3664 if (!latest_regionviews.empty()) {
3665 // XXX why just the first one ? we only expect one
3666 atv->reveal_dependent_views (*latest_regionviews.front());
3667 selection->add (latest_regionviews);
3670 /* if the original region was locked, we don't care for the new one */
3672 newregion->set_locked (false);
3676 /* just change the model */
3678 rv->region()->set_position (where, (void*) this);
3684 session->add_command (new MementoCommand<Playlist>(*to_playlist, 0, &to_playlist->get_state()));
3686 if (drag_info.copy) {
3687 copies.push_back (rv);
3695 commit_reversible_command ();
3698 for (vector<RegionView*>::iterator x = copies.begin(); x != copies.end(); ++x) {
3704 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
3706 /* Either add to or set the set the region selection, unless
3707 this is an alignment click (control used)
3710 if (Keyboard::modifier_state_contains (event->state, Keyboard::Control)) {
3711 TimeAxisView* tv = &rv.get_time_axis_view();
3712 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*>(tv);
3714 if (atv && atv->is_audio_track()) {
3715 speed = atv->get_diskstream()->speed();
3718 nframes64_t where = get_preferred_edit_position();
3722 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Alt))) {
3724 align_region (rv.region(), SyncPoint, (nframes_t) (where * speed));
3726 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Shift))) {
3728 align_region (rv.region(), End, (nframes_t) (where * speed));
3732 align_region (rv.region(), Start, (nframes_t) (where * speed));
3739 Editor::show_verbose_time_cursor (nframes_t frame, double offset, double xpos, double ypos)
3745 nframes_t frame_rate;
3752 switch (Profile->get_small_screen() ? ARDOUR_UI::instance()->primary_clock.mode () : ARDOUR_UI::instance()->secondary_clock.mode ()) {
3753 case AudioClock::BBT:
3754 session->bbt_time (frame, bbt);
3755 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, bbt.bars, bbt.beats, bbt.ticks);
3758 case AudioClock::SMPTE:
3759 session->smpte_time (frame, smpte);
3760 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
3763 case AudioClock::MinSec:
3764 /* XXX this is copied from show_verbose_duration_cursor() */
3765 frame_rate = session->frame_rate();
3766 hours = frame / (frame_rate * 3600);
3767 frame = frame % (frame_rate * 3600);
3768 mins = frame / (frame_rate * 60);
3769 frame = frame % (frame_rate * 60);
3770 secs = (float) frame / (float) frame_rate;
3771 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
3775 snprintf (buf, sizeof(buf), "%u", frame);
3779 if (xpos >= 0 && ypos >=0) {
3780 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
3783 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
3785 show_verbose_canvas_cursor ();
3789 Editor::show_verbose_duration_cursor (nframes_t start, nframes_t end, double offset, double xpos, double ypos)
3796 nframes_t distance, frame_rate;
3798 Meter meter_at_start(session->tempo_map().meter_at(start));
3804 switch (ARDOUR_UI::instance()->secondary_clock.mode ()) {
3805 case AudioClock::BBT:
3806 session->bbt_time (start, sbbt);
3807 session->bbt_time (end, ebbt);
3810 /* XXX this computation won't work well if the
3811 user makes a selection that spans any meter changes.
3814 ebbt.bars -= sbbt.bars;
3815 if (ebbt.beats >= sbbt.beats) {
3816 ebbt.beats -= sbbt.beats;
3819 ebbt.beats = int(meter_at_start.beats_per_bar()) + ebbt.beats - sbbt.beats;
3821 if (ebbt.ticks >= sbbt.ticks) {
3822 ebbt.ticks -= sbbt.ticks;
3825 ebbt.ticks = int(Meter::ticks_per_beat) + ebbt.ticks - sbbt.ticks;
3828 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, ebbt.bars, ebbt.beats, ebbt.ticks);
3831 case AudioClock::SMPTE:
3832 session->smpte_duration (end - start, smpte);
3833 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
3836 case AudioClock::MinSec:
3837 /* XXX this stuff should be elsewhere.. */
3838 distance = end - start;
3839 frame_rate = session->frame_rate();
3840 hours = distance / (frame_rate * 3600);
3841 distance = distance % (frame_rate * 3600);
3842 mins = distance / (frame_rate * 60);
3843 distance = distance % (frame_rate * 60);
3844 secs = (float) distance / (float) frame_rate;
3845 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
3849 snprintf (buf, sizeof(buf), "%u", end - start);
3853 if (xpos >= 0 && ypos >=0) {
3854 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
3857 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
3859 show_verbose_canvas_cursor ();
3863 Editor::collect_new_region_view (RegionView* rv)
3865 latest_regionviews.push_back (rv);
3869 Editor::start_selection_grab (ArdourCanvas::Item* item, GdkEvent* event)
3871 if (clicked_regionview == 0) {
3875 /* lets try to create new Region for the selection */
3877 vector<boost::shared_ptr<AudioRegion> > new_regions;
3878 create_region_from_selection (new_regions);
3880 if (new_regions.empty()) {
3884 /* XXX fix me one day to use all new regions */
3886 boost::shared_ptr<Region> region (new_regions.front());
3888 /* add it to the current stream/playlist.
3890 tricky: the streamview for the track will add a new regionview. we will
3891 catch the signal it sends when it creates the regionview to
3892 set the regionview we want to then drag.
3895 latest_regionviews.clear();
3896 sigc::connection c = clicked_audio_trackview->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3898 /* A selection grab currently creates two undo/redo operations, one for
3899 creating the new region and another for moving it.
3902 begin_reversible_command (_("selection grab"));
3904 boost::shared_ptr<Playlist> playlist = clicked_trackview->playlist();
3906 XMLNode *before = &(playlist->get_state());
3907 clicked_trackview->playlist()->add_region (region, selection->time[clicked_selection].start);
3908 XMLNode *after = &(playlist->get_state());
3909 session->add_command(new MementoCommand<Playlist>(*playlist, before, after));
3911 commit_reversible_command ();
3915 if (latest_regionviews.empty()) {
3916 /* something went wrong */
3920 /* we need to deselect all other regionviews, and select this one
3921 i'm ignoring undo stuff, because the region creation will take care of it
3923 selection->set (latest_regionviews);
3925 drag_info.item = latest_regionviews.front()->get_canvas_group();
3926 drag_info.data = latest_regionviews.front();
3927 drag_info.motion_callback = &Editor::region_drag_motion_callback;
3928 drag_info.finished_callback = &Editor::region_drag_finished_callback;
3932 drag_info.last_trackview = clicked_trackview;
3933 drag_info.last_frame_position = latest_regionviews.front()->region()->position();
3934 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
3936 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3940 Editor::cancel_selection ()
3942 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
3943 (*i)->hide_selection ();
3945 begin_reversible_command (_("cancel selection"));
3946 selection->clear ();
3947 clicked_selection = 0;
3948 commit_reversible_command ();
3952 Editor::start_selection_op (ArdourCanvas::Item* item, GdkEvent* event, SelectionOp op)
3954 nframes_t start = 0;
3961 drag_info.item = item;
3962 drag_info.motion_callback = &Editor::drag_selection;
3963 drag_info.finished_callback = &Editor::end_selection_op;
3968 case CreateSelection:
3969 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
3970 drag_info.copy = true;
3972 drag_info.copy = false;
3974 start_grab (event, selector_cursor);
3977 case SelectionStartTrim:
3978 if (clicked_trackview) {
3979 clicked_trackview->order_selection_trims (item, true);
3981 start_grab (event, trimmer_cursor);
3982 start = selection->time[clicked_selection].start;
3983 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
3986 case SelectionEndTrim:
3987 if (clicked_trackview) {
3988 clicked_trackview->order_selection_trims (item, false);
3990 start_grab (event, trimmer_cursor);
3991 end = selection->time[clicked_selection].end;
3992 drag_info.pointer_frame_offset = drag_info.grab_frame - end;
3996 start = selection->time[clicked_selection].start;
3998 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
4002 if (selection_op == SelectionMove) {
4003 show_verbose_time_cursor(start, 10);
4005 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4010 Editor::drag_selection (ArdourCanvas::Item* item, GdkEvent* event)
4012 nframes_t start = 0;
4015 nframes_t pending_position;
4017 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
4018 pending_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
4020 pending_position = 0;
4023 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4024 snap_to (pending_position);
4027 /* only alter selection if the current frame is
4028 different from the last frame position (adjusted)
4031 if (pending_position == drag_info.last_pointer_frame) return;
4033 switch (selection_op) {
4034 case CreateSelection:
4036 if (drag_info.first_move) {
4037 snap_to (drag_info.grab_frame);
4040 if (pending_position < drag_info.grab_frame) {
4041 start = pending_position;
4042 end = drag_info.grab_frame;
4044 end = pending_position;
4045 start = drag_info.grab_frame;
4048 /* first drag: Either add to the selection
4049 or create a new selection->
4052 if (drag_info.first_move) {
4054 begin_reversible_command (_("range selection"));
4056 if (drag_info.copy) {
4057 /* adding to the selection */
4058 clicked_selection = selection->add (start, end);
4059 drag_info.copy = false;
4061 /* new selection-> */
4062 clicked_selection = selection->set (clicked_trackview, start, end);
4067 case SelectionStartTrim:
4069 if (drag_info.first_move) {
4070 begin_reversible_command (_("trim selection start"));
4073 start = selection->time[clicked_selection].start;
4074 end = selection->time[clicked_selection].end;
4076 if (pending_position > end) {
4079 start = pending_position;
4083 case SelectionEndTrim:
4085 if (drag_info.first_move) {
4086 begin_reversible_command (_("trim selection end"));
4089 start = selection->time[clicked_selection].start;
4090 end = selection->time[clicked_selection].end;
4092 if (pending_position < start) {
4095 end = pending_position;
4102 if (drag_info.first_move) {
4103 begin_reversible_command (_("move selection"));
4106 start = selection->time[clicked_selection].start;
4107 end = selection->time[clicked_selection].end;
4109 length = end - start;
4111 start = pending_position;
4114 end = start + length;
4119 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
4120 start_canvas_autoscroll (1);
4124 selection->replace (clicked_selection, start, end);
4127 drag_info.last_pointer_frame = pending_position;
4128 drag_info.first_move = false;
4130 if (selection_op == SelectionMove) {
4131 show_verbose_time_cursor(start, 10);
4133 show_verbose_time_cursor(pending_position, 10);
4138 Editor::end_selection_op (ArdourCanvas::Item* item, GdkEvent* event)
4140 if (!drag_info.first_move) {
4141 drag_selection (item, event);
4142 /* XXX this is not object-oriented programming at all. ick */
4143 if (selection->time.consolidate()) {
4144 selection->TimeChanged ();
4146 commit_reversible_command ();
4148 /* just a click, no pointer movement.*/
4150 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
4152 selection->clear_time();
4157 /* XXX what happens if its a music selection? */
4158 session->set_audio_range (selection->time);
4159 stop_canvas_autoscroll ();
4163 Editor::start_trim (ArdourCanvas::Item* item, GdkEvent* event)
4166 TimeAxisView* tvp = clicked_trackview;
4167 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
4169 if (tv && tv->is_audio_track()) {
4170 speed = tv->get_diskstream()->speed();
4173 nframes_t region_start = (nframes_t) (clicked_regionview->region()->position() / speed);
4174 nframes_t region_end = (nframes_t) (clicked_regionview->region()->last_frame() / speed);
4175 nframes_t region_length = (nframes_t) (clicked_regionview->region()->length() / speed);
4177 //drag_info.item = clicked_regionview->get_name_highlight();
4178 drag_info.item = item;
4179 drag_info.motion_callback = &Editor::trim_motion_callback;
4180 drag_info.finished_callback = &Editor::trim_finished_callback;
4182 start_grab (event, trimmer_cursor);
4184 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
4185 trim_op = ContentsTrim;
4187 /* These will get overridden for a point trim.*/
4188 if (drag_info.current_pointer_frame < (region_start + region_length/2)) {
4189 /* closer to start */
4190 trim_op = StartTrim;
4191 } else if (drag_info.current_pointer_frame > (region_end - region_length/2)) {
4199 show_verbose_time_cursor(region_start, 10);
4202 show_verbose_time_cursor(region_end, 10);
4205 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4211 Editor::trim_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
4213 RegionView* rv = clicked_regionview;
4214 nframes_t frame_delta = 0;
4215 bool left_direction;
4216 bool obey_snap = !Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier());
4218 /* snap modifier works differently here..
4219 its' current state has to be passed to the
4220 various trim functions in order to work properly
4224 TimeAxisView* tvp = clicked_trackview;
4225 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4226 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
4228 if (tv && tv->is_audio_track()) {
4229 speed = tv->get_diskstream()->speed();
4232 if (drag_info.last_pointer_frame > drag_info.current_pointer_frame) {
4233 left_direction = true;
4235 left_direction = false;
4239 snap_to (drag_info.current_pointer_frame);
4242 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
4246 if (drag_info.first_move) {
4252 trim_type = "Region start trim";
4255 trim_type = "Region end trim";
4258 trim_type = "Region content trim";
4262 begin_reversible_command (trim_type);
4264 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4265 (*i)->fake_set_opaque(false);
4266 (*i)->region()->freeze ();
4268 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
4270 arv->temporarily_hide_envelope ();
4272 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4273 insert_result = motion_frozen_playlists.insert (pl);
4274 if (insert_result.second) {
4275 session->add_command(new MementoCommand<Playlist>(*pl, &pl->get_state(), 0));
4280 if (left_direction) {
4281 frame_delta = (drag_info.last_pointer_frame - drag_info.current_pointer_frame);
4283 frame_delta = (drag_info.current_pointer_frame - drag_info.last_pointer_frame);
4288 if ((left_direction == false) && (drag_info.current_pointer_frame <= rv->region()->first_frame()/speed)) {
4291 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4292 single_start_trim (**i, frame_delta, left_direction, obey_snap);
4298 if ((left_direction == true) && (drag_info.current_pointer_frame > (nframes_t) (rv->region()->last_frame()/speed))) {
4301 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4302 single_end_trim (**i, frame_delta, left_direction, obey_snap);
4309 bool swap_direction = false;
4311 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
4312 swap_direction = true;
4315 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4316 i != selection->regions.by_layer().end(); ++i)
4318 single_contents_trim (**i, frame_delta, left_direction, swap_direction, obey_snap);
4326 show_verbose_time_cursor((nframes_t) (rv->region()->position()/speed), 10);
4329 show_verbose_time_cursor((nframes_t) (rv->region()->last_frame()/speed), 10);
4332 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4336 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4337 drag_info.first_move = false;
4341 Editor::single_contents_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool swap_direction, bool obey_snap)
4343 boost::shared_ptr<Region> region (rv.region());
4345 if (region->locked()) {
4349 nframes_t new_bound;
4352 TimeAxisView* tvp = clicked_trackview;
4353 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4355 if (tv && tv->is_audio_track()) {
4356 speed = tv->get_diskstream()->speed();
4359 if (left_direction) {
4360 if (swap_direction) {
4361 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4363 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4366 if (swap_direction) {
4367 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4369 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4374 snap_to (new_bound);
4376 region->trim_start ((nframes_t) (new_bound * speed), this);
4377 rv.region_changed (StartChanged);
4381 Editor::single_start_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool obey_snap)
4383 boost::shared_ptr<Region> region (rv.region());
4385 if (region->locked()) {
4389 nframes_t new_bound;
4392 TimeAxisView* tvp = clicked_trackview;
4393 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
4395 if (tv && tv->is_audio_track()) {
4396 speed = tv->get_diskstream()->speed();
4399 if (left_direction) {
4400 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4402 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4406 snap_to (new_bound, (left_direction ? 0 : 1));
4409 region->trim_front ((nframes_t) (new_bound * speed), this);
4411 rv.region_changed (Change (LengthChanged|PositionChanged|StartChanged));
4415 Editor::single_end_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool obey_snap)
4417 boost::shared_ptr<Region> region (rv.region());
4419 if (region->locked()) {
4423 nframes_t new_bound;
4426 TimeAxisView* tvp = clicked_trackview;
4427 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
4429 if (tv && tv->is_audio_track()) {
4430 speed = tv->get_diskstream()->speed();
4433 if (left_direction) {
4434 new_bound = (nframes_t) ((region->last_frame() + 1)/speed) - frame_delta;
4436 new_bound = (nframes_t) ((region->last_frame() + 1)/speed) + frame_delta;
4440 snap_to (new_bound);
4442 region->trim_end ((nframes_t) (new_bound * speed), this);
4443 rv.region_changed (LengthChanged);
4447 Editor::trim_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
4449 if (!drag_info.first_move) {
4450 trim_motion_callback (item, event);
4452 if (!selection->selected (clicked_regionview)) {
4453 thaw_region_after_trim (*clicked_regionview);
4456 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4457 i != selection->regions.by_layer().end(); ++i)
4459 thaw_region_after_trim (**i);
4460 (*i)->fake_set_opaque (true);
4464 for (set<boost::shared_ptr<Playlist> >::iterator p = motion_frozen_playlists.begin(); p != motion_frozen_playlists.end(); ++p) {
4466 session->add_command (new MementoCommand<Playlist>(*(*p).get(), 0, &(*p)->get_state()));
4469 motion_frozen_playlists.clear ();
4471 commit_reversible_command();
4473 /* no mouse movement */
4479 Editor::point_trim (GdkEvent* event)
4481 RegionView* rv = clicked_regionview;
4482 nframes_t new_bound = drag_info.current_pointer_frame;
4484 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4485 snap_to (new_bound);
4488 /* Choose action dependant on which button was pressed */
4489 switch (event->button.button) {
4491 trim_op = StartTrim;
4492 begin_reversible_command (_("Start point trim"));
4494 if (selection->selected (rv)) {
4496 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4497 i != selection->regions.by_layer().end(); ++i)
4499 if (!(*i)->region()->locked()) {
4500 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4501 XMLNode &before = pl->get_state();
4502 (*i)->region()->trim_front (new_bound, this);
4503 XMLNode &after = pl->get_state();
4504 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4510 if (!rv->region()->locked()) {
4511 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
4512 XMLNode &before = pl->get_state();
4513 rv->region()->trim_front (new_bound, this);
4514 XMLNode &after = pl->get_state();
4515 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4519 commit_reversible_command();
4524 begin_reversible_command (_("End point trim"));
4526 if (selection->selected (rv)) {
4528 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
4530 if (!(*i)->region()->locked()) {
4531 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4532 XMLNode &before = pl->get_state();
4533 (*i)->region()->trim_end (new_bound, this);
4534 XMLNode &after = pl->get_state();
4535 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4541 if (!rv->region()->locked()) {
4542 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
4543 XMLNode &before = pl->get_state();
4544 rv->region()->trim_end (new_bound, this);
4545 XMLNode &after = pl->get_state();
4546 session->add_command (new MementoCommand<Playlist>(*pl.get(), &before, &after));
4550 commit_reversible_command();
4559 Editor::thaw_region_after_trim (RegionView& rv)
4561 boost::shared_ptr<Region> region (rv.region());
4563 if (region->locked()) {
4567 region->thaw (_("trimmed region"));
4568 XMLNode &after = region->playlist()->get_state();
4569 session->add_command (new MementoCommand<Playlist>(*(region->playlist()), 0, &after));
4571 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(&rv);
4573 arv->unhide_envelope ();
4577 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* event)
4582 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
4583 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
4587 Location* location = find_location_from_marker (marker, is_start);
4588 location->set_hidden (true, this);
4593 Editor::start_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event, RangeMarkerOp op)
4599 drag_info.item = item;
4600 drag_info.motion_callback = &Editor::drag_range_markerbar_op;
4601 drag_info.finished_callback = &Editor::end_range_markerbar_op;
4603 range_marker_op = op;
4605 if (!temp_location) {
4606 temp_location = new Location;
4610 case CreateRangeMarker:
4611 case CreateTransportMarker:
4613 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
4614 drag_info.copy = true;
4616 drag_info.copy = false;
4618 start_grab (event, selector_cursor);
4622 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4627 Editor::drag_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4629 nframes_t start = 0;
4631 ArdourCanvas::SimpleRect *crect = (range_marker_op == CreateRangeMarker) ? range_bar_drag_rect: transport_bar_drag_rect;
4633 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4634 snap_to (drag_info.current_pointer_frame);
4637 /* only alter selection if the current frame is
4638 different from the last frame position.
4641 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4643 switch (range_marker_op) {
4644 case CreateRangeMarker:
4645 case CreateTransportMarker:
4646 if (drag_info.first_move) {
4647 snap_to (drag_info.grab_frame);
4650 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4651 start = drag_info.current_pointer_frame;
4652 end = drag_info.grab_frame;
4654 end = drag_info.current_pointer_frame;
4655 start = drag_info.grab_frame;
4658 /* first drag: Either add to the selection
4659 or create a new selection.
4662 if (drag_info.first_move) {
4664 temp_location->set (start, end);
4668 update_marker_drag_item (temp_location);
4669 range_marker_drag_rect->show();
4670 range_marker_drag_rect->raise_to_top();
4676 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
4677 start_canvas_autoscroll (1);
4681 temp_location->set (start, end);
4683 double x1 = frame_to_pixel (start);
4684 double x2 = frame_to_pixel (end);
4685 crect->property_x1() = x1;
4686 crect->property_x2() = x2;
4688 update_marker_drag_item (temp_location);
4691 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4692 drag_info.first_move = false;
4694 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4699 Editor::end_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4701 Location * newloc = 0;
4704 if (!drag_info.first_move) {
4705 drag_range_markerbar_op (item, event);
4707 switch (range_marker_op) {
4708 case CreateRangeMarker:
4710 begin_reversible_command (_("new range marker"));
4711 XMLNode &before = session->locations()->get_state();
4712 session->locations()->next_available_name(rangename,"unnamed");
4713 newloc = new Location(temp_location->start(), temp_location->end(), rangename, Location::IsRangeMarker);
4714 session->locations()->add (newloc, true);
4715 XMLNode &after = session->locations()->get_state();
4716 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
4717 commit_reversible_command ();
4719 range_bar_drag_rect->hide();
4720 range_marker_drag_rect->hide();
4724 case CreateTransportMarker:
4725 // popup menu to pick loop or punch
4726 new_transport_marker_context_menu (&event->button, item);
4731 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
4733 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
4738 start = session->locations()->first_mark_before (drag_info.grab_frame);
4739 end = session->locations()->first_mark_after (drag_info.grab_frame);
4741 if (end == max_frames) {
4742 end = session->current_end_frame ();
4746 start = session->current_start_frame ();
4749 switch (mouse_mode) {
4751 /* find the two markers on either side and then make the selection from it */
4752 select_all_within (start, end, 0.0f, FLT_MAX, track_views, Selection::Set);
4756 /* find the two markers on either side of the click and make the range out of it */
4757 selection->set (0, start, end);
4766 stop_canvas_autoscroll ();
4772 Editor::start_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4774 drag_info.item = item;
4775 drag_info.motion_callback = &Editor::drag_mouse_zoom;
4776 drag_info.finished_callback = &Editor::end_mouse_zoom;
4778 start_grab (event, zoom_cursor);
4780 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4784 Editor::drag_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4789 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4790 snap_to (drag_info.current_pointer_frame);
4792 if (drag_info.first_move) {
4793 snap_to (drag_info.grab_frame);
4797 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4799 /* base start and end on initial click position */
4800 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4801 start = drag_info.current_pointer_frame;
4802 end = drag_info.grab_frame;
4804 end = drag_info.current_pointer_frame;
4805 start = drag_info.grab_frame;
4810 if (drag_info.first_move) {
4812 zoom_rect->raise_to_top();
4815 reposition_zoom_rect(start, end);
4817 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4818 drag_info.first_move = false;
4820 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4825 Editor::end_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4827 if (!drag_info.first_move) {
4828 drag_mouse_zoom (item, event);
4830 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
4831 temporal_zoom_by_frame (drag_info.grab_frame, drag_info.last_pointer_frame, "mouse zoom");
4833 temporal_zoom_by_frame (drag_info.last_pointer_frame, drag_info.grab_frame, "mouse zoom");
4836 temporal_zoom_to_frame (false, drag_info.grab_frame);
4838 temporal_zoom_step (false);
4839 center_screen (drag_info.grab_frame);
4847 Editor::reposition_zoom_rect (nframes_t start, nframes_t end)
4849 double x1 = frame_to_pixel (start);
4850 double x2 = frame_to_pixel (end);
4851 double y2 = full_canvas_height - 1.0;
4853 zoom_rect->property_x1() = x1;
4854 zoom_rect->property_y1() = 1.0;
4855 zoom_rect->property_x2() = x2;
4856 zoom_rect->property_y2() = y2;
4860 Editor::start_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4862 drag_info.item = item;
4863 drag_info.motion_callback = &Editor::drag_rubberband_select;
4864 drag_info.finished_callback = &Editor::end_rubberband_select;
4866 start_grab (event, cross_hair_cursor);
4868 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4872 Editor::drag_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4879 /* use a bigger drag threshold than the default */
4881 if (abs ((int) (drag_info.current_pointer_frame - drag_info.grab_frame)) < 8) {
4885 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4886 if (drag_info.first_move) {
4887 snap_to (drag_info.grab_frame);
4889 snap_to (drag_info.current_pointer_frame);
4892 /* base start and end on initial click position */
4894 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4895 start = drag_info.current_pointer_frame;
4896 end = drag_info.grab_frame;
4898 end = drag_info.current_pointer_frame;
4899 start = drag_info.grab_frame;
4902 if (drag_info.current_pointer_y < drag_info.grab_y) {
4903 y1 = drag_info.current_pointer_y;
4904 y2 = drag_info.grab_y;
4906 y2 = drag_info.current_pointer_y;
4907 y1 = drag_info.grab_y;
4911 if (start != end || y1 != y2) {
4913 double x1 = frame_to_pixel (start);
4914 double x2 = frame_to_pixel (end);
4916 rubberband_rect->property_x1() = x1;
4917 rubberband_rect->property_y1() = y1;
4918 rubberband_rect->property_x2() = x2;
4919 rubberband_rect->property_y2() = y2;
4921 rubberband_rect->show();
4922 rubberband_rect->raise_to_top();
4924 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4925 drag_info.first_move = false;
4927 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4932 Editor::end_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4934 if (!drag_info.first_move) {
4936 drag_rubberband_select (item, event);
4939 if (drag_info.current_pointer_y < drag_info.grab_y) {
4940 y1 = drag_info.current_pointer_y;
4941 y2 = drag_info.grab_y;
4944 y2 = drag_info.current_pointer_y;
4945 y1 = drag_info.grab_y;
4949 Selection::Operation op = Keyboard::selection_type (event->button.state);
4952 begin_reversible_command (_("rubberband selection"));
4954 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
4955 commit = select_all_within (drag_info.grab_frame, drag_info.last_pointer_frame, y1, y2, track_views, op);
4957 commit = select_all_within (drag_info.last_pointer_frame, drag_info.grab_frame, y1, y2, track_views, op);
4961 commit_reversible_command ();
4965 selection->clear_tracks();
4966 selection->clear_regions();
4967 selection->clear_points ();
4968 selection->clear_lines ();
4971 rubberband_rect->hide();
4976 Editor::mouse_rename_region (ArdourCanvas::Item* item, GdkEvent* event)
4978 using namespace Gtkmm2ext;
4980 ArdourPrompter prompter (false);
4982 prompter.set_prompt (_("Name for region:"));
4983 prompter.set_initial_text (clicked_regionview->region()->name());
4984 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
4985 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
4986 prompter.show_all ();
4987 switch (prompter.run ()) {
4988 case Gtk::RESPONSE_ACCEPT:
4990 prompter.get_result(str);
4992 clicked_regionview->region()->set_name (str);
5000 Editor::start_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
5002 drag_info.item = item;
5003 drag_info.motion_callback = &Editor::time_fx_motion;
5004 drag_info.finished_callback = &Editor::end_time_fx;
5008 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5012 Editor::time_fx_motion (ArdourCanvas::Item *item, GdkEvent* event)
5014 RegionView* rv = clicked_regionview;
5016 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
5017 snap_to (drag_info.current_pointer_frame);
5020 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
5024 if (drag_info.current_pointer_frame > rv->region()->position()) {
5025 rv->get_time_axis_view().show_timestretch (rv->region()->position(), drag_info.current_pointer_frame);
5028 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
5029 drag_info.first_move = false;
5031 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5035 Editor::end_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
5037 clicked_regionview->get_time_axis_view().hide_timestretch ();
5039 if (drag_info.first_move) {
5043 if (drag_info.last_pointer_frame < clicked_regionview->region()->position()) {
5044 /* backwards drag of the left edge - not usable */
5048 nframes_t newlen = drag_info.last_pointer_frame - clicked_regionview->region()->position();
5049 #ifdef USE_RUBBERBAND
5050 float percentage = (float) ((double) newlen / (double) clicked_regionview->region()->length());
5052 float percentage = (float) ((double) newlen - (double) clicked_regionview->region()->length()) / ((double) newlen) * 100.0f;
5055 begin_reversible_command (_("timestretch"));
5057 if (run_timestretch (selection->regions, percentage) == 0) {
5058 session->commit_reversible_command ();
5063 Editor::mouse_brush_insert_region (RegionView* rv, nframes_t pos)
5065 /* no brushing without a useful snap setting */
5068 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
5071 switch (snap_mode) {
5073 return; /* can't work because it allows region to be placed anywhere */
5078 switch (snap_type) {
5086 /* don't brush a copy over the original */
5088 if (pos == rv->region()->position()) {
5092 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*>(&arv->get_time_axis_view());
5094 if (atv == 0 || !atv->is_audio_track()) {
5098 boost::shared_ptr<Playlist> playlist = atv->playlist();
5099 double speed = atv->get_diskstream()->speed();
5101 XMLNode &before = playlist->get_state();
5102 playlist->add_region (boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (arv->audio_region())), (nframes_t) (pos * speed));
5103 XMLNode &after = playlist->get_state();
5104 session->add_command(new MementoCommand<Playlist>(*playlist.get(), &before, &after));
5106 // playlist is frozen, so we have to update manually
5108 playlist->Modified(); /* EMIT SIGNAL */
5112 Editor::track_height_step_timeout ()
5115 struct timeval delta;
5117 gettimeofday (&now, 0);
5118 timersub (&now, &last_track_height_step_timestamp, &delta);
5120 if (delta.tv_sec * 1000000 + delta.tv_usec > 250000) { /* milliseconds */
5121 current_stepping_trackview = 0;