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)
77 Gdk::ModifierType mask;
78 Glib::RefPtr<Gdk::Window> canvas_window = track_canvas.get_window();
79 Glib::RefPtr<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)
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)
333 /* in object/audition/timefx mode, any button press sets
334 the selection if the object can be selected. this is a
335 bit of hack, because we want to avoid this if the
336 mouse operation is a region alignment.
338 note: not dbl-click or triple-click
341 if (((mouse_mode != MouseObject) &&
342 (mouse_mode != MouseAudition || item_type != RegionItem) &&
343 (mouse_mode != MouseTimeFX || item_type != RegionItem) &&
344 (mouse_mode != MouseRange)) ||
346 (event->type != GDK_BUTTON_PRESS && event->type != GDK_BUTTON_RELEASE || event->button.button > 3)) {
351 if (event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) {
353 if ((event->button.state & Keyboard::RelevantModifierKeyMask) && event->button.button != 1) {
355 /* almost no selection action on modified button-2 or button-3 events */
357 if (item_type != RegionItem && event->button.button != 2) {
363 Selection::Operation op = Keyboard::selection_type (event->button.state);
364 bool press = (event->type == GDK_BUTTON_PRESS);
366 // begin_reversible_command (_("select on click"));
370 if (mouse_mode != MouseRange) {
371 commit = set_selected_regionview_from_click (press, op, true);
372 } else if (event->type == GDK_BUTTON_PRESS) {
373 commit = set_selected_track_from_click (press, op, false);
377 case RegionViewNameHighlight:
379 if (mouse_mode != MouseRange) {
380 commit = set_selected_regionview_from_click (press, op, true);
381 } else if (event->type == GDK_BUTTON_PRESS) {
382 commit = set_selected_track_from_click (press, op, false);
386 case FadeInHandleItem:
388 case FadeOutHandleItem:
390 if (mouse_mode != MouseRange) {
391 commit = set_selected_regionview_from_click (press, op, true);
392 } else if (event->type == GDK_BUTTON_PRESS) {
393 commit = set_selected_track_from_click (press, op, false);
397 case GainAutomationControlPointItem:
398 case PanAutomationControlPointItem:
399 case RedirectAutomationControlPointItem:
400 commit = set_selected_track_from_click (press, op, true);
401 if (mouse_mode != MouseRange) {
402 commit |= set_selected_control_point_from_click (op, false);
407 /* for context click or range selection, select track */
408 if (event->button.button == 3) {
409 commit = set_selected_track_from_click (press, op, true);
410 } else if (event->type == GDK_BUTTON_PRESS && mouse_mode == MouseRange) {
411 commit = set_selected_track_from_click (press, op, false);
415 case AutomationTrackItem:
416 commit = set_selected_track_from_click (press, op, true);
424 // commit_reversible_command ();
428 const static double ZERO_GAIN_FRACTION = gain_to_slider_position(dB_to_coefficient(0.0));
431 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
433 track_canvas.grab_focus();
435 if (session && session->actively_recording()) {
439 button_selection (item, event, item_type);
441 if (drag_info.item == 0 &&
442 (Keyboard::is_delete_event (&event->button) ||
443 Keyboard::is_context_menu_event (&event->button) ||
444 Keyboard::is_edit_event (&event->button))) {
446 /* handled by button release */
450 switch (event->button.button) {
453 if (event->type == GDK_BUTTON_PRESS) {
455 if (drag_info.item) {
456 drag_info.item->ungrab (event->button.time);
459 /* single mouse clicks on any of these item types operate
460 independent of mouse mode, mostly because they are
461 not on the main track canvas or because we want
467 case PlayheadCursorItem:
468 start_cursor_grab (item, event);
472 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask(Keyboard::Control|Keyboard::Shift))) {
473 hide_marker (item, event);
475 start_marker_grab (item, event);
479 case TempoMarkerItem:
480 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
481 start_tempo_marker_copy_grab (item, event);
483 start_tempo_marker_grab (item, event);
487 case MeterMarkerItem:
488 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
489 start_meter_marker_copy_grab (item, event);
491 start_meter_marker_grab (item, event);
501 case RangeMarkerBarItem:
502 start_range_markerbar_op (item, event, CreateRangeMarker);
506 case TransportMarkerBarItem:
507 start_range_markerbar_op (item, event, CreateTransportMarker);
516 switch (mouse_mode) {
519 case StartSelectionTrimItem:
520 start_selection_op (item, event, SelectionStartTrim);
523 case EndSelectionTrimItem:
524 start_selection_op (item, event, SelectionEndTrim);
528 if (Keyboard::modifier_state_contains
529 (event->button.state, Keyboard::ModifierMask(Keyboard::Alt))) {
530 // contains and not equals because I can't use alt as a modifier alone.
531 start_selection_grab (item, event);
532 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
533 /* grab selection for moving */
534 start_selection_op (item, event, SelectionMove);
537 /* this was debated, but decided the more common action was to
538 make a new selection */
539 start_selection_op (item, event, CreateSelection);
544 start_selection_op (item, event, CreateSelection);
550 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::Control|Keyboard::Alt)) &&
551 event->type == GDK_BUTTON_PRESS) {
553 start_rubberband_select (item, event);
555 } else if (event->type == GDK_BUTTON_PRESS) {
558 case FadeInHandleItem:
559 start_fade_in_grab (item, event);
562 case FadeOutHandleItem:
563 start_fade_out_grab (item, event);
567 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
568 start_region_copy_grab (item, event);
569 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
570 start_region_brush_grab (item, event);
572 start_region_grab (item, event);
576 case RegionViewNameHighlight:
577 start_trim (item, event);
582 /* rename happens on edit clicks */
583 start_trim (clicked_regionview->get_name_highlight(), event);
587 case GainAutomationControlPointItem:
588 case PanAutomationControlPointItem:
589 case RedirectAutomationControlPointItem:
590 start_control_point_grab (item, event);
594 case GainAutomationLineItem:
595 case PanAutomationLineItem:
596 case RedirectAutomationLineItem:
597 start_line_grab_from_line (item, event);
602 case AutomationTrackItem:
603 start_rubberband_select (item, event);
606 /* <CMT Additions> */
607 case ImageFrameHandleStartItem:
608 imageframe_start_handle_op(item, event) ;
611 case ImageFrameHandleEndItem:
612 imageframe_end_handle_op(item, event) ;
615 case MarkerViewHandleStartItem:
616 markerview_item_start_handle_op(item, event) ;
619 case MarkerViewHandleEndItem:
620 markerview_item_end_handle_op(item, event) ;
623 /* </CMT Additions> */
625 /* <CMT Additions> */
627 start_markerview_grab(item, event) ;
630 start_imageframe_grab(item, event) ;
632 /* </CMT Additions> */
648 // start_line_grab_from_regionview (item, event);
651 case GainControlPointItem:
652 start_control_point_grab (item, event);
656 start_line_grab_from_line (item, event);
659 case GainAutomationControlPointItem:
660 case PanAutomationControlPointItem:
661 case RedirectAutomationControlPointItem:
662 start_control_point_grab (item, event);
673 case GainAutomationControlPointItem:
674 case PanAutomationControlPointItem:
675 case RedirectAutomationControlPointItem:
676 start_control_point_grab (item, event);
679 case GainAutomationLineItem:
680 case PanAutomationLineItem:
681 case RedirectAutomationLineItem:
682 start_line_grab_from_line (item, event);
686 // XXX need automation mode to identify which
688 // start_line_grab_from_regionview (item, event);
698 if (event->type == GDK_BUTTON_PRESS) {
699 start_mouse_zoom (item, event);
706 if (item_type == RegionItem) {
707 start_time_fx (item, event);
713 last_scrub_x = event->button.x;
714 scrubbing_direction = 0;
715 /* rest handled in motion & release */
724 switch (mouse_mode) {
726 if (event->type == GDK_BUTTON_PRESS) {
729 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
730 start_region_copy_grab (item, event);
732 start_region_grab (item, event);
736 case GainAutomationControlPointItem:
737 case PanAutomationControlPointItem:
738 case RedirectAutomationControlPointItem:
739 start_control_point_grab (item, event);
750 case RegionViewNameHighlight:
751 start_trim (item, event);
756 start_trim (clicked_regionview->get_name_highlight(), event);
767 if (event->type == GDK_BUTTON_PRESS) {
768 /* relax till release */
775 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
776 temporal_zoom_session();
778 temporal_zoom_to_frame (true, event_frame(event));
801 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
803 nframes_t where = event_frame (event, 0, 0);
805 /* no action if we're recording */
807 if (session && session->actively_recording()) {
811 /* first, see if we're finishing a drag ... */
813 if (drag_info.item) {
814 if (end_grab (item, event)) {
815 /* grab dragged, so do nothing else */
820 button_selection (item, event, item_type);
822 /* edit events get handled here */
824 if (drag_info.item == 0 && Keyboard::is_edit_event (&event->button)) {
830 case TempoMarkerItem:
831 edit_tempo_marker (item);
834 case MeterMarkerItem:
835 edit_meter_marker (item);
839 if (clicked_regionview->name_active()) {
840 return mouse_rename_region (item, event);
850 /* context menu events get handled here */
852 if (Keyboard::is_context_menu_event (&event->button)) {
854 if (drag_info.item == 0) {
856 /* no matter which button pops up the context menu, tell the menu
857 widget to use button 1 to drive menu selection.
862 case FadeInHandleItem:
864 case FadeOutHandleItem:
865 popup_fade_context_menu (1, event->button.time, item, item_type);
869 popup_track_context_menu (1, event->button.time, item_type, false, where);
873 case RegionViewNameHighlight:
875 popup_track_context_menu (1, event->button.time, item_type, false, where);
879 popup_track_context_menu (1, event->button.time, item_type, true, where);
882 case AutomationTrackItem:
883 popup_track_context_menu (1, event->button.time, item_type, false, where);
887 case RangeMarkerBarItem:
888 case TransportMarkerBarItem:
891 popup_ruler_menu (pixel_to_frame(event->button.x), item_type);
895 marker_context_menu (&event->button, item);
898 case TempoMarkerItem:
899 tm_marker_context_menu (&event->button, item);
902 case MeterMarkerItem:
903 tm_marker_context_menu (&event->button, item);
906 case CrossfadeViewItem:
907 popup_track_context_menu (1, event->button.time, item_type, false, where);
910 /* <CMT Additions> */
912 popup_imageframe_edit_menu(1, event->button.time, item, true) ;
914 case ImageFrameTimeAxisItem:
915 popup_imageframe_edit_menu(1, event->button.time, item, false) ;
918 popup_marker_time_axis_edit_menu(1, event->button.time, item, true) ;
920 case MarkerTimeAxisItem:
921 popup_marker_time_axis_edit_menu(1, event->button.time, item, false) ;
923 /* <CMT Additions> */
934 /* delete events get handled here */
936 if (drag_info.item == 0 && Keyboard::is_delete_event (&event->button)) {
939 case TempoMarkerItem:
940 remove_tempo_marker (item);
943 case MeterMarkerItem:
944 remove_meter_marker (item);
948 remove_marker (*item, event);
952 if (mouse_mode == MouseObject) {
953 remove_clicked_region ();
957 case GainControlPointItem:
958 if (mouse_mode == MouseGain) {
959 remove_gain_control_point (item, event);
963 case GainAutomationControlPointItem:
964 case PanAutomationControlPointItem:
965 case RedirectAutomationControlPointItem:
966 remove_control_point (item, event);
975 switch (event->button.button) {
979 /* see comments in button_press_handler */
981 case PlayheadCursorItem:
984 case GainAutomationLineItem:
985 case PanAutomationLineItem:
986 case RedirectAutomationLineItem:
987 case StartSelectionTrimItem:
988 case EndSelectionTrimItem:
992 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
993 snap_to (where, 0, true);
995 mouse_add_new_marker (where);
999 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1002 mouse_add_new_tempo_event (where);
1006 mouse_add_new_meter_event (pixel_to_frame (event->button.x));
1014 switch (mouse_mode) {
1016 switch (item_type) {
1017 case AutomationTrackItem:
1018 dynamic_cast<AutomationTimeAxisView*>(clicked_trackview)->add_automation_event
1032 // Gain only makes sense for audio regions
1034 if (!dynamic_cast<AudioRegionView*>(clicked_regionview)) {
1038 switch (item_type) {
1040 dynamic_cast<AudioRegionView*>(clicked_regionview)->add_gain_point_event (item, event);
1044 case AutomationTrackItem:
1045 dynamic_cast<AutomationTimeAxisView*>(clicked_trackview)->
1046 add_automation_event (item, event, where, event->button.y);
1056 if (scrubbing_direction == 0) {
1057 /* no drag, just a click */
1058 switch (item_type) {
1060 audition_selected_region ();
1066 /* make sure we stop */
1067 session->request_transport_speed (0.0);
1081 switch (mouse_mode) {
1084 switch (item_type) {
1086 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
1088 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::Shift|Keyboard::Alt))) {
1091 // Button2 click is unused
1104 // x_style_paste (where, 1.0);
1124 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1130 switch (item_type) {
1131 case GainControlPointItem:
1132 if (mouse_mode == MouseGain) {
1133 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1134 cp->set_visible (true);
1138 at_y = cp->get_y ();
1139 cp->item->i2w (at_x, at_y);
1143 fraction = 1.0 - (cp->get_y() / cp->line.height());
1145 set_verbose_canvas_cursor (cp->line.get_verbose_cursor_string (fraction), at_x, at_y);
1146 show_verbose_canvas_cursor ();
1148 if (is_drawable()) {
1149 track_canvas.get_window()->set_cursor (*fader_cursor);
1154 case GainAutomationControlPointItem:
1155 case PanAutomationControlPointItem:
1156 case RedirectAutomationControlPointItem:
1157 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1158 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1159 cp->set_visible (true);
1163 at_y = cp->get_y ();
1164 cp->item->i2w (at_x, at_y);
1168 fraction = 1.0 - (cp->get_y() / cp->line.height());
1170 set_verbose_canvas_cursor (cp->line.get_verbose_cursor_string (fraction), at_x, at_y);
1171 show_verbose_canvas_cursor ();
1173 if (is_drawable()) {
1174 track_canvas.get_window()->set_cursor (*fader_cursor);
1180 if (mouse_mode == MouseGain) {
1181 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1183 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredGainLine.get();
1184 if (is_drawable()) {
1185 track_canvas.get_window()->set_cursor (*fader_cursor);
1190 case GainAutomationLineItem:
1191 case RedirectAutomationLineItem:
1192 case PanAutomationLineItem:
1193 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1195 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1197 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredAutomationLine.get();
1199 if (is_drawable()) {
1200 track_canvas.get_window()->set_cursor (*fader_cursor);
1205 case RegionViewNameHighlight:
1206 if (is_drawable() && mouse_mode == MouseObject) {
1207 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1211 case StartSelectionTrimItem:
1212 case EndSelectionTrimItem:
1213 /* <CMT Additions> */
1214 case ImageFrameHandleStartItem:
1215 case ImageFrameHandleEndItem:
1216 case MarkerViewHandleStartItem:
1217 case MarkerViewHandleEndItem:
1218 /* </CMT Additions> */
1220 if (is_drawable()) {
1221 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1225 case EditCursorItem:
1226 case PlayheadCursorItem:
1227 if (is_drawable()) {
1228 track_canvas.get_window()->set_cursor (*grabber_cursor);
1232 case RegionViewName:
1234 /* when the name is not an active item, the entire name highlight is for trimming */
1236 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1237 if (mouse_mode == MouseObject && is_drawable()) {
1238 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1244 case AutomationTrackItem:
1245 if (is_drawable()) {
1246 Gdk::Cursor *cursor;
1247 switch (mouse_mode) {
1249 cursor = selector_cursor;
1252 cursor = zoom_cursor;
1255 cursor = cross_hair_cursor;
1259 track_canvas.get_window()->set_cursor (*cursor);
1261 AutomationTimeAxisView* atv;
1262 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1263 clear_entered_track = false;
1264 set_entered_track (atv);
1270 case RangeMarkerBarItem:
1271 case TransportMarkerBarItem:
1274 if (is_drawable()) {
1275 time_canvas.get_window()->set_cursor (*timebar_cursor);
1280 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1283 marker->set_color_rgba (ARDOUR_UI::config()->canvasvar_EnteredMarker.get());
1285 case MeterMarkerItem:
1286 case TempoMarkerItem:
1287 if (is_drawable()) {
1288 time_canvas.get_window()->set_cursor (*timebar_cursor);
1291 case FadeInHandleItem:
1292 case FadeOutHandleItem:
1293 if (mouse_mode == MouseObject) {
1294 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1296 rect->property_fill_color_rgba() = 0;
1297 rect->property_outline_pixels() = 1;
1306 /* second pass to handle entered track status in a comprehensible way.
1309 switch (item_type) {
1311 case GainAutomationLineItem:
1312 case RedirectAutomationLineItem:
1313 case PanAutomationLineItem:
1314 case GainControlPointItem:
1315 case GainAutomationControlPointItem:
1316 case PanAutomationControlPointItem:
1317 case RedirectAutomationControlPointItem:
1318 /* these do not affect the current entered track state */
1319 clear_entered_track = false;
1322 case AutomationTrackItem:
1323 /* handled above already */
1327 set_entered_track (0);
1335 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1344 switch (item_type) {
1345 case GainControlPointItem:
1346 case GainAutomationControlPointItem:
1347 case PanAutomationControlPointItem:
1348 case RedirectAutomationControlPointItem:
1349 cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
1350 if (cp->line.npoints() > 1) {
1351 if (!cp->selected) {
1352 cp->set_visible (false);
1356 if (is_drawable()) {
1357 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1360 hide_verbose_canvas_cursor ();
1363 case RegionViewNameHighlight:
1364 case StartSelectionTrimItem:
1365 case EndSelectionTrimItem:
1366 case EditCursorItem:
1367 case PlayheadCursorItem:
1368 /* <CMT Additions> */
1369 case ImageFrameHandleStartItem:
1370 case ImageFrameHandleEndItem:
1371 case MarkerViewHandleStartItem:
1372 case MarkerViewHandleEndItem:
1373 /* </CMT Additions> */
1374 if (is_drawable()) {
1375 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1380 case GainAutomationLineItem:
1381 case RedirectAutomationLineItem:
1382 case PanAutomationLineItem:
1383 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1385 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1387 line->property_fill_color_rgba() = al->get_line_color();
1389 if (is_drawable()) {
1390 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1394 case RegionViewName:
1395 /* see enter_handler() for notes */
1396 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1397 if (is_drawable() && mouse_mode == MouseObject) {
1398 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1403 case RangeMarkerBarItem:
1404 case TransportMarkerBarItem:
1408 if (is_drawable()) {
1409 time_canvas.get_window()->set_cursor (*timebar_cursor);
1414 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1417 loc = find_location_from_marker (marker, is_start);
1418 if (loc) location_flags_changed (loc, this);
1420 case MeterMarkerItem:
1421 case TempoMarkerItem:
1423 if (is_drawable()) {
1424 time_canvas.get_window()->set_cursor (*timebar_cursor);
1429 case FadeInHandleItem:
1430 case FadeOutHandleItem:
1431 rv = static_cast<RegionView*>(item->get_data ("regionview"));
1433 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1435 rect->property_fill_color_rgba() = rv->get_fill_color();
1436 rect->property_outline_pixels() = 0;
1441 case AutomationTrackItem:
1442 if (is_drawable()) {
1443 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1444 clear_entered_track = true;
1445 Glib::signal_idle().connect (mem_fun(*this, &Editor::left_automation_track));
1457 Editor::left_automation_track ()
1459 if (clear_entered_track) {
1460 set_entered_track (0);
1461 clear_entered_track = false;
1467 Editor::motion_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type, bool from_autoscroll)
1471 /* We call this so that MOTION_NOTIFY events continue to be
1472 delivered to the canvas. We need to do this because we set
1473 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
1474 the density of the events, at the expense of a round-trip
1475 to the server. Given that this will mostly occur on cases
1476 where DISPLAY = :0.0, and given the cost of what the motion
1477 event might do, its a good tradeoff.
1480 track_canvas.get_pointer (x, y);
1482 if (current_stepping_trackview) {
1483 /* don't keep the persistent stepped trackview if the mouse moves */
1484 current_stepping_trackview = 0;
1485 step_timeout.disconnect ();
1488 if (session && session->actively_recording()) {
1489 /* Sorry. no dragging stuff around while we record */
1493 drag_info.item_type = item_type;
1494 drag_info.last_pointer_x = drag_info.current_pointer_x;
1495 drag_info.last_pointer_y = drag_info.current_pointer_y;
1496 drag_info.current_pointer_frame = event_frame (event, &drag_info.current_pointer_x,
1497 &drag_info.current_pointer_y);
1499 switch (mouse_mode) {
1505 if (scrubbing_direction == 0) {
1507 session->request_locate (drag_info.current_pointer_frame, false);
1508 session->request_transport_speed (0.1);
1509 scrubbing_direction = 1;
1514 if (last_scrub_x > drag_info.current_pointer_x) {
1515 /* move to the left */
1517 if (scrubbing_direction > 0) {
1518 /* we reversed direction to go backwards */
1520 session->request_transport_speed (-0.1);
1523 /* still moving to the left (backwards) */
1525 delta = 0.005 * (last_scrub_x - drag_info.current_pointer_x);
1526 session->request_transport_speed (session->transport_speed() - delta);
1529 scrubbing_direction = -1;
1532 /* move to the right */
1533 if (scrubbing_direction < 0) {
1534 /* we reversed direction to go forward */
1536 session->request_transport_speed (0.1);
1538 /* still moving to the right */
1540 delta = 0.005 * (drag_info.current_pointer_x - last_scrub_x);
1541 session->request_transport_speed (session->transport_speed() + delta);
1544 scrubbing_direction = 1;
1548 last_scrub_x = drag_info.current_pointer_x;
1555 if (!from_autoscroll && drag_info.item) {
1556 /* item != 0 is the best test i can think of for dragging.
1558 if (!drag_info.move_threshold_passed) {
1560 bool x_threshold_passed = (abs ((nframes64_t) (drag_info.current_pointer_x - drag_info.grab_x)) > 4LL);
1561 bool y_threshold_passed = (abs ((nframes64_t) (drag_info.current_pointer_y - drag_info.grab_y)) > 4LL);
1563 drag_info.move_threshold_passed = (x_threshold_passed || y_threshold_passed);
1565 // and change the initial grab loc/frame if this drag info wants us to
1567 if (drag_info.want_move_threshold && drag_info.move_threshold_passed) {
1568 drag_info.grab_frame = drag_info.current_pointer_frame;
1569 drag_info.grab_x = drag_info.current_pointer_x;
1570 drag_info.grab_y = drag_info.current_pointer_y;
1571 drag_info.last_pointer_frame = drag_info.grab_frame;
1572 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
1577 switch (item_type) {
1578 case PlayheadCursorItem:
1579 case EditCursorItem:
1581 case GainControlPointItem:
1582 case RedirectAutomationControlPointItem:
1583 case GainAutomationControlPointItem:
1584 case PanAutomationControlPointItem:
1585 case TempoMarkerItem:
1586 case MeterMarkerItem:
1587 case RegionViewNameHighlight:
1588 case StartSelectionTrimItem:
1589 case EndSelectionTrimItem:
1592 case RedirectAutomationLineItem:
1593 case GainAutomationLineItem:
1594 case PanAutomationLineItem:
1595 case FadeInHandleItem:
1596 case FadeOutHandleItem:
1597 /* <CMT Additions> */
1598 case ImageFrameHandleStartItem:
1599 case ImageFrameHandleEndItem:
1600 case MarkerViewHandleStartItem:
1601 case MarkerViewHandleEndItem:
1602 /* </CMT Additions> */
1603 if (drag_info.item && (event->motion.state & Gdk::BUTTON1_MASK ||
1604 (event->motion.state & Gdk::BUTTON2_MASK))) {
1605 if (!from_autoscroll) {
1606 maybe_autoscroll (event);
1608 (this->*(drag_info.motion_callback)) (item, event);
1617 switch (mouse_mode) {
1622 if (drag_info.item && (event->motion.state & GDK_BUTTON1_MASK ||
1623 (event->motion.state & GDK_BUTTON2_MASK))) {
1624 if (!from_autoscroll) {
1625 maybe_autoscroll (event);
1627 (this->*(drag_info.motion_callback)) (item, event);
1638 track_canvas_motion (event);
1639 // drag_info.last_pointer_frame = drag_info.current_pointer_frame;
1647 Editor::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
1649 if (drag_info.item == 0) {
1650 fatal << _("programming error: start_grab called without drag item") << endmsg;
1656 cursor = grabber_cursor;
1659 // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
1661 if (event->button.button == 2) {
1662 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Alt)) {
1663 drag_info.y_constrained = true;
1664 drag_info.x_constrained = false;
1666 drag_info.y_constrained = false;
1667 drag_info.x_constrained = true;
1670 drag_info.x_constrained = false;
1671 drag_info.y_constrained = false;
1674 drag_info.grab_frame = event_frame (event, &drag_info.grab_x, &drag_info.grab_y);
1675 drag_info.last_pointer_frame = drag_info.grab_frame;
1676 drag_info.current_pointer_frame = drag_info.grab_frame;
1677 drag_info.current_pointer_x = drag_info.grab_x;
1678 drag_info.current_pointer_y = drag_info.grab_y;
1679 drag_info.last_pointer_x = drag_info.current_pointer_x;
1680 drag_info.last_pointer_y = drag_info.current_pointer_y;
1681 drag_info.cumulative_x_drag = 0;
1682 drag_info.cumulative_y_drag = 0;
1683 drag_info.first_move = true;
1684 drag_info.move_threshold_passed = false;
1685 drag_info.want_move_threshold = false;
1686 drag_info.pointer_frame_offset = 0;
1687 drag_info.brushing = false;
1688 drag_info.copied_location = 0;
1690 drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK,
1692 event->button.time);
1694 if (session && session->transport_rolling()) {
1695 drag_info.was_rolling = true;
1697 drag_info.was_rolling = false;
1700 switch (snap_type) {
1701 case SnapToRegionStart:
1702 case SnapToRegionEnd:
1703 case SnapToRegionSync:
1704 case SnapToRegionBoundary:
1705 build_region_boundary_cache ();
1713 Editor::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t time)
1715 drag_info.item->ungrab (0);
1716 drag_info.item = new_item;
1719 cursor = grabber_cursor;
1722 drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK, *cursor, time);
1726 Editor::end_grab (ArdourCanvas::Item* item, GdkEvent* event)
1728 bool did_drag = false;
1730 stop_canvas_autoscroll ();
1732 if (drag_info.item == 0) {
1736 drag_info.item->ungrab (event->button.time);
1738 if (drag_info.finished_callback) {
1739 drag_info.last_pointer_x = drag_info.current_pointer_x;
1740 drag_info.last_pointer_y = drag_info.current_pointer_y;
1741 (this->*(drag_info.finished_callback)) (item, event);
1744 did_drag = !drag_info.first_move;
1746 hide_verbose_canvas_cursor();
1749 drag_info.copy = false;
1750 drag_info.motion_callback = 0;
1751 drag_info.finished_callback = 0;
1752 drag_info.last_trackview = 0;
1753 drag_info.last_frame_position = 0;
1754 drag_info.grab_frame = 0;
1755 drag_info.last_pointer_frame = 0;
1756 drag_info.current_pointer_frame = 0;
1757 drag_info.brushing = false;
1759 if (drag_info.copied_location) {
1760 delete drag_info.copied_location;
1761 drag_info.copied_location = 0;
1768 Editor::set_edit_cursor (GdkEvent* event)
1770 nframes_t pointer_frame = event_frame (event);
1772 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1773 if (snap_type != SnapToEditCursor) {
1774 snap_to (pointer_frame);
1778 edit_cursor->set_position (pointer_frame);
1779 edit_cursor_clock.set (pointer_frame);
1783 Editor::set_playhead_cursor (GdkEvent* event)
1785 nframes_t pointer_frame = event_frame (event);
1787 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1788 snap_to (pointer_frame);
1792 session->request_locate (pointer_frame, session->transport_rolling());
1797 Editor::start_fade_in_grab (ArdourCanvas::Item* item, GdkEvent* event)
1799 drag_info.item = item;
1800 drag_info.motion_callback = &Editor::fade_in_drag_motion_callback;
1801 drag_info.finished_callback = &Editor::fade_in_drag_finished_callback;
1805 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
1806 fatal << _("programming error: fade in canvas item has no regionview data pointer!") << endmsg;
1810 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1812 drag_info.pointer_frame_offset = drag_info.grab_frame - ((nframes_t) arv->audio_region()->fade_in().back()->when + arv->region()->position());
1816 Editor::fade_in_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1818 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1820 nframes_t fade_length;
1822 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1823 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1829 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1833 if (pos < (arv->region()->position() + 64)) {
1834 fade_length = 64; // this should be a minimum defined somewhere
1835 } else if (pos > arv->region()->last_frame()) {
1836 fade_length = arv->region()->length();
1838 fade_length = pos - arv->region()->position();
1840 /* mapover the region selection */
1842 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1844 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1850 tmp->reset_fade_in_shape_width (fade_length);
1853 show_verbose_duration_cursor (arv->region()->position(), arv->region()->position() + fade_length, 10);
1855 drag_info.first_move = false;
1859 Editor::fade_in_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1861 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1863 nframes_t fade_length;
1865 if (drag_info.first_move) return;
1867 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1868 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1873 if (pos < (arv->region()->position() + 64)) {
1874 fade_length = 64; // this should be a minimum defined somewhere
1875 } else if (pos > arv->region()->last_frame()) {
1876 fade_length = arv->region()->length();
1878 fade_length = pos - arv->region()->position();
1881 begin_reversible_command (_("change fade in length"));
1883 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1885 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1891 AutomationList& alist = tmp->audio_region()->fade_in();
1892 XMLNode &before = alist.get_state();
1894 tmp->audio_region()->set_fade_in_length (fade_length);
1896 XMLNode &after = alist.get_state();
1897 session->add_command(new MementoCommand<AutomationList>(alist, &before, &after));
1900 commit_reversible_command ();
1904 Editor::start_fade_out_grab (ArdourCanvas::Item* item, GdkEvent* event)
1906 drag_info.item = item;
1907 drag_info.motion_callback = &Editor::fade_out_drag_motion_callback;
1908 drag_info.finished_callback = &Editor::fade_out_drag_finished_callback;
1912 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
1913 fatal << _("programming error: fade out canvas item has no regionview data pointer!") << endmsg;
1917 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1919 drag_info.pointer_frame_offset = drag_info.grab_frame - (arv->region()->length() - (nframes_t) arv->audio_region()->fade_out().back()->when + arv->region()->position());
1923 Editor::fade_out_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1925 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1927 nframes_t fade_length;
1929 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1930 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1935 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1939 if (pos > (arv->region()->last_frame() - 64)) {
1940 fade_length = 64; // this should really be a minimum fade defined somewhere
1942 else if (pos < arv->region()->position()) {
1943 fade_length = arv->region()->length();
1946 fade_length = arv->region()->last_frame() - pos;
1949 /* mapover the region selection */
1951 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1953 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1959 tmp->reset_fade_out_shape_width (fade_length);
1962 show_verbose_duration_cursor (arv->region()->last_frame() - fade_length, arv->region()->last_frame(), 10);
1964 drag_info.first_move = false;
1968 Editor::fade_out_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1970 if (drag_info.first_move) return;
1972 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1974 nframes_t fade_length;
1976 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1977 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1983 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1987 if (pos > (arv->region()->last_frame() - 64)) {
1988 fade_length = 64; // this should really be a minimum fade defined somewhere
1990 else if (pos < arv->region()->position()) {
1991 fade_length = arv->region()->length();
1994 fade_length = arv->region()->last_frame() - pos;
1997 begin_reversible_command (_("change fade out length"));
1999 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2001 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2007 AutomationList& alist = tmp->audio_region()->fade_out();
2008 XMLNode &before = alist.get_state();
2010 tmp->audio_region()->set_fade_out_length (fade_length);
2012 XMLNode &after = alist.get_state();
2013 session->add_command(new MementoCommand<AutomationList>(alist, &before, &after));
2016 commit_reversible_command ();
2020 Editor::start_cursor_grab (ArdourCanvas::Item* item, GdkEvent* event)
2022 drag_info.item = item;
2023 drag_info.motion_callback = &Editor::cursor_drag_motion_callback;
2024 drag_info.finished_callback = &Editor::cursor_drag_finished_callback;
2028 if ((drag_info.data = (item->get_data ("cursor"))) == 0) {
2029 fatal << _("programming error: cursor canvas item has no cursor data pointer!") << endmsg;
2033 Cursor* cursor = (Cursor *) drag_info.data;
2035 if (cursor == playhead_cursor) {
2036 _dragging_playhead = true;
2038 if (session && drag_info.was_rolling) {
2039 session->request_stop ();
2042 if (session && session->is_auditioning()) {
2043 session->cancel_audition ();
2047 drag_info.pointer_frame_offset = drag_info.grab_frame - cursor->current_frame;
2049 show_verbose_time_cursor (cursor->current_frame, 10);
2053 Editor::cursor_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2055 Cursor* cursor = (Cursor *) drag_info.data;
2056 nframes_t adjusted_frame;
2058 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2059 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2065 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2066 if (cursor != edit_cursor || snap_type != SnapToEditCursor) {
2067 snap_to (adjusted_frame);
2071 if (adjusted_frame == drag_info.last_pointer_frame) return;
2073 cursor->set_position (adjusted_frame);
2075 if (cursor == edit_cursor) {
2076 edit_cursor_clock.set (cursor->current_frame);
2078 UpdateAllTransportClocks (cursor->current_frame);
2081 show_verbose_time_cursor (cursor->current_frame, 10);
2083 drag_info.last_pointer_frame = adjusted_frame;
2084 drag_info.first_move = false;
2088 Editor::cursor_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2090 if (drag_info.first_move) return;
2092 cursor_drag_motion_callback (item, event);
2094 _dragging_playhead = false;
2096 if (item == &playhead_cursor->canvas_item) {
2098 session->request_locate (playhead_cursor->current_frame, drag_info.was_rolling);
2100 } else if (item == &edit_cursor->canvas_item) {
2101 edit_cursor->set_position (edit_cursor->current_frame);
2102 edit_cursor_clock.set (edit_cursor->current_frame);
2107 Editor::update_marker_drag_item (Location *location)
2109 double x1 = frame_to_pixel (location->start());
2110 double x2 = frame_to_pixel (location->end());
2112 if (location->is_mark()) {
2113 marker_drag_line_points.front().set_x(x1);
2114 marker_drag_line_points.back().set_x(x1);
2115 marker_drag_line->property_points() = marker_drag_line_points;
2118 range_marker_drag_rect->property_x1() = x1;
2119 range_marker_drag_rect->property_x2() = x2;
2124 Editor::start_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2128 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2129 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2135 Location *location = find_location_from_marker (marker, is_start);
2137 drag_info.item = item;
2138 drag_info.data = marker;
2139 drag_info.motion_callback = &Editor::marker_drag_motion_callback;
2140 drag_info.finished_callback = &Editor::marker_drag_finished_callback;
2144 drag_info.copied_location = new Location (*location);
2145 drag_info.pointer_frame_offset = drag_info.grab_frame - (is_start ? location->start() : location->end());
2147 update_marker_drag_item (location);
2149 if (location->is_mark()) {
2150 marker_drag_line->show();
2151 marker_drag_line->raise_to_top();
2153 range_marker_drag_rect->show();
2154 range_marker_drag_rect->raise_to_top();
2158 show_verbose_time_cursor (location->start(), 10);
2160 show_verbose_time_cursor (location->end(), 10);
2165 Editor::marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2168 Marker* marker = (Marker *) drag_info.data;
2169 Location *real_location;
2170 Location *copy_location;
2172 bool move_both = false;
2176 if (drag_info.pointer_frame_offset <= drag_info.current_pointer_frame) {
2177 newframe = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2182 nframes_t next = newframe;
2184 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2185 snap_to (newframe, 0, true);
2188 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
2192 /* call this to find out if its the start or end */
2194 real_location = find_location_from_marker (marker, is_start);
2196 /* use the copy that we're "dragging" around */
2198 copy_location = drag_info.copied_location;
2200 f_delta = copy_location->end() - copy_location->start();
2202 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
2206 if (copy_location->is_mark()) {
2209 copy_location->set_start (newframe);
2213 if (is_start) { // start-of-range marker
2216 copy_location->set_start (newframe);
2217 copy_location->set_end (newframe + f_delta);
2218 } else if (newframe < copy_location->end()) {
2219 copy_location->set_start (newframe);
2221 snap_to (next, 1, true);
2222 copy_location->set_end (next);
2223 copy_location->set_start (newframe);
2226 } else { // end marker
2229 copy_location->set_end (newframe);
2230 copy_location->set_start (newframe - f_delta);
2231 } else if (newframe > copy_location->start()) {
2232 copy_location->set_end (newframe);
2234 } else if (newframe > 0) {
2235 snap_to (next, -1, true);
2236 copy_location->set_start (next);
2237 copy_location->set_end (newframe);
2242 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
2243 drag_info.first_move = false;
2245 update_marker_drag_item (copy_location);
2247 LocationMarkers* lm = find_location_markers (real_location);
2248 lm->set_position (copy_location->start(), copy_location->end());
2250 show_verbose_time_cursor (newframe, 10);
2254 Editor::marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2256 if (drag_info.first_move) {
2257 marker_drag_motion_callback (item, event);
2261 Marker* marker = (Marker *) drag_info.data;
2265 begin_reversible_command ( _("move marker") );
2266 XMLNode &before = session->locations()->get_state();
2268 Location * location = find_location_from_marker (marker, is_start);
2271 if (location->is_mark()) {
2272 location->set_start (drag_info.copied_location->start());
2274 location->set (drag_info.copied_location->start(), drag_info.copied_location->end());
2278 XMLNode &after = session->locations()->get_state();
2279 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
2280 commit_reversible_command ();
2282 marker_drag_line->hide();
2283 range_marker_drag_rect->hide();
2287 Editor::start_meter_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2290 MeterMarker* meter_marker;
2292 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2293 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2297 meter_marker = dynamic_cast<MeterMarker*> (marker);
2299 MetricSection& section (meter_marker->meter());
2301 if (!section.movable()) {
2305 drag_info.item = item;
2306 drag_info.data = marker;
2307 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2308 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2312 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2314 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2318 Editor::start_meter_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2321 MeterMarker* meter_marker;
2323 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2324 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2328 meter_marker = dynamic_cast<MeterMarker*> (marker);
2330 // create a dummy marker for visual representation of moving the copy.
2331 // The actual copying is not done before we reach the finish callback.
2333 snprintf (name, sizeof(name), "%g/%g", meter_marker->meter().beats_per_bar(), meter_marker->meter().note_divisor ());
2334 MeterMarker* new_marker = new MeterMarker(*this, *meter_group, ARDOUR_UI::config()->canvasvar_MeterMarker.get(), name,
2335 *new MeterSection(meter_marker->meter()));
2337 drag_info.item = &new_marker->the_item();
2338 drag_info.copy = true;
2339 drag_info.data = new_marker;
2340 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2341 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2345 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2347 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2351 Editor::meter_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2353 MeterMarker* marker = (MeterMarker *) drag_info.data;
2354 nframes_t adjusted_frame;
2356 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2357 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2363 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2364 snap_to (adjusted_frame);
2367 if (adjusted_frame == drag_info.last_pointer_frame) return;
2369 marker->set_position (adjusted_frame);
2372 drag_info.last_pointer_frame = adjusted_frame;
2373 drag_info.first_move = false;
2375 show_verbose_time_cursor (adjusted_frame, 10);
2379 Editor::meter_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2381 if (drag_info.first_move) return;
2383 meter_marker_drag_motion_callback (drag_info.item, event);
2385 MeterMarker* marker = (MeterMarker *) drag_info.data;
2388 TempoMap& map (session->tempo_map());
2389 map.bbt_time (drag_info.last_pointer_frame, when);
2391 if (drag_info.copy == true) {
2392 begin_reversible_command (_("copy meter mark"));
2393 XMLNode &before = map.get_state();
2394 map.add_meter (marker->meter(), when);
2395 XMLNode &after = map.get_state();
2396 session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2397 commit_reversible_command ();
2399 // delete the dummy marker we used for visual representation of copying.
2400 // a new visual marker will show up automatically.
2403 begin_reversible_command (_("move meter mark"));
2404 XMLNode &before = map.get_state();
2405 map.move_meter (marker->meter(), when);
2406 XMLNode &after = map.get_state();
2407 session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2408 commit_reversible_command ();
2413 Editor::start_tempo_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2416 TempoMarker* tempo_marker;
2418 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2419 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2423 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2424 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2428 MetricSection& section (tempo_marker->tempo());
2430 if (!section.movable()) {
2434 drag_info.item = item;
2435 drag_info.data = marker;
2436 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2437 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2441 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2442 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2446 Editor::start_tempo_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2449 TempoMarker* tempo_marker;
2451 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2452 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2456 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2457 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2461 // create a dummy marker for visual representation of moving the copy.
2462 // The actual copying is not done before we reach the finish callback.
2464 snprintf (name, sizeof (name), "%.2f", tempo_marker->tempo().beats_per_minute());
2465 TempoMarker* new_marker = new TempoMarker(*this, *tempo_group, ARDOUR_UI::config()->canvasvar_TempoMarker.get(), name,
2466 *new TempoSection(tempo_marker->tempo()));
2468 drag_info.item = &new_marker->the_item();
2469 drag_info.copy = true;
2470 drag_info.data = new_marker;
2471 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2472 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2476 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2478 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2482 Editor::tempo_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2484 TempoMarker* marker = (TempoMarker *) drag_info.data;
2485 nframes_t adjusted_frame;
2487 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2488 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2494 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2495 snap_to (adjusted_frame);
2498 if (adjusted_frame == drag_info.last_pointer_frame) return;
2500 /* OK, we've moved far enough to make it worth actually move the thing. */
2502 marker->set_position (adjusted_frame);
2504 show_verbose_time_cursor (adjusted_frame, 10);
2506 drag_info.last_pointer_frame = adjusted_frame;
2507 drag_info.first_move = false;
2511 Editor::tempo_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2513 if (drag_info.first_move) return;
2515 tempo_marker_drag_motion_callback (drag_info.item, event);
2517 TempoMarker* marker = (TempoMarker *) drag_info.data;
2520 TempoMap& map (session->tempo_map());
2521 map.bbt_time (drag_info.last_pointer_frame, when);
2523 if (drag_info.copy == true) {
2524 begin_reversible_command (_("copy tempo mark"));
2525 XMLNode &before = map.get_state();
2526 map.add_tempo (marker->tempo(), when);
2527 XMLNode &after = map.get_state();
2528 session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2529 commit_reversible_command ();
2531 // delete the dummy marker we used for visual representation of copying.
2532 // a new visual marker will show up automatically.
2535 begin_reversible_command (_("move tempo mark"));
2536 XMLNode &before = map.get_state();
2537 map.move_tempo (marker->tempo(), when);
2538 XMLNode &after = map.get_state();
2539 session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2540 commit_reversible_command ();
2545 Editor::remove_gain_control_point (ArdourCanvas::Item*item, GdkEvent* event)
2547 ControlPoint* control_point;
2549 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2550 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2554 // We shouldn't remove the first or last gain point
2555 if (control_point->line.is_last_point(*control_point) ||
2556 control_point->line.is_first_point(*control_point)) {
2560 control_point->line.remove_point (*control_point);
2564 Editor::remove_control_point (ArdourCanvas::Item*item, GdkEvent* event)
2566 ControlPoint* control_point;
2568 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2569 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2573 control_point->line.remove_point (*control_point);
2577 Editor::start_control_point_grab (ArdourCanvas::Item* item, GdkEvent* event)
2579 ControlPoint* control_point;
2581 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2582 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2586 drag_info.item = item;
2587 drag_info.data = control_point;
2588 drag_info.motion_callback = &Editor::control_point_drag_motion_callback;
2589 drag_info.finished_callback = &Editor::control_point_drag_finished_callback;
2591 start_grab (event, fader_cursor);
2593 // start the grab at the center of the control point so
2594 // the point doesn't 'jump' to the mouse after the first drag
2595 drag_info.grab_x = control_point->get_x();
2596 drag_info.grab_y = control_point->get_y();
2597 control_point->line.parent_group().i2w(drag_info.grab_x, drag_info.grab_y);
2598 track_canvas.w2c(drag_info.grab_x, drag_info.grab_y,
2599 drag_info.grab_x, drag_info.grab_y);
2601 drag_info.grab_frame = pixel_to_frame(drag_info.grab_x);
2603 control_point->line.start_drag (control_point, drag_info.grab_frame, 0);
2605 float fraction = 1.0 - (control_point->get_y() / control_point->line.height());
2606 set_verbose_canvas_cursor (control_point->line.get_verbose_cursor_string (fraction),
2607 drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
2609 show_verbose_canvas_cursor ();
2613 Editor::control_point_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2615 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2617 double dx = drag_info.current_pointer_x - drag_info.last_pointer_x;
2618 double dy = drag_info.current_pointer_y - drag_info.last_pointer_y;
2620 if (event->button.state & Keyboard::Alt) {
2625 double cx = drag_info.grab_x + drag_info.cumulative_x_drag + dx;
2626 double cy = drag_info.grab_y + drag_info.cumulative_y_drag + dy;
2628 // calculate zero crossing point. back off by .01 to stay on the
2629 // positive side of zero
2631 double zero_gain_y = (1.0 - ZERO_GAIN_FRACTION) * cp->line.height() - .01;
2632 cp->line.parent_group().i2w(_unused, zero_gain_y);
2634 // make sure we hit zero when passing through
2635 if ((cy < zero_gain_y and (cy - dy) > zero_gain_y)
2636 or (cy > zero_gain_y and (cy - dy) < zero_gain_y)) {
2640 if (drag_info.x_constrained) {
2641 cx = drag_info.grab_x;
2643 if (drag_info.y_constrained) {
2644 cy = drag_info.grab_y;
2647 drag_info.cumulative_x_drag = cx - drag_info.grab_x;
2648 drag_info.cumulative_y_drag = cy - drag_info.grab_y;
2650 cp->line.parent_group().w2i (cx, cy);
2654 cy = min ((double) cp->line.height(), cy);
2656 //translate cx to frames
2657 nframes_t cx_frames = unit_to_frame (cx);
2659 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier()) && !drag_info.x_constrained) {
2660 snap_to (cx_frames);
2663 float fraction = 1.0 - (cy / cp->line.height());
2667 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
2673 cp->line.point_drag (*cp, cx_frames , fraction, push);
2675 set_verbose_canvas_cursor_text (cp->line.get_verbose_cursor_string (fraction));
2677 drag_info.first_move = false;
2681 Editor::control_point_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2683 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2685 if (drag_info.first_move) {
2689 if ((event->type == GDK_BUTTON_RELEASE) && (event->button.button == 1) && Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
2690 reset_point_selection ();
2694 control_point_drag_motion_callback (item, event);
2696 cp->line.end_drag (cp);
2700 Editor::start_line_grab_from_regionview (ArdourCanvas::Item* item, GdkEvent* event)
2702 switch (mouse_mode) {
2704 assert(dynamic_cast<AudioRegionView*>(clicked_regionview));
2705 start_line_grab (dynamic_cast<AudioRegionView*>(clicked_regionview)->get_gain_line(), event);
2713 Editor::start_line_grab_from_line (ArdourCanvas::Item* item, GdkEvent* event)
2717 if ((al = reinterpret_cast<AutomationLine*> (item->get_data ("line"))) == 0) {
2718 fatal << _("programming error: line canvas item has no line pointer!") << endmsg;
2722 start_line_grab (al, event);
2726 Editor::start_line_grab (AutomationLine* line, GdkEvent* event)
2730 nframes_t frame_within_region;
2732 /* need to get x coordinate in terms of parent (TimeAxisItemView)
2736 cx = event->button.x;
2737 cy = event->button.y;
2738 line->parent_group().w2i (cx, cy);
2739 frame_within_region = (nframes_t) floor (cx * frames_per_unit);
2741 if (!line->control_points_adjacent (frame_within_region, current_line_drag_info.before,
2742 current_line_drag_info.after)) {
2743 /* no adjacent points */
2747 drag_info.item = &line->grab_item();
2748 drag_info.data = line;
2749 drag_info.motion_callback = &Editor::line_drag_motion_callback;
2750 drag_info.finished_callback = &Editor::line_drag_finished_callback;
2752 start_grab (event, fader_cursor);
2754 double fraction = 1.0 - (cy / line->height());
2756 line->start_drag (0, drag_info.grab_frame, fraction);
2758 set_verbose_canvas_cursor (line->get_verbose_cursor_string (fraction),
2759 drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
2760 show_verbose_canvas_cursor ();
2764 Editor::line_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2766 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
2768 double dy = drag_info.current_pointer_y - drag_info.last_pointer_y;
2770 if (event->button.state & Keyboard::Alt) {
2774 double cx = drag_info.current_pointer_x;
2775 double cy = drag_info.grab_y + drag_info.cumulative_y_drag + dy;
2777 // calculate zero crossing point. back off by .01 to stay on the
2778 // positive side of zero
2780 double zero_gain_y = (1.0 - ZERO_GAIN_FRACTION) * line->height() - .01;
2781 line->parent_group().i2w(_unused, zero_gain_y);
2783 // make sure we hit zero when passing through
2784 if ((cy < zero_gain_y and (cy - dy) > zero_gain_y)
2785 or (cy > zero_gain_y and (cy - dy) < zero_gain_y)) {
2789 drag_info.cumulative_y_drag = cy - drag_info.grab_y;
2791 line->parent_group().w2i (cx, cy);
2794 cy = min ((double) line->height(), cy);
2797 fraction = 1.0 - (cy / line->height());
2801 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
2807 line->line_drag (current_line_drag_info.before, current_line_drag_info.after, fraction, push);
2809 set_verbose_canvas_cursor_text (line->get_verbose_cursor_string (fraction));
2813 Editor::line_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2815 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
2816 line_drag_motion_callback (item, event);
2821 Editor::start_region_grab (ArdourCanvas::Item* item, GdkEvent* event)
2823 if (selection->regions.empty() || clicked_regionview == 0) {
2827 drag_info.copy = false;
2828 drag_info.item = item;
2829 drag_info.data = clicked_regionview;
2830 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2831 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2836 TimeAxisView* tvp = clicked_trackview;
2837 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2839 if (tv && tv->is_audio_track()) {
2840 speed = tv->get_diskstream()->speed();
2843 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2844 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2845 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2846 // we want a move threshold
2847 drag_info.want_move_threshold = true;
2849 show_verbose_time_cursor (drag_info.last_frame_position, 10);
2851 begin_reversible_command (_("move region(s)"));
2855 Editor::start_region_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2857 if (selection->regions.empty() || clicked_regionview == 0) {
2861 drag_info.copy = true;
2862 drag_info.item = item;
2863 drag_info.data = clicked_regionview;
2867 TimeAxisView* tv = &clicked_regionview->get_time_axis_view();
2868 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*>(tv);
2871 if (atv && atv->is_audio_track()) {
2872 speed = atv->get_diskstream()->speed();
2875 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2876 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2877 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2878 // we want a move threshold
2879 drag_info.want_move_threshold = true;
2880 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2881 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2882 show_verbose_time_cursor (drag_info.last_frame_position, 10);
2886 Editor::start_region_brush_grab (ArdourCanvas::Item* item, GdkEvent* event)
2888 if (selection->regions.empty() || clicked_regionview == 0) {
2892 drag_info.copy = false;
2893 drag_info.item = item;
2894 drag_info.data = clicked_regionview;
2895 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2896 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2901 TimeAxisView* tvp = clicked_trackview;
2902 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2904 if (tv && tv->is_audio_track()) {
2905 speed = tv->get_diskstream()->speed();
2908 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2909 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2910 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2911 // we want a move threshold
2912 drag_info.want_move_threshold = true;
2913 drag_info.brushing = true;
2915 begin_reversible_command (_("Drag region brush"));
2919 Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2923 RegionView* rv = reinterpret_cast<RegionView*> (drag_info.data);
2924 nframes_t pending_region_position = 0;
2925 int32_t pointer_y_span = 0, canvas_pointer_y_span = 0, original_pointer_order;
2926 int32_t visible_y_high = 0, visible_y_low = 512; //high meaning higher numbered.. not the height on the screen
2927 bool clamp_y_axis = false;
2928 vector<int32_t> height_list(512) ;
2929 vector<int32_t>::iterator j;
2931 if (drag_info.copy && drag_info.move_threshold_passed && drag_info.want_move_threshold) {
2933 drag_info.want_move_threshold = false; // don't copy again
2935 /* duplicate the region(s) */
2937 vector<RegionView*> new_regionviews;
2939 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
2942 AudioRegionView* arv;
2947 if ((arv = dynamic_cast<AudioRegionView*>(rv)) == 0) {
2948 /* XXX handle MIDI here */
2952 nrv = new AudioRegionView (*arv);
2953 nrv->get_canvas_group()->show ();
2955 new_regionviews.push_back (nrv);
2958 if (new_regionviews.empty()) {
2962 /* reset selection to new regionviews */
2964 selection->set (new_regionviews);
2966 /* reset drag_info data to reflect the fact that we are dragging the copies */
2968 drag_info.data = new_regionviews.front();
2970 swap_grab (new_regionviews.front()->get_canvas_group (), 0, event->motion.time);
2973 /* Which trackview is this ? */
2975 TimeAxisView* tvp = trackview_by_y_position (drag_info.current_pointer_y);
2976 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
2978 /* The region motion is only processed if the pointer is over
2982 if (!tv || !tv->is_audio_track()) {
2983 /* To make sure we hide the verbose canvas cursor when the mouse is
2984 not held over and audiotrack.
2986 hide_verbose_canvas_cursor ();
2990 original_pointer_order = drag_info.last_trackview->order;
2992 /************************************************************
2994 ************************************************************/
2996 if (drag_info.brushing) {
2997 clamp_y_axis = true;
3002 if ((pointer_y_span = (drag_info.last_trackview->order - tv->order)) != 0) {
3004 int32_t children = 0, numtracks = 0;
3005 // XXX hard coding track limit, oh my, so very very bad
3006 bitset <1024> tracks (0x00);
3007 /* get a bitmask representing the visible tracks */
3009 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
3010 TimeAxisView *tracklist_timeview;
3011 tracklist_timeview = (*i);
3012 AudioTimeAxisView* atv2 = dynamic_cast<AudioTimeAxisView*>(tracklist_timeview);
3013 list<TimeAxisView*> children_list;
3015 /* zeroes are audio tracks. ones are other types. */
3017 if (!atv2->hidden()) {
3019 if (visible_y_high < atv2->order) {
3020 visible_y_high = atv2->order;
3022 if (visible_y_low > atv2->order) {
3023 visible_y_low = atv2->order;
3026 if (!atv2->is_audio_track()) {
3027 tracks = tracks |= (0x01 << atv2->order);
3030 height_list[atv2->order] = (*i)->height;
3032 if ((children_list = atv2->get_child_list()).size() > 0) {
3033 for (list<TimeAxisView*>::iterator j = children_list.begin(); j != children_list.end(); ++j) {
3034 tracks = tracks |= (0x01 << (atv2->order + children));
3035 height_list[atv2->order + children] = (*j)->height;
3043 /* find the actual span according to the canvas */
3045 canvas_pointer_y_span = pointer_y_span;
3046 if (drag_info.last_trackview->order >= tv->order) {
3048 for (y = tv->order; y < drag_info.last_trackview->order; y++) {
3049 if (height_list[y] == 0 ) {
3050 canvas_pointer_y_span--;
3055 for (y = drag_info.last_trackview->order;y <= tv->order; y++) {
3056 if ( height_list[y] == 0 ) {
3057 canvas_pointer_y_span++;
3062 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3063 RegionView* rv2 = (*i);
3064 double ix1, ix2, iy1, iy2;
3067 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3068 rv2->get_canvas_group()->i2w (ix1, iy1);
3069 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3070 RouteTimeAxisView* atv2 = dynamic_cast<RouteTimeAxisView*>(tvp2);
3072 if (atv2->order != original_pointer_order) {
3073 /* this isn't the pointer track */
3075 if (canvas_pointer_y_span > 0) {
3077 /* moving up the canvas */
3078 if ((atv2->order - canvas_pointer_y_span) >= visible_y_low) {
3080 int32_t visible_tracks = 0;
3081 while (visible_tracks < canvas_pointer_y_span ) {
3084 while (height_list[atv2->order - (visible_tracks - n)] == 0) {
3085 /* we're passing through a hidden track */
3090 if (tracks[atv2->order - (canvas_pointer_y_span - n)] != 0x00) {
3091 clamp_y_axis = true;
3095 clamp_y_axis = true;
3098 } else if (canvas_pointer_y_span < 0) {
3100 /*moving down the canvas*/
3102 if ((atv2->order - (canvas_pointer_y_span - n)) <= visible_y_high) { // we will overflow
3105 int32_t visible_tracks = 0;
3107 while (visible_tracks > canvas_pointer_y_span ) {
3110 while (height_list[atv2->order - (visible_tracks - n)] == 0) {
3114 if ( tracks[atv2->order - ( canvas_pointer_y_span - n)] != 0x00) {
3115 clamp_y_axis = true;
3120 clamp_y_axis = true;
3126 /* this is the pointer's track */
3127 if ((atv2->order - pointer_y_span) > visible_y_high) { // we will overflow
3128 clamp_y_axis = true;
3129 } else if ((atv2->order - pointer_y_span) < visible_y_low) { // we will underflow
3130 clamp_y_axis = true;
3138 } else if (drag_info.last_trackview == tv) {
3139 clamp_y_axis = true;
3143 if (!clamp_y_axis) {
3144 drag_info.last_trackview = tv;
3147 /************************************************************
3149 ************************************************************/
3151 /* compute the amount of pointer motion in frames, and where
3152 the region would be if we moved it by that much.
3155 if (drag_info.move_threshold_passed) {
3157 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
3159 nframes_t sync_frame;
3160 nframes_t sync_offset;
3163 pending_region_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
3165 sync_offset = rv->region()->sync_offset (sync_dir);
3166 sync_frame = rv->region()->adjust_to_sync (pending_region_position);
3168 /* we snap if the snap modifier is not enabled.
3171 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3172 snap_to (sync_frame);
3175 if (sync_frame - sync_offset <= sync_frame) {
3176 pending_region_position = sync_frame - (sync_dir*sync_offset);
3178 pending_region_position = 0;
3182 pending_region_position = 0;
3185 if (pending_region_position > max_frames - rv->region()->length()) {
3186 pending_region_position = drag_info.last_frame_position;
3189 // printf ("3: pending_region_position= %lu %lu\n", pending_region_position, drag_info.last_frame_position );
3191 if (pending_region_position != drag_info.last_frame_position && !drag_info.x_constrained) {
3193 /* now compute the canvas unit distance we need to move the regiondrag_info.last_trackview->order
3194 to make it appear at the new location.
3197 if (pending_region_position > drag_info.last_frame_position) {
3198 x_delta = ((double) (pending_region_position - drag_info.last_frame_position) / frames_per_unit);
3200 x_delta = -((double) (drag_info.last_frame_position - pending_region_position) / frames_per_unit);
3203 drag_info.last_frame_position = pending_region_position;
3210 /* threshold not passed */
3215 /*************************************************************
3217 ************************************************************/
3219 if (x_delta == 0 && (pointer_y_span == 0)) {
3220 /* haven't reached next snap point, and we're not switching
3221 trackviews. nothing to do.
3228 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3230 RegionView* rv2 = (*i);
3232 // If any regionview is at zero, we need to know so we can stop further leftward motion.
3234 double ix1, ix2, iy1, iy2;
3235 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3236 rv2->get_canvas_group()->i2w (ix1, iy1);
3245 /*************************************************************
3247 ************************************************************/
3251 if (drag_info.first_move) {
3252 if (drag_info.move_threshold_passed) {
3263 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
3264 const list<RegionView*>& layered_regions = selection->regions.by_layer();
3266 for (list<RegionView*>::const_iterator i = layered_regions.begin(); i != layered_regions.end(); ++i) {
3268 RegionView* rv = (*i);
3269 double ix1, ix2, iy1, iy2;
3270 int32_t temp_pointer_y_span = pointer_y_span;
3272 /* get item BBox, which will be relative to parent. so we have
3273 to query on a child, then convert to world coordinates using
3277 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3278 rv->get_canvas_group()->i2w (ix1, iy1);
3279 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3280 AudioTimeAxisView* canvas_atv = dynamic_cast<AudioTimeAxisView*>(tvp2);
3281 AudioTimeAxisView* temp_atv;
3283 if ((pointer_y_span != 0) && !clamp_y_axis) {
3286 for (j = height_list.begin(); j!= height_list.end(); j++) {
3287 if (x == canvas_atv->order) {
3288 /* we found the track the region is on */
3289 if (x != original_pointer_order) {
3290 /*this isn't from the same track we're dragging from */
3291 temp_pointer_y_span = canvas_pointer_y_span;
3293 while (temp_pointer_y_span > 0) {
3294 /* we're moving up canvas-wise,
3295 so we need to find the next track height
3297 if (j != height_list.begin()) {
3300 if (x != original_pointer_order) {
3301 /* we're not from the dragged track, so ignore hidden tracks. */
3303 temp_pointer_y_span++;
3307 temp_pointer_y_span--;
3309 while (temp_pointer_y_span < 0) {
3311 if (x != original_pointer_order) {
3313 temp_pointer_y_span--;
3317 if (j != height_list.end()) {
3320 temp_pointer_y_span++;
3322 /* find out where we'll be when we move and set height accordingly */
3324 tvp2 = trackview_by_y_position (iy1 + y_delta);
3325 temp_atv = dynamic_cast<AudioTimeAxisView*>(tvp2);
3326 rv->set_height (temp_atv->height);
3328 /* if you un-comment the following, the region colours will follow the track colours whilst dragging,
3329 personally, i think this can confuse things, but never mind.
3332 //const GdkColor& col (temp_atv->view->get_region_color());
3333 //rv->set_color (const_cast<GdkColor&>(col));
3340 /* prevent the regionview from being moved to before
3341 the zero position on the canvas.
3346 if (-x_delta > ix1) {
3349 } else if ((x_delta > 0) && (rv->region()->last_frame() > max_frames - x_delta)) {
3350 x_delta = max_frames - rv->region()->last_frame();
3354 if (drag_info.first_move) {
3356 /* hide any dependent views */
3358 rv->get_time_axis_view().hide_dependent_views (*rv);
3360 /* this is subtle. raising the regionview itself won't help,
3361 because raise_to_top() just puts the item on the top of
3362 its parent's stack. so, we need to put the trackview canvas_display group
3363 on the top, since its parent is the whole canvas.
3366 rv->get_canvas_group()->raise_to_top();
3367 rv->get_time_axis_view().canvas_display->raise_to_top();
3368 cursor_group->raise_to_top();
3369 rv->fake_set_opaque (true);
3372 if (drag_info.brushing) {
3373 mouse_brush_insert_region (rv, pending_region_position);
3375 rv->move (x_delta, y_delta);
3378 } /* foreach region */
3382 if (drag_info.first_move && drag_info.move_threshold_passed) {
3383 cursor_group->raise_to_top();
3384 drag_info.first_move = false;
3387 if (x_delta != 0 && !drag_info.brushing) {
3388 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3393 Editor::region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3396 RegionView* rv = reinterpret_cast<RegionView *> (drag_info.data);
3397 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
3398 bool nocommit = true;
3400 RouteTimeAxisView* atv;
3401 bool regionview_y_movement;
3402 bool regionview_x_movement;
3403 vector<RegionView*> copies;
3405 /* first_move is set to false if the regionview has been moved in the
3409 if (drag_info.first_move) {
3416 /* The regionview has been moved at some stage during the grab so we need
3417 to account for any mouse movement between this event and the last one.
3420 region_drag_motion_callback (item, event);
3422 if (drag_info.brushing) {
3423 /* all changes were made during motion event handlers */
3425 if (drag_info.copy) {
3426 for (list<RegionView*>::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
3427 copies.push_back (*i);
3434 /* adjust for track speed */
3437 atv = dynamic_cast<AudioTimeAxisView*> (drag_info.last_trackview);
3438 if (atv && atv->get_diskstream()) {
3439 speed = atv->get_diskstream()->speed();
3442 regionview_x_movement = (drag_info.last_frame_position != (nframes_t) (rv->region()->position()/speed));
3443 regionview_y_movement = (drag_info.last_trackview != &rv->get_time_axis_view());
3445 //printf ("last_frame: %s position is %lu %g\n", rv->get_time_axis_view().name().c_str(), drag_info.last_frame_position, speed);
3446 //printf ("last_rackview: %s \n", drag_info.last_trackview->name().c_str());
3450 if (drag_info.copy) {
3451 if (drag_info.x_constrained) {
3452 op_string = _("fixed time region copy");
3454 op_string = _("region copy");
3457 if (drag_info.x_constrained) {
3458 op_string = _("fixed time region drag");
3460 op_string = _("region drag");
3464 begin_reversible_command (op_string);
3466 if (regionview_y_movement) {
3468 /* moved to a different audio track. */
3470 vector<RegionView*> new_selection;
3472 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ) {
3474 RegionView* rv = (*i);
3476 double ix1, ix2, iy1, iy2;
3478 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3479 rv->get_canvas_group()->i2w (ix1, iy1);
3480 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3481 AudioTimeAxisView* atv2 = dynamic_cast<AudioTimeAxisView*>(tvp2);
3483 boost::shared_ptr<Playlist> from_playlist = rv->region()->playlist();
3484 boost::shared_ptr<Playlist> to_playlist = atv2->playlist();
3486 where = (nframes_t) (unit_to_frame (ix1) * speed);
3487 boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region()));
3489 /* undo the previous hide_dependent_views so that xfades don't
3490 disappear on copying regions
3493 rv->get_time_axis_view().reveal_dependent_views (*rv);
3495 if (!drag_info.copy) {
3497 /* the region that used to be in the old playlist is not
3498 moved to the new one - we make a copy of it. as a result,
3499 any existing editor for the region should no longer be
3503 rv->hide_region_editor();
3504 rv->fake_set_opaque (false);
3506 session->add_command (new MementoCommand<Playlist>(*from_playlist, &from_playlist->get_state(), 0));
3507 from_playlist->remove_region ((rv->region()));
3508 session->add_command (new MementoCommand<Playlist>(*from_playlist, 0, &from_playlist->get_state()));
3512 /* the regionview we dragged around is a temporary copy, queue it for deletion */
3514 copies.push_back (rv);
3517 latest_regionview = 0;
3519 sigc::connection c = atv2->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3520 session->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
3521 to_playlist->add_region (new_region, where);
3522 session->add_command (new MementoCommand<Playlist>(*to_playlist, 0, &to_playlist->get_state()));
3525 if (latest_regionview) {
3526 new_selection.push_back (latest_regionview);
3529 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
3530 was selected in all of them, then removing it from the playlist will have removed all
3531 trace of it from the selection (i.e. there were N regions selected, we removed 1,
3532 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
3533 corresponding regionview, and the selection is now empty).
3535 this could have invalidated any and all iterators into the region selection.
3537 the heuristic we use here is: if the region selection is empty, break out of the loop
3538 here. if the region selection is not empty, then restart the loop because we know that
3539 we must have removed at least the region(view) we've just been working on as well as any
3540 that we processed on previous iterations.
3542 EXCEPT .... if we are doing a copy drag, then the selection hasn't been modified and
3543 we can just iterate.
3546 if (drag_info.copy) {
3549 if (selection->regions.empty()) {
3552 i = selection->regions.by_layer().begin();
3557 selection->set (new_selection);
3561 /* motion within a single track */
3563 list<RegionView*> regions = selection->regions.by_layer();
3565 if (drag_info.copy) {
3566 selection->clear_regions();
3569 for (list<RegionView*>::iterator i = regions.begin(); i != regions.end(); ++i) {
3573 if (rv->region()->locked()) {
3578 if (regionview_x_movement) {
3579 double ownspeed = 1.0;
3580 atv = dynamic_cast<AudioTimeAxisView*> (&(rv->get_time_axis_view()));
3582 if (atv && atv->get_diskstream()) {
3583 ownspeed = atv->get_diskstream()->speed();
3586 /* base the new region position on the current position of the regionview.*/
3588 double ix1, ix2, iy1, iy2;
3590 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3591 rv->get_canvas_group()->i2w (ix1, iy1);
3592 where = (nframes_t) (unit_to_frame (ix1) * ownspeed);
3596 where = rv->region()->position();
3599 boost::shared_ptr<Playlist> to_playlist = rv->region()->playlist();
3601 assert (to_playlist);
3605 session->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
3607 if (drag_info.copy) {
3609 boost::shared_ptr<Region> newregion;
3610 boost::shared_ptr<Region> ar;
3612 if ((ar = boost::dynamic_pointer_cast<AudioRegion>(rv->region())) != 0) {
3613 newregion = RegionFactory::create (ar);
3615 /* XXX MIDI HERE drobilla */
3621 latest_regionview = 0;
3622 sigc::connection c = atv->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3623 to_playlist->add_region (newregion, (nframes_t) (where * atv->get_diskstream()->speed()));
3626 if (latest_regionview) {
3627 atv->reveal_dependent_views (*latest_regionview);
3628 selection->add (latest_regionview);
3631 /* if the original region was locked, we don't care for the new one */
3633 newregion->set_locked (false);
3637 /* just change the model */
3639 rv->region()->set_position (where, (void*) this);
3645 session->add_command (new MementoCommand<Playlist>(*to_playlist, 0, &to_playlist->get_state()));
3647 if (drag_info.copy) {
3648 copies.push_back (rv);
3656 commit_reversible_command ();
3659 for (vector<RegionView*>::iterator x = copies.begin(); x != copies.end(); ++x) {
3665 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
3667 /* Either add to or set the set the region selection, unless
3668 this is an alignment click (control used)
3671 if (Keyboard::modifier_state_contains (event->state, Keyboard::Control)) {
3672 TimeAxisView* tv = &rv.get_time_axis_view();
3673 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*>(tv);
3675 if (atv && atv->is_audio_track()) {
3676 speed = atv->get_diskstream()->speed();
3679 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Alt))) {
3681 align_region (rv.region(), SyncPoint, (nframes_t) (edit_cursor->current_frame * speed));
3683 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Shift))) {
3685 align_region (rv.region(), End, (nframes_t) (edit_cursor->current_frame * speed));
3689 align_region (rv.region(), Start, (nframes_t) (edit_cursor->current_frame * speed));
3695 Editor::show_verbose_time_cursor (nframes_t frame, double offset, double xpos, double ypos)
3701 nframes_t frame_rate;
3708 switch (Profile->get_small_screen() ? ARDOUR_UI::instance()->primary_clock.mode () : ARDOUR_UI::instance()->secondary_clock.mode ()) {
3709 case AudioClock::BBT:
3710 session->bbt_time (frame, bbt);
3711 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, bbt.bars, bbt.beats, bbt.ticks);
3714 case AudioClock::SMPTE:
3715 session->smpte_time (frame, smpte);
3716 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
3719 case AudioClock::MinSec:
3720 /* XXX this is copied from show_verbose_duration_cursor() */
3721 frame_rate = session->frame_rate();
3722 hours = frame / (frame_rate * 3600);
3723 frame = frame % (frame_rate * 3600);
3724 mins = frame / (frame_rate * 60);
3725 frame = frame % (frame_rate * 60);
3726 secs = (float) frame / (float) frame_rate;
3727 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
3731 snprintf (buf, sizeof(buf), "%u", frame);
3735 if (xpos >= 0 && ypos >=0) {
3736 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
3739 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
3741 show_verbose_canvas_cursor ();
3745 Editor::show_verbose_duration_cursor (nframes_t start, nframes_t end, double offset, double xpos, double ypos)
3752 nframes_t distance, frame_rate;
3754 Meter meter_at_start(session->tempo_map().meter_at(start));
3760 switch (ARDOUR_UI::instance()->secondary_clock.mode ()) {
3761 case AudioClock::BBT:
3762 session->bbt_time (start, sbbt);
3763 session->bbt_time (end, ebbt);
3766 /* XXX this computation won't work well if the
3767 user makes a selection that spans any meter changes.
3770 ebbt.bars -= sbbt.bars;
3771 if (ebbt.beats >= sbbt.beats) {
3772 ebbt.beats -= sbbt.beats;
3775 ebbt.beats = int(meter_at_start.beats_per_bar()) + ebbt.beats - sbbt.beats;
3777 if (ebbt.ticks >= sbbt.ticks) {
3778 ebbt.ticks -= sbbt.ticks;
3781 ebbt.ticks = int(Meter::ticks_per_beat) + ebbt.ticks - sbbt.ticks;
3784 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, ebbt.bars, ebbt.beats, ebbt.ticks);
3787 case AudioClock::SMPTE:
3788 session->smpte_duration (end - start, smpte);
3789 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
3792 case AudioClock::MinSec:
3793 /* XXX this stuff should be elsewhere.. */
3794 distance = end - start;
3795 frame_rate = session->frame_rate();
3796 hours = distance / (frame_rate * 3600);
3797 distance = distance % (frame_rate * 3600);
3798 mins = distance / (frame_rate * 60);
3799 distance = distance % (frame_rate * 60);
3800 secs = (float) distance / (float) frame_rate;
3801 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
3805 snprintf (buf, sizeof(buf), "%u", end - start);
3809 if (xpos >= 0 && ypos >=0) {
3810 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
3813 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
3815 show_verbose_canvas_cursor ();
3819 Editor::collect_new_region_view (RegionView* rv)
3821 latest_regionview = rv;
3825 Editor::start_selection_grab (ArdourCanvas::Item* item, GdkEvent* event)
3827 if (clicked_regionview == 0) {
3831 /* lets try to create new Region for the selection */
3833 vector<boost::shared_ptr<AudioRegion> > new_regions;
3834 create_region_from_selection (new_regions);
3836 if (new_regions.empty()) {
3840 /* XXX fix me one day to use all new regions */
3842 boost::shared_ptr<Region> region (new_regions.front());
3844 /* add it to the current stream/playlist.
3846 tricky: the streamview for the track will add a new regionview. we will
3847 catch the signal it sends when it creates the regionview to
3848 set the regionview we want to then drag.
3851 latest_regionview = 0;
3852 sigc::connection c = clicked_audio_trackview->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3854 /* A selection grab currently creates two undo/redo operations, one for
3855 creating the new region and another for moving it.
3858 begin_reversible_command (_("selection grab"));
3860 boost::shared_ptr<Playlist> playlist = clicked_trackview->playlist();
3862 XMLNode *before = &(playlist->get_state());
3863 clicked_trackview->playlist()->add_region (region, selection->time[clicked_selection].start);
3864 XMLNode *after = &(playlist->get_state());
3865 session->add_command(new MementoCommand<Playlist>(*playlist, before, after));
3867 commit_reversible_command ();
3871 if (latest_regionview == 0) {
3872 /* something went wrong */
3876 /* we need to deselect all other regionviews, and select this one
3877 i'm ignoring undo stuff, because the region creation will take care of it */
3878 selection->set (latest_regionview);
3880 drag_info.item = latest_regionview->get_canvas_group();
3881 drag_info.data = latest_regionview;
3882 drag_info.motion_callback = &Editor::region_drag_motion_callback;
3883 drag_info.finished_callback = &Editor::region_drag_finished_callback;
3887 drag_info.last_trackview = clicked_trackview;
3888 drag_info.last_frame_position = latest_regionview->region()->position();
3889 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
3891 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3895 Editor::cancel_selection ()
3897 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
3898 (*i)->hide_selection ();
3900 begin_reversible_command (_("cancel selection"));
3901 selection->clear ();
3902 clicked_selection = 0;
3903 commit_reversible_command ();
3907 Editor::start_selection_op (ArdourCanvas::Item* item, GdkEvent* event, SelectionOp op)
3909 nframes_t start = 0;
3916 drag_info.item = item;
3917 drag_info.motion_callback = &Editor::drag_selection;
3918 drag_info.finished_callback = &Editor::end_selection_op;
3923 case CreateSelection:
3924 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
3925 drag_info.copy = true;
3927 drag_info.copy = false;
3929 start_grab (event, selector_cursor);
3932 case SelectionStartTrim:
3933 if (clicked_trackview) {
3934 clicked_trackview->order_selection_trims (item, true);
3936 start_grab (event, trimmer_cursor);
3937 start = selection->time[clicked_selection].start;
3938 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
3941 case SelectionEndTrim:
3942 if (clicked_trackview) {
3943 clicked_trackview->order_selection_trims (item, false);
3945 start_grab (event, trimmer_cursor);
3946 end = selection->time[clicked_selection].end;
3947 drag_info.pointer_frame_offset = drag_info.grab_frame - end;
3951 start = selection->time[clicked_selection].start;
3953 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
3957 if (selection_op == SelectionMove) {
3958 show_verbose_time_cursor(start, 10);
3960 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
3965 Editor::drag_selection (ArdourCanvas::Item* item, GdkEvent* event)
3967 nframes_t start = 0;
3970 nframes_t pending_position;
3972 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
3973 pending_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
3975 pending_position = 0;
3978 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3979 snap_to (pending_position);
3982 /* only alter selection if the current frame is
3983 different from the last frame position (adjusted)
3986 if (pending_position == drag_info.last_pointer_frame) return;
3988 switch (selection_op) {
3989 case CreateSelection:
3991 if (drag_info.first_move) {
3992 snap_to (drag_info.grab_frame);
3995 if (pending_position < drag_info.grab_frame) {
3996 start = pending_position;
3997 end = drag_info.grab_frame;
3999 end = pending_position;
4000 start = drag_info.grab_frame;
4003 /* first drag: Either add to the selection
4004 or create a new selection->
4007 if (drag_info.first_move) {
4009 begin_reversible_command (_("range selection"));
4011 if (drag_info.copy) {
4012 /* adding to the selection */
4013 clicked_selection = selection->add (start, end);
4014 drag_info.copy = false;
4016 /* new selection-> */
4017 clicked_selection = selection->set (clicked_trackview, start, end);
4022 case SelectionStartTrim:
4024 if (drag_info.first_move) {
4025 begin_reversible_command (_("trim selection start"));
4028 start = selection->time[clicked_selection].start;
4029 end = selection->time[clicked_selection].end;
4031 if (pending_position > end) {
4034 start = pending_position;
4038 case SelectionEndTrim:
4040 if (drag_info.first_move) {
4041 begin_reversible_command (_("trim selection end"));
4044 start = selection->time[clicked_selection].start;
4045 end = selection->time[clicked_selection].end;
4047 if (pending_position < start) {
4050 end = pending_position;
4057 if (drag_info.first_move) {
4058 begin_reversible_command (_("move selection"));
4061 start = selection->time[clicked_selection].start;
4062 end = selection->time[clicked_selection].end;
4064 length = end - start;
4066 start = pending_position;
4069 end = start + length;
4074 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
4075 start_canvas_autoscroll (1);
4079 selection->replace (clicked_selection, start, end);
4082 drag_info.last_pointer_frame = pending_position;
4083 drag_info.first_move = false;
4085 if (selection_op == SelectionMove) {
4086 show_verbose_time_cursor(start, 10);
4088 show_verbose_time_cursor(pending_position, 10);
4093 Editor::end_selection_op (ArdourCanvas::Item* item, GdkEvent* event)
4095 if (!drag_info.first_move) {
4096 drag_selection (item, event);
4097 /* XXX this is not object-oriented programming at all. ick */
4098 if (selection->time.consolidate()) {
4099 selection->TimeChanged ();
4101 commit_reversible_command ();
4103 /* just a click, no pointer movement.*/
4105 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
4107 selection->clear_time();
4112 /* XXX what happens if its a music selection? */
4113 session->set_audio_range (selection->time);
4114 stop_canvas_autoscroll ();
4118 Editor::start_trim (ArdourCanvas::Item* item, GdkEvent* event)
4121 TimeAxisView* tvp = clicked_trackview;
4122 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
4124 if (tv && tv->is_audio_track()) {
4125 speed = tv->get_diskstream()->speed();
4128 nframes_t region_start = (nframes_t) (clicked_regionview->region()->position() / speed);
4129 nframes_t region_end = (nframes_t) (clicked_regionview->region()->last_frame() / speed);
4130 nframes_t region_length = (nframes_t) (clicked_regionview->region()->length() / speed);
4132 //drag_info.item = clicked_regionview->get_name_highlight();
4133 drag_info.item = item;
4134 drag_info.motion_callback = &Editor::trim_motion_callback;
4135 drag_info.finished_callback = &Editor::trim_finished_callback;
4137 start_grab (event, trimmer_cursor);
4139 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
4140 trim_op = ContentsTrim;
4142 /* These will get overridden for a point trim.*/
4143 if (drag_info.current_pointer_frame < (region_start + region_length/2)) {
4144 /* closer to start */
4145 trim_op = StartTrim;
4146 } else if (drag_info.current_pointer_frame > (region_end - region_length/2)) {
4154 show_verbose_time_cursor(region_start, 10);
4157 show_verbose_time_cursor(region_end, 10);
4160 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4166 Editor::trim_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
4168 RegionView* rv = clicked_regionview;
4169 nframes_t frame_delta = 0;
4170 bool left_direction;
4171 bool obey_snap = !Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier());
4173 /* snap modifier works differently here..
4174 its' current state has to be passed to the
4175 various trim functions in order to work properly
4179 TimeAxisView* tvp = clicked_trackview;
4180 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4181 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
4183 if (tv && tv->is_audio_track()) {
4184 speed = tv->get_diskstream()->speed();
4187 if (drag_info.last_pointer_frame > drag_info.current_pointer_frame) {
4188 left_direction = true;
4190 left_direction = false;
4194 snap_to (drag_info.current_pointer_frame);
4197 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
4201 if (drag_info.first_move) {
4207 trim_type = "Region start trim";
4210 trim_type = "Region end trim";
4213 trim_type = "Region content trim";
4217 begin_reversible_command (trim_type);
4219 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4220 (*i)->fake_set_opaque(false);
4221 (*i)->region()->freeze ();
4223 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
4225 arv->temporarily_hide_envelope ();
4227 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4228 insert_result = motion_frozen_playlists.insert (pl);
4229 if (insert_result.second) {
4230 session->add_command(new MementoCommand<Playlist>(*pl, &pl->get_state(), 0));
4235 if (left_direction) {
4236 frame_delta = (drag_info.last_pointer_frame - drag_info.current_pointer_frame);
4238 frame_delta = (drag_info.current_pointer_frame - drag_info.last_pointer_frame);
4243 if ((left_direction == false) && (drag_info.current_pointer_frame <= rv->region()->first_frame()/speed)) {
4246 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4247 single_start_trim (**i, frame_delta, left_direction, obey_snap);
4253 if ((left_direction == true) && (drag_info.current_pointer_frame > (nframes_t) (rv->region()->last_frame()/speed))) {
4256 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4257 single_end_trim (**i, frame_delta, left_direction, obey_snap);
4264 bool swap_direction = false;
4266 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
4267 swap_direction = true;
4270 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4271 i != selection->regions.by_layer().end(); ++i)
4273 single_contents_trim (**i, frame_delta, left_direction, swap_direction, obey_snap);
4281 show_verbose_time_cursor((nframes_t) (rv->region()->position()/speed), 10);
4284 show_verbose_time_cursor((nframes_t) (rv->region()->last_frame()/speed), 10);
4287 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4291 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4292 drag_info.first_move = false;
4296 Editor::single_contents_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool swap_direction, bool obey_snap)
4298 boost::shared_ptr<Region> region (rv.region());
4300 if (region->locked()) {
4304 nframes_t new_bound;
4307 TimeAxisView* tvp = clicked_trackview;
4308 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4310 if (tv && tv->is_audio_track()) {
4311 speed = tv->get_diskstream()->speed();
4314 if (left_direction) {
4315 if (swap_direction) {
4316 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4318 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4321 if (swap_direction) {
4322 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4324 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4329 snap_to (new_bound);
4331 region->trim_start ((nframes_t) (new_bound * speed), this);
4332 rv.region_changed (StartChanged);
4336 Editor::single_start_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool obey_snap)
4338 boost::shared_ptr<Region> region (rv.region());
4340 if (region->locked()) {
4344 nframes_t new_bound;
4347 TimeAxisView* tvp = clicked_trackview;
4348 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
4350 if (tv && tv->is_audio_track()) {
4351 speed = tv->get_diskstream()->speed();
4354 if (left_direction) {
4355 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4357 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4361 snap_to (new_bound, (left_direction ? 0 : 1));
4364 region->trim_front ((nframes_t) (new_bound * speed), this);
4366 rv.region_changed (Change (LengthChanged|PositionChanged|StartChanged));
4370 Editor::single_end_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool obey_snap)
4372 boost::shared_ptr<Region> region (rv.region());
4374 if (region->locked()) {
4378 nframes_t new_bound;
4381 TimeAxisView* tvp = clicked_trackview;
4382 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
4384 if (tv && tv->is_audio_track()) {
4385 speed = tv->get_diskstream()->speed();
4388 if (left_direction) {
4389 new_bound = (nframes_t) ((region->last_frame() + 1)/speed) - frame_delta;
4391 new_bound = (nframes_t) ((region->last_frame() + 1)/speed) + frame_delta;
4395 snap_to (new_bound);
4397 region->trim_end ((nframes_t) (new_bound * speed), this);
4398 rv.region_changed (LengthChanged);
4402 Editor::trim_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
4404 if (!drag_info.first_move) {
4405 trim_motion_callback (item, event);
4407 if (!clicked_regionview->get_selected()) {
4408 thaw_region_after_trim (*clicked_regionview);
4411 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4412 i != selection->regions.by_layer().end(); ++i)
4414 thaw_region_after_trim (**i);
4415 (*i)->fake_set_opaque (true);
4419 for (set<boost::shared_ptr<Playlist> >::iterator p = motion_frozen_playlists.begin(); p != motion_frozen_playlists.end(); ++p) {
4421 session->add_command (new MementoCommand<Playlist>(*(*p).get(), 0, &(*p)->get_state()));
4424 motion_frozen_playlists.clear ();
4426 commit_reversible_command();
4428 /* no mouse movement */
4434 Editor::point_trim (GdkEvent* event)
4436 RegionView* rv = clicked_regionview;
4437 nframes_t new_bound = drag_info.current_pointer_frame;
4439 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4440 snap_to (new_bound);
4443 /* Choose action dependant on which button was pressed */
4444 switch (event->button.button) {
4446 trim_op = StartTrim;
4447 begin_reversible_command (_("Start point trim"));
4449 if (rv->get_selected()) {
4451 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4452 i != selection->regions.by_layer().end(); ++i)
4454 if (!(*i)->region()->locked()) {
4455 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4456 XMLNode &before = pl->get_state();
4457 (*i)->region()->trim_front (new_bound, this);
4458 XMLNode &after = pl->get_state();
4459 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4465 if (!rv->region()->locked()) {
4466 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
4467 XMLNode &before = pl->get_state();
4468 rv->region()->trim_front (new_bound, this);
4469 XMLNode &after = pl->get_state();
4470 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4474 commit_reversible_command();
4479 begin_reversible_command (_("End point trim"));
4481 if (rv->get_selected()) {
4483 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
4485 if (!(*i)->region()->locked()) {
4486 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4487 XMLNode &before = pl->get_state();
4488 (*i)->region()->trim_end (new_bound, this);
4489 XMLNode &after = pl->get_state();
4490 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4496 if (!rv->region()->locked()) {
4497 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
4498 XMLNode &before = pl->get_state();
4499 rv->region()->trim_end (new_bound, this);
4500 XMLNode &after = pl->get_state();
4501 session->add_command (new MementoCommand<Playlist>(*pl.get(), &before, &after));
4505 commit_reversible_command();
4514 Editor::thaw_region_after_trim (RegionView& rv)
4516 boost::shared_ptr<Region> region (rv.region());
4518 if (region->locked()) {
4522 region->thaw (_("trimmed region"));
4523 XMLNode &after = region->playlist()->get_state();
4524 session->add_command (new MementoCommand<Playlist>(*(region->playlist()), 0, &after));
4526 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(&rv);
4528 arv->unhide_envelope ();
4532 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* event)
4537 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
4538 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
4542 Location* location = find_location_from_marker (marker, is_start);
4543 location->set_hidden (true, this);
4548 Editor::start_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event, RangeMarkerOp op)
4554 drag_info.item = item;
4555 drag_info.motion_callback = &Editor::drag_range_markerbar_op;
4556 drag_info.finished_callback = &Editor::end_range_markerbar_op;
4558 range_marker_op = op;
4560 if (!temp_location) {
4561 temp_location = new Location;
4565 case CreateRangeMarker:
4566 case CreateTransportMarker:
4568 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
4569 drag_info.copy = true;
4571 drag_info.copy = false;
4573 start_grab (event, selector_cursor);
4577 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4582 Editor::drag_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4584 nframes_t start = 0;
4586 ArdourCanvas::SimpleRect *crect = (range_marker_op == CreateRangeMarker) ? range_bar_drag_rect: transport_bar_drag_rect;
4588 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4589 snap_to (drag_info.current_pointer_frame);
4592 /* only alter selection if the current frame is
4593 different from the last frame position.
4596 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4598 switch (range_marker_op) {
4599 case CreateRangeMarker:
4600 case CreateTransportMarker:
4601 if (drag_info.first_move) {
4602 snap_to (drag_info.grab_frame);
4605 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4606 start = drag_info.current_pointer_frame;
4607 end = drag_info.grab_frame;
4609 end = drag_info.current_pointer_frame;
4610 start = drag_info.grab_frame;
4613 /* first drag: Either add to the selection
4614 or create a new selection.
4617 if (drag_info.first_move) {
4619 temp_location->set (start, end);
4623 update_marker_drag_item (temp_location);
4624 range_marker_drag_rect->show();
4625 range_marker_drag_rect->raise_to_top();
4631 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
4632 start_canvas_autoscroll (1);
4636 temp_location->set (start, end);
4638 double x1 = frame_to_pixel (start);
4639 double x2 = frame_to_pixel (end);
4640 crect->property_x1() = x1;
4641 crect->property_x2() = x2;
4643 update_marker_drag_item (temp_location);
4646 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4647 drag_info.first_move = false;
4649 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4654 Editor::end_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4656 Location * newloc = 0;
4659 if (!drag_info.first_move) {
4660 drag_range_markerbar_op (item, event);
4662 switch (range_marker_op) {
4663 case CreateRangeMarker:
4665 begin_reversible_command (_("new range marker"));
4666 XMLNode &before = session->locations()->get_state();
4667 session->locations()->next_available_name(rangename,"unnamed");
4668 newloc = new Location(temp_location->start(), temp_location->end(), rangename, Location::IsRangeMarker);
4669 session->locations()->add (newloc, true);
4670 XMLNode &after = session->locations()->get_state();
4671 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
4672 commit_reversible_command ();
4674 range_bar_drag_rect->hide();
4675 range_marker_drag_rect->hide();
4679 case CreateTransportMarker:
4680 // popup menu to pick loop or punch
4681 new_transport_marker_context_menu (&event->button, item);
4686 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
4688 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
4693 start = session->locations()->first_mark_before (drag_info.grab_frame);
4694 end = session->locations()->first_mark_after (drag_info.grab_frame);
4696 if (end == max_frames) {
4697 end = session->current_end_frame ();
4701 start = session->current_start_frame ();
4704 switch (mouse_mode) {
4706 /* find the two markers on either side and then make the selection from it */
4707 select_all_within (start, end, 0.0f, FLT_MAX, track_views, Selection::Set);
4711 /* find the two markers on either side of the click and make the range out of it */
4712 selection->set (0, start, end);
4721 stop_canvas_autoscroll ();
4727 Editor::start_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4729 drag_info.item = item;
4730 drag_info.motion_callback = &Editor::drag_mouse_zoom;
4731 drag_info.finished_callback = &Editor::end_mouse_zoom;
4733 start_grab (event, zoom_cursor);
4735 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4739 Editor::drag_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4744 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4745 snap_to (drag_info.current_pointer_frame);
4747 if (drag_info.first_move) {
4748 snap_to (drag_info.grab_frame);
4752 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4754 /* base start and end on initial click position */
4755 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4756 start = drag_info.current_pointer_frame;
4757 end = drag_info.grab_frame;
4759 end = drag_info.current_pointer_frame;
4760 start = drag_info.grab_frame;
4765 if (drag_info.first_move) {
4767 zoom_rect->raise_to_top();
4770 reposition_zoom_rect(start, end);
4772 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4773 drag_info.first_move = false;
4775 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4780 Editor::end_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4782 if (!drag_info.first_move) {
4783 drag_mouse_zoom (item, event);
4785 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
4786 temporal_zoom_by_frame (drag_info.grab_frame, drag_info.last_pointer_frame, "mouse zoom");
4788 temporal_zoom_by_frame (drag_info.last_pointer_frame, drag_info.grab_frame, "mouse zoom");
4791 temporal_zoom_to_frame (false, drag_info.grab_frame);
4793 temporal_zoom_step (false);
4794 center_screen (drag_info.grab_frame);
4802 Editor::reposition_zoom_rect (nframes_t start, nframes_t end)
4804 double x1 = frame_to_pixel (start);
4805 double x2 = frame_to_pixel (end);
4806 double y2 = full_canvas_height - 1.0;
4808 zoom_rect->property_x1() = x1;
4809 zoom_rect->property_y1() = 1.0;
4810 zoom_rect->property_x2() = x2;
4811 zoom_rect->property_y2() = y2;
4815 Editor::start_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4817 drag_info.item = item;
4818 drag_info.motion_callback = &Editor::drag_rubberband_select;
4819 drag_info.finished_callback = &Editor::end_rubberband_select;
4821 start_grab (event, cross_hair_cursor);
4823 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4827 Editor::drag_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4834 /* use a bigger drag threshold than the default */
4836 if (abs ((int) (drag_info.current_pointer_frame - drag_info.grab_frame)) < 8) {
4840 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4841 if (drag_info.first_move) {
4842 snap_to (drag_info.grab_frame);
4844 snap_to (drag_info.current_pointer_frame);
4847 /* base start and end on initial click position */
4849 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4850 start = drag_info.current_pointer_frame;
4851 end = drag_info.grab_frame;
4853 end = drag_info.current_pointer_frame;
4854 start = drag_info.grab_frame;
4857 if (drag_info.current_pointer_y < drag_info.grab_y) {
4858 y1 = drag_info.current_pointer_y;
4859 y2 = drag_info.grab_y;
4861 y2 = drag_info.current_pointer_y;
4862 y1 = drag_info.grab_y;
4866 if (start != end || y1 != y2) {
4868 double x1 = frame_to_pixel (start);
4869 double x2 = frame_to_pixel (end);
4871 rubberband_rect->property_x1() = x1;
4872 rubberband_rect->property_y1() = y1;
4873 rubberband_rect->property_x2() = x2;
4874 rubberband_rect->property_y2() = y2;
4876 rubberband_rect->show();
4877 rubberband_rect->raise_to_top();
4879 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4880 drag_info.first_move = false;
4882 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4887 Editor::end_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4889 if (!drag_info.first_move) {
4891 drag_rubberband_select (item, event);
4894 if (drag_info.current_pointer_y < drag_info.grab_y) {
4895 y1 = drag_info.current_pointer_y;
4896 y2 = drag_info.grab_y;
4899 y2 = drag_info.current_pointer_y;
4900 y1 = drag_info.grab_y;
4904 Selection::Operation op = Keyboard::selection_type (event->button.state);
4907 begin_reversible_command (_("rubberband selection"));
4909 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
4910 commit = select_all_within (drag_info.grab_frame, drag_info.last_pointer_frame, y1, y2, track_views, op);
4912 commit = select_all_within (drag_info.last_pointer_frame, drag_info.grab_frame, y1, y2, track_views, op);
4916 commit_reversible_command ();
4920 selection->clear_tracks();
4921 selection->clear_regions();
4922 selection->clear_points ();
4923 selection->clear_lines ();
4926 rubberband_rect->hide();
4931 Editor::mouse_rename_region (ArdourCanvas::Item* item, GdkEvent* event)
4933 using namespace Gtkmm2ext;
4935 ArdourPrompter prompter (false);
4937 prompter.set_prompt (_("Name for region:"));
4938 prompter.set_initial_text (clicked_regionview->region()->name());
4939 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
4940 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
4941 prompter.show_all ();
4942 switch (prompter.run ()) {
4943 case Gtk::RESPONSE_ACCEPT:
4945 prompter.get_result(str);
4947 clicked_regionview->region()->set_name (str);
4955 Editor::start_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
4957 drag_info.item = item;
4958 drag_info.motion_callback = &Editor::time_fx_motion;
4959 drag_info.finished_callback = &Editor::end_time_fx;
4963 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4967 Editor::time_fx_motion (ArdourCanvas::Item *item, GdkEvent* event)
4969 RegionView* rv = clicked_regionview;
4971 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4972 snap_to (drag_info.current_pointer_frame);
4975 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
4979 if (drag_info.current_pointer_frame > rv->region()->position()) {
4980 rv->get_time_axis_view().show_timestretch (rv->region()->position(), drag_info.current_pointer_frame);
4983 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4984 drag_info.first_move = false;
4986 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4990 Editor::end_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
4992 clicked_regionview->get_time_axis_view().hide_timestretch ();
4994 if (drag_info.first_move) {
4998 if (drag_info.last_pointer_frame < clicked_regionview->region()->position()) {
4999 /* backwards drag of the left edge - not usable */
5003 nframes_t newlen = drag_info.last_pointer_frame - clicked_regionview->region()->position();
5004 float percentage = (float) ((double) newlen - (double) clicked_regionview->region()->length()) / ((double) newlen) * 100.0f;
5006 begin_reversible_command (_("timestretch"));
5008 if (run_timestretch (selection->regions, percentage) == 0) {
5009 session->commit_reversible_command ();
5014 Editor::mouse_brush_insert_region (RegionView* rv, nframes_t pos)
5016 /* no brushing without a useful snap setting */
5019 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
5022 switch (snap_mode) {
5024 return; /* can't work because it allows region to be placed anywhere */
5029 switch (snap_type) {
5032 case SnapToEditCursor:
5039 /* don't brush a copy over the original */
5041 if (pos == rv->region()->position()) {
5045 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*>(&arv->get_time_axis_view());
5047 if (atv == 0 || !atv->is_audio_track()) {
5051 boost::shared_ptr<Playlist> playlist = atv->playlist();
5052 double speed = atv->get_diskstream()->speed();
5054 XMLNode &before = playlist->get_state();
5055 playlist->add_region (boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (arv->audio_region())), (nframes_t) (pos * speed));
5056 XMLNode &after = playlist->get_state();
5057 session->add_command(new MementoCommand<Playlist>(*playlist.get(), &before, &after));
5059 // playlist is frozen, so we have to update manually
5061 playlist->Modified(); /* EMIT SIGNAL */
5065 Editor::track_height_step_timeout ()
5068 struct timeval delta;
5070 gettimeofday (&now, 0);
5071 timersub (&now, &last_track_height_step_timestamp, &delta);
5073 if (delta.tv_sec * 1000000 + delta.tv_usec > 250000) { /* milliseconds */
5074 current_stepping_trackview = 0;