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.
28 #include <pbd/error.h>
29 #include <gtkmm2ext/utils.h>
30 #include <pbd/memento_command.h>
32 #include "ardour_ui.h"
34 #include "time_axis_view.h"
35 #include "audio_time_axis.h"
36 #include "audio_region_view.h"
37 #include "midi_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/midi_region.h>
58 #include <ardour/dB.h>
59 #include <ardour/utils.h>
60 #include <ardour/region_factory.h>
67 using namespace ARDOUR;
71 using namespace Editing;
74 Editor::event_frame (GdkEvent* event, double* pcx, double* pcy)
88 switch (event->type) {
89 case GDK_BUTTON_RELEASE:
90 case GDK_BUTTON_PRESS:
91 case GDK_2BUTTON_PRESS:
92 case GDK_3BUTTON_PRESS:
93 track_canvas.w2c(event->button.x, event->button.y, *pcx, *pcy);
95 case GDK_MOTION_NOTIFY:
96 track_canvas.w2c(event->motion.x, event->motion.y, *pcx, *pcy);
98 case GDK_ENTER_NOTIFY:
99 case GDK_LEAVE_NOTIFY:
100 track_canvas.w2c(event->crossing.x, event->crossing.y, *pcx, *pcy);
103 case GDK_KEY_RELEASE:
104 // track_canvas.w2c(event->key.x, event->key.y, *pcx, *pcy);
107 warning << string_compose (_("Editor::event_frame() used on unhandled event type %1"), event->type) << endmsg;
111 /* note that pixel_to_frame() never returns less than zero, so even if the pixel
112 position is negative (as can be the case with motion events in particular),
113 the frame location is always positive.
116 return pixel_to_frame (*pcx);
120 Editor::mouse_mode_toggled (MouseMode m)
122 if (ignore_mouse_mode_toggle) {
128 if (mouse_select_button.get_active()) {
134 if (mouse_move_button.get_active()) {
140 if (mouse_gain_button.get_active()) {
146 if (mouse_zoom_button.get_active()) {
152 if (mouse_timefx_button.get_active()) {
158 if (mouse_audition_button.get_active()) {
169 Editor::set_mouse_mode (MouseMode m, bool force)
171 if (drag_info.item) {
175 if (!force && m == mouse_mode) {
183 if (mouse_mode != MouseRange) {
185 /* in all modes except range, hide the range selection,
186 show the object (region) selection.
189 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
190 (*i)->set_should_show_selection (true);
192 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
193 (*i)->hide_selection ();
199 in range mode,show the range selection.
202 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
203 if ((*i)->get_selected()) {
204 (*i)->show_selection (selection->time);
209 /* XXX the hack of unsetting all other buttongs should go
210 away once GTK2 allows us to use regular radio buttons drawn like
211 normal buttons, rather than my silly GroupedButton hack.
214 ignore_mouse_mode_toggle = true;
216 switch (mouse_mode) {
218 mouse_select_button.set_active (true);
219 current_canvas_cursor = selector_cursor;
223 mouse_move_button.set_active (true);
224 current_canvas_cursor = grabber_cursor;
228 mouse_gain_button.set_active (true);
229 current_canvas_cursor = cross_hair_cursor;
233 mouse_zoom_button.set_active (true);
234 current_canvas_cursor = zoom_cursor;
238 mouse_timefx_button.set_active (true);
239 current_canvas_cursor = time_fx_cursor; // just use playhead
243 mouse_audition_button.set_active (true);
244 current_canvas_cursor = speaker_cursor;
248 ignore_mouse_mode_toggle = false;
251 track_canvas.get_window()->set_cursor(*current_canvas_cursor);
256 Editor::step_mouse_mode (bool next)
258 switch (current_mouse_mode()) {
260 if (next) set_mouse_mode (MouseRange);
261 else set_mouse_mode (MouseTimeFX);
265 if (next) set_mouse_mode (MouseZoom);
266 else set_mouse_mode (MouseObject);
270 if (next) set_mouse_mode (MouseGain);
271 else set_mouse_mode (MouseRange);
275 if (next) set_mouse_mode (MouseTimeFX);
276 else set_mouse_mode (MouseZoom);
280 if (next) set_mouse_mode (MouseAudition);
281 else set_mouse_mode (MouseGain);
285 if (next) set_mouse_mode (MouseObject);
286 else set_mouse_mode (MouseTimeFX);
292 Editor::button_selection (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
296 /* in object/audition/timefx mode, any button press sets
297 the selection if the object can be selected. this is a
298 bit of hack, because we want to avoid this if the
299 mouse operation is a region alignment.
301 note: not dbl-click or triple-click
304 if (((mouse_mode != MouseObject) &&
305 (mouse_mode != MouseAudition || item_type != RegionItem) &&
306 (mouse_mode != MouseTimeFX || item_type != RegionItem) &&
307 (mouse_mode != MouseRange)) ||
309 (event->type != GDK_BUTTON_PRESS && event->type != GDK_BUTTON_RELEASE || event->button.button > 3)) {
314 if (event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) {
316 if ((event->button.state & Keyboard::RelevantModifierKeyMask) && event->button.button != 1) {
318 /* almost no selection action on modified button-2 or button-3 events */
320 if (item_type != RegionItem && event->button.button != 2) {
326 Selection::Operation op = Keyboard::selection_type (event->button.state);
327 bool press = (event->type == GDK_BUTTON_PRESS);
329 // begin_reversible_command (_("select on click"));
333 case RegionViewNameHighlight:
335 case FadeInHandleItem:
337 case FadeOutHandleItem:
339 if (mouse_mode != MouseRange) {
340 commit = set_selected_regionview_from_click (press, op, true);
341 } else if (event->type == GDK_BUTTON_PRESS) {
342 commit = set_selected_track_from_click (press, op, false);
346 case CrossfadeViewItem:
347 commit = set_selected_track_from_click (press, op, false);
350 case GainAutomationControlPointItem:
351 case PanAutomationControlPointItem:
352 case RedirectAutomationControlPointItem:
353 commit = set_selected_track_from_click (press, op, true);
354 if (mouse_mode != MouseRange) {
355 commit |= set_selected_control_point_from_click (op, false);
360 /* for context click or range selection, select track */
361 if (event->button.button == 3) {
362 commit = set_selected_track_from_click (press, op, true);
363 } else if (event->type == GDK_BUTTON_PRESS && mouse_mode == MouseRange) {
364 commit = set_selected_track_from_click (press, op, false);
368 case AutomationTrackItem:
369 commit = set_selected_track_from_click (press, op, true);
377 // commit_reversible_command ();
382 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
384 track_canvas.grab_focus();
386 if (session && session->actively_recording()) {
390 button_selection (item, event, item_type);
392 if (drag_info.item == 0 &&
393 (Keyboard::is_delete_event (&event->button) ||
394 Keyboard::is_context_menu_event (&event->button) ||
395 Keyboard::is_edit_event (&event->button))) {
397 /* handled by button release */
401 switch (event->button.button) {
404 if (event->type == GDK_BUTTON_PRESS) {
406 if (drag_info.item) {
407 drag_info.item->ungrab (event->button.time);
410 /* single mouse clicks on any of these item types operate
411 independent of mouse mode, mostly because they are
412 not on the main track canvas or because we want
418 case PlayheadCursorItem:
419 start_cursor_grab (item, event);
423 if (Keyboard::modifier_state_equals (event->button.state,
424 Keyboard::ModifierMask(Keyboard::Control|Keyboard::Shift))) {
425 hide_marker (item, event);
427 start_marker_grab (item, event);
431 case TempoMarkerItem:
432 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
433 start_tempo_marker_copy_grab (item, event);
435 start_tempo_marker_grab (item, event);
439 case MeterMarkerItem:
440 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
441 start_meter_marker_copy_grab (item, event);
443 start_meter_marker_grab (item, event);
453 case RangeMarkerBarItem:
454 start_range_markerbar_op (item, event, CreateRangeMarker);
458 case TransportMarkerBarItem:
459 start_range_markerbar_op (item, event, CreateTransportMarker);
468 switch (mouse_mode) {
471 case StartSelectionTrimItem:
472 start_selection_op (item, event, SelectionStartTrim);
475 case EndSelectionTrimItem:
476 start_selection_op (item, event, SelectionEndTrim);
480 if (Keyboard::modifier_state_contains
481 (event->button.state, Keyboard::ModifierMask(Keyboard::Alt))) {
482 // contains and not equals because I can't use alt as a modifier alone.
483 start_selection_grab (item, event);
484 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
485 /* grab selection for moving */
486 start_selection_op (item, event, SelectionMove);
489 /* this was debated, but decided the more common action was to
490 make a new selection */
491 start_selection_op (item, event, CreateSelection);
496 start_selection_op (item, event, CreateSelection);
502 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::Control|Keyboard::Alt)) &&
503 event->type == GDK_BUTTON_PRESS) {
505 start_rubberband_select (item, event);
507 } else if (event->type == GDK_BUTTON_PRESS) {
510 case FadeInHandleItem:
511 start_fade_in_grab (item, event);
514 case FadeOutHandleItem:
515 start_fade_out_grab (item, event);
519 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
520 start_region_copy_grab (item, event);
521 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
522 start_region_brush_grab (item, event);
524 start_region_grab (item, event);
528 case RegionViewNameHighlight:
529 start_trim (item, event);
534 /* rename happens on edit clicks */
535 start_trim (clicked_regionview->get_name_highlight(), event);
539 case GainAutomationControlPointItem:
540 case PanAutomationControlPointItem:
541 case RedirectAutomationControlPointItem:
542 start_control_point_grab (item, event);
546 case GainAutomationLineItem:
547 case PanAutomationLineItem:
548 case RedirectAutomationLineItem:
549 start_line_grab_from_line (item, event);
554 case AutomationTrackItem:
555 start_rubberband_select (item, event);
559 case ImageFrameHandleStartItem:
560 imageframe_start_handle_op(item, event) ;
563 case ImageFrameHandleEndItem:
564 imageframe_end_handle_op(item, event) ;
567 case MarkerViewHandleStartItem:
568 markerview_item_start_handle_op(item, event) ;
571 case MarkerViewHandleEndItem:
572 markerview_item_end_handle_op(item, event) ;
576 start_markerview_grab(item, event) ;
579 start_imageframe_grab(item, event) ;
597 // start_line_grab_from_regionview (item, event);
600 case GainControlPointItem:
601 start_control_point_grab (item, event);
605 start_line_grab_from_line (item, event);
608 case GainAutomationControlPointItem:
609 case PanAutomationControlPointItem:
610 case RedirectAutomationControlPointItem:
611 start_control_point_grab (item, event);
622 case GainAutomationControlPointItem:
623 case PanAutomationControlPointItem:
624 case RedirectAutomationControlPointItem:
625 start_control_point_grab (item, event);
628 case GainAutomationLineItem:
629 case PanAutomationLineItem:
630 case RedirectAutomationLineItem:
631 start_line_grab_from_line (item, event);
635 // XXX need automation mode to identify which
637 // start_line_grab_from_regionview (item, event);
647 if (event->type == GDK_BUTTON_PRESS) {
648 start_mouse_zoom (item, event);
655 if (item_type == RegionItem) {
656 start_time_fx (item, event);
661 /* handled in release */
670 switch (mouse_mode) {
672 if (event->type == GDK_BUTTON_PRESS) {
675 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
676 start_region_copy_grab (item, event);
678 start_region_grab (item, event);
682 case GainAutomationControlPointItem:
683 case PanAutomationControlPointItem:
684 case RedirectAutomationControlPointItem:
685 start_control_point_grab (item, event);
696 case RegionViewNameHighlight:
697 start_trim (item, event);
702 start_trim (clicked_regionview->get_name_highlight(), event);
713 if (event->type == GDK_BUTTON_PRESS) {
714 /* relax till release */
721 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
722 temporal_zoom_session();
724 temporal_zoom_to_frame (true, event_frame(event));
747 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
749 nframes_t where = event_frame (event, 0, 0);
751 /* no action if we're recording */
753 if (session && session->actively_recording()) {
757 /* first, see if we're finishing a drag ... */
759 if (drag_info.item) {
760 if (end_grab (item, event)) {
761 /* grab dragged, so do nothing else */
766 button_selection (item, event, item_type);
768 /* edit events get handled here */
770 if (drag_info.item == 0 && Keyboard::is_edit_event (&event->button)) {
776 case TempoMarkerItem:
777 edit_tempo_marker (item);
780 case MeterMarkerItem:
781 edit_meter_marker (item);
785 if (clicked_regionview->name_active()) {
786 return mouse_rename_region (item, event);
796 /* context menu events get handled here */
798 if (Keyboard::is_context_menu_event (&event->button)) {
800 if (drag_info.item == 0) {
802 /* no matter which button pops up the context menu, tell the menu
803 widget to use button 1 to drive menu selection.
808 case FadeInHandleItem:
810 case FadeOutHandleItem:
811 popup_fade_context_menu (1, event->button.time, item, item_type);
816 case RegionViewNameHighlight:
819 case AutomationTrackItem:
820 case CrossfadeViewItem:
821 popup_track_context_menu (1, event->button.time, where);
825 case RangeMarkerBarItem:
826 case TransportMarkerBarItem:
829 popup_ruler_menu (pixel_to_frame(event->button.x), item_type);
833 marker_context_menu (&event->button, item);
836 case TempoMarkerItem:
837 tm_marker_context_menu (&event->button, item);
840 case MeterMarkerItem:
841 tm_marker_context_menu (&event->button, item);
846 popup_imageframe_edit_menu(1, event->button.time, item, true) ;
848 case ImageFrameTimeAxisItem:
849 popup_imageframe_edit_menu(1, event->button.time, item, false) ;
852 popup_marker_time_axis_edit_menu(1, event->button.time, item, true) ;
854 case MarkerTimeAxisItem:
855 popup_marker_time_axis_edit_menu(1, event->button.time, item, false) ;
867 /* delete events get handled here */
869 if (drag_info.item == 0 && Keyboard::is_delete_event (&event->button)) {
872 case TempoMarkerItem:
873 remove_tempo_marker (item);
876 case MeterMarkerItem:
877 remove_meter_marker (item);
881 remove_marker (*item, event);
885 if (mouse_mode == MouseObject) {
886 remove_clicked_region ();
890 case GainControlPointItem:
891 if (mouse_mode == MouseGain) {
892 remove_gain_control_point (item, event);
896 case GainAutomationControlPointItem:
897 case PanAutomationControlPointItem:
898 case RedirectAutomationControlPointItem:
899 remove_control_point (item, event);
908 switch (event->button.button) {
912 /* see comments in button_press_handler */
914 case PlayheadCursorItem:
917 case GainAutomationLineItem:
918 case PanAutomationLineItem:
919 case RedirectAutomationLineItem:
920 case StartSelectionTrimItem:
921 case EndSelectionTrimItem:
925 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
926 snap_to (where, 0, true);
928 mouse_add_new_marker (where);
932 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
935 mouse_add_new_tempo_event (where);
939 mouse_add_new_meter_event (pixel_to_frame (event->button.x));
947 switch (mouse_mode) {
950 case AutomationTrackItem:
951 dynamic_cast<AutomationTimeAxisView*>(clicked_axisview)->add_automation_event
965 // Gain only makes sense for audio regions
967 if (!dynamic_cast<AudioRegionView*>(clicked_regionview)) {
973 dynamic_cast<AudioRegionView*>(clicked_regionview)->add_gain_point_event (item, event);
977 case AutomationTrackItem:
978 dynamic_cast<AutomationTimeAxisView*>(clicked_axisview)->
979 add_automation_event (item, event, where, event->button.y);
990 audition_selected_region ();
1007 switch (mouse_mode) {
1011 // x_style_paste (where, 1.0);
1031 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1037 switch (item_type) {
1038 case GainControlPointItem:
1039 if (mouse_mode == MouseGain) {
1040 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1041 cp->set_visible (true);
1045 at_y = cp->get_y ();
1046 cp->item->i2w (at_x, at_y);
1050 fraction = 1.0 - ((cp->get_y() - cp->line.y_position()) / cp->line.height());
1052 set_verbose_canvas_cursor (cp->line.get_verbose_cursor_string (fraction), at_x, at_y);
1053 show_verbose_canvas_cursor ();
1055 if (is_drawable()) {
1056 track_canvas.get_window()->set_cursor (*fader_cursor);
1061 case GainAutomationControlPointItem:
1062 case PanAutomationControlPointItem:
1063 case RedirectAutomationControlPointItem:
1064 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1065 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1066 cp->set_visible (true);
1070 at_y = cp->get_y ();
1071 cp->item->i2w (at_x, at_y);
1075 fraction = 1.0 - (cp->get_y() / cp->line.height());
1077 set_verbose_canvas_cursor (cp->line.get_verbose_cursor_string (fraction), at_x, at_y);
1078 show_verbose_canvas_cursor ();
1080 if (is_drawable()) {
1081 track_canvas.get_window()->set_cursor (*fader_cursor);
1087 if (mouse_mode == MouseGain) {
1088 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1090 line->property_fill_color_rgba() = color_map[cEnteredGainLine];
1091 if (is_drawable()) {
1092 track_canvas.get_window()->set_cursor (*fader_cursor);
1097 case GainAutomationLineItem:
1098 case RedirectAutomationLineItem:
1099 case PanAutomationLineItem:
1100 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1102 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1104 line->property_fill_color_rgba() = color_map[cEnteredAutomationLine];
1106 if (is_drawable()) {
1107 track_canvas.get_window()->set_cursor (*fader_cursor);
1112 case RegionViewNameHighlight:
1113 if (is_drawable() && mouse_mode == MouseObject) {
1114 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1118 case StartSelectionTrimItem:
1119 case EndSelectionTrimItem:
1122 case ImageFrameHandleStartItem:
1123 case ImageFrameHandleEndItem:
1124 case MarkerViewHandleStartItem:
1125 case MarkerViewHandleEndItem:
1128 if (is_drawable()) {
1129 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1133 case EditCursorItem:
1134 case PlayheadCursorItem:
1135 if (is_drawable()) {
1136 track_canvas.get_window()->set_cursor (*grabber_cursor);
1140 case RegionViewName:
1142 /* when the name is not an active item, the entire name highlight is for trimming */
1144 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1145 if (mouse_mode == MouseObject && is_drawable()) {
1146 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1152 case AutomationTrackItem:
1153 if (is_drawable()) {
1154 Gdk::Cursor *cursor;
1155 switch (mouse_mode) {
1157 cursor = selector_cursor;
1160 cursor = zoom_cursor;
1163 cursor = cross_hair_cursor;
1167 track_canvas.get_window()->set_cursor (*cursor);
1169 AutomationTimeAxisView* atv;
1170 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1171 clear_entered_track = false;
1172 set_entered_track (atv);
1178 case RangeMarkerBarItem:
1179 case TransportMarkerBarItem:
1182 if (is_drawable()) {
1183 time_canvas.get_window()->set_cursor (*timebar_cursor);
1188 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1191 marker->set_color_rgba (color_map[cEnteredMarker]);
1193 case MeterMarkerItem:
1194 case TempoMarkerItem:
1195 if (is_drawable()) {
1196 time_canvas.get_window()->set_cursor (*timebar_cursor);
1199 case FadeInHandleItem:
1200 case FadeOutHandleItem:
1201 if (mouse_mode == MouseObject) {
1202 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1204 rect->property_fill_color_rgba() = 0;
1205 rect->property_outline_pixels() = 1;
1214 /* second pass to handle entered track status in a comprehensible way.
1217 switch (item_type) {
1219 case GainAutomationLineItem:
1220 case RedirectAutomationLineItem:
1221 case PanAutomationLineItem:
1222 case GainControlPointItem:
1223 case GainAutomationControlPointItem:
1224 case PanAutomationControlPointItem:
1225 case RedirectAutomationControlPointItem:
1226 /* these do not affect the current entered track state */
1227 clear_entered_track = false;
1230 case AutomationTrackItem:
1231 /* handled above already */
1235 set_entered_track (0);
1243 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1252 switch (item_type) {
1253 case GainControlPointItem:
1254 case GainAutomationControlPointItem:
1255 case PanAutomationControlPointItem:
1256 case RedirectAutomationControlPointItem:
1257 cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
1258 if (cp->line.npoints() > 1) {
1259 if (!cp->selected) {
1260 cp->set_visible (false);
1264 if (is_drawable()) {
1265 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1268 hide_verbose_canvas_cursor ();
1271 case RegionViewNameHighlight:
1272 case StartSelectionTrimItem:
1273 case EndSelectionTrimItem:
1274 case EditCursorItem:
1275 case PlayheadCursorItem:
1278 case ImageFrameHandleStartItem:
1279 case ImageFrameHandleEndItem:
1280 case MarkerViewHandleStartItem:
1281 case MarkerViewHandleEndItem:
1284 if (is_drawable()) {
1285 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1290 case GainAutomationLineItem:
1291 case RedirectAutomationLineItem:
1292 case PanAutomationLineItem:
1293 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1295 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1297 line->property_fill_color_rgba() = al->get_line_color();
1299 if (is_drawable()) {
1300 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1304 case RegionViewName:
1305 /* see enter_handler() for notes */
1306 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1307 if (is_drawable() && mouse_mode == MouseObject) {
1308 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1313 case RangeMarkerBarItem:
1314 case TransportMarkerBarItem:
1318 if (is_drawable()) {
1319 time_canvas.get_window()->set_cursor (*timebar_cursor);
1324 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1327 loc = find_location_from_marker (marker, is_start);
1328 if (loc) location_flags_changed (loc, this);
1330 case MeterMarkerItem:
1331 case TempoMarkerItem:
1333 if (is_drawable()) {
1334 time_canvas.get_window()->set_cursor (*timebar_cursor);
1339 case FadeInHandleItem:
1340 case FadeOutHandleItem:
1341 rv = static_cast<RegionView*>(item->get_data ("regionview"));
1343 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1345 rect->property_fill_color_rgba() = rv->get_fill_color();
1346 rect->property_outline_pixels() = 0;
1351 case AutomationTrackItem:
1352 if (is_drawable()) {
1353 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1354 clear_entered_track = true;
1355 Glib::signal_idle().connect (mem_fun(*this, &Editor::left_automation_track));
1367 Editor::left_automation_track ()
1369 if (clear_entered_track) {
1370 set_entered_track (0);
1371 clear_entered_track = false;
1377 Editor::motion_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type, bool from_autoscroll)
1381 /* We call this so that MOTION_NOTIFY events continue to be
1382 delivered to the canvas. We need to do this because we set
1383 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
1384 the density of the events, at the expense of a round-trip
1385 to the server. Given that this will mostly occur on cases
1386 where DISPLAY = :0.0, and given the cost of what the motion
1387 event might do, its a good tradeoff.
1390 track_canvas.get_pointer (x, y);
1392 if (current_stepping_trackview) {
1393 /* don't keep the persistent stepped trackview if the mouse moves */
1394 current_stepping_trackview = 0;
1395 step_timeout.disconnect ();
1398 if (session && session->actively_recording()) {
1399 /* Sorry. no dragging stuff around while we record */
1403 drag_info.item_type = item_type;
1404 drag_info.current_pointer_frame = event_frame (event, &drag_info.current_pointer_x,
1405 &drag_info.current_pointer_y);
1407 if (!from_autoscroll && drag_info.item) {
1408 /* item != 0 is the best test i can think of for dragging.
1410 if (!drag_info.move_threshold_passed) {
1412 bool x_threshold_passed = (abs ((int) (drag_info.current_pointer_x - drag_info.grab_x)) > 4);
1413 bool y_threshold_passed = (abs ((int) (drag_info.current_pointer_y - drag_info.grab_y)) > 4);
1415 drag_info.move_threshold_passed = (x_threshold_passed || y_threshold_passed);
1417 // and change the initial grab loc/frame if this drag info wants us to
1419 if (drag_info.want_move_threshold && drag_info.move_threshold_passed) {
1420 drag_info.grab_frame = drag_info.current_pointer_frame;
1421 drag_info.grab_x = drag_info.current_pointer_x;
1422 drag_info.grab_y = drag_info.current_pointer_y;
1423 drag_info.last_pointer_frame = drag_info.grab_frame;
1424 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
1429 switch (item_type) {
1430 case PlayheadCursorItem:
1431 case EditCursorItem:
1433 case GainControlPointItem:
1434 case RedirectAutomationControlPointItem:
1435 case GainAutomationControlPointItem:
1436 case PanAutomationControlPointItem:
1437 case TempoMarkerItem:
1438 case MeterMarkerItem:
1439 case RegionViewNameHighlight:
1440 case StartSelectionTrimItem:
1441 case EndSelectionTrimItem:
1444 case RedirectAutomationLineItem:
1445 case GainAutomationLineItem:
1446 case PanAutomationLineItem:
1447 case FadeInHandleItem:
1448 case FadeOutHandleItem:
1451 case ImageFrameHandleStartItem:
1452 case ImageFrameHandleEndItem:
1453 case MarkerViewHandleStartItem:
1454 case MarkerViewHandleEndItem:
1457 if (drag_info.item && (event->motion.state & Gdk::BUTTON1_MASK ||
1458 (event->motion.state & Gdk::BUTTON2_MASK))) {
1459 if (!from_autoscroll) {
1460 maybe_autoscroll (event);
1462 (this->*(drag_info.motion_callback)) (item, event);
1471 switch (mouse_mode) {
1476 if (drag_info.item && (event->motion.state & GDK_BUTTON1_MASK ||
1477 (event->motion.state & GDK_BUTTON2_MASK))) {
1478 if (!from_autoscroll) {
1479 maybe_autoscroll (event);
1481 (this->*(drag_info.motion_callback)) (item, event);
1492 track_canvas_motion (event);
1493 // drag_info.last_pointer_frame = drag_info.current_pointer_frame;
1501 Editor::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
1503 if (drag_info.item == 0) {
1504 fatal << _("programming error: start_grab called without drag item") << endmsg;
1510 cursor = grabber_cursor;
1513 // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
1515 if (event->button.button == 2) {
1516 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Alt)) {
1517 drag_info.y_constrained = true;
1518 drag_info.x_constrained = false;
1520 drag_info.y_constrained = false;
1521 drag_info.x_constrained = true;
1524 drag_info.x_constrained = false;
1525 drag_info.y_constrained = false;
1528 drag_info.grab_frame = event_frame(event, &drag_info.grab_x, &drag_info.grab_y);
1529 drag_info.last_pointer_frame = drag_info.grab_frame;
1530 drag_info.current_pointer_frame = drag_info.grab_frame;
1531 drag_info.current_pointer_x = drag_info.grab_x;
1532 drag_info.current_pointer_y = drag_info.grab_y;
1533 drag_info.cumulative_x_drag = 0;
1534 drag_info.cumulative_y_drag = 0;
1535 drag_info.first_move = true;
1536 drag_info.move_threshold_passed = false;
1537 drag_info.want_move_threshold = false;
1538 drag_info.pointer_frame_offset = 0;
1539 drag_info.brushing = false;
1540 drag_info.copied_location = 0;
1542 drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK,
1544 event->button.time);
1546 if (session && session->transport_rolling()) {
1547 drag_info.was_rolling = true;
1549 drag_info.was_rolling = false;
1552 switch (snap_type) {
1553 case SnapToRegionStart:
1554 case SnapToRegionEnd:
1555 case SnapToRegionSync:
1556 case SnapToRegionBoundary:
1557 build_region_boundary_cache ();
1565 Editor::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t time)
1567 drag_info.item->ungrab (0);
1568 drag_info.item = new_item;
1571 cursor = grabber_cursor;
1574 drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK, *cursor, time);
1578 Editor::end_grab (ArdourCanvas::Item* item, GdkEvent* event)
1580 bool did_drag = false;
1582 stop_canvas_autoscroll ();
1584 if (drag_info.item == 0) {
1588 drag_info.item->ungrab (event->button.time);
1590 if (drag_info.finished_callback) {
1591 (this->*(drag_info.finished_callback)) (item, event);
1594 did_drag = !drag_info.first_move;
1596 hide_verbose_canvas_cursor();
1599 drag_info.copy = false;
1600 drag_info.motion_callback = 0;
1601 drag_info.finished_callback = 0;
1602 drag_info.last_trackview = 0;
1603 drag_info.last_frame_position = 0;
1604 drag_info.grab_frame = 0;
1605 drag_info.last_pointer_frame = 0;
1606 drag_info.current_pointer_frame = 0;
1607 drag_info.brushing = false;
1609 if (drag_info.copied_location) {
1610 delete drag_info.copied_location;
1611 drag_info.copied_location = 0;
1618 Editor::set_edit_cursor (GdkEvent* event)
1620 nframes_t pointer_frame = event_frame (event);
1622 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1623 if (snap_type != SnapToEditCursor) {
1624 snap_to (pointer_frame);
1628 edit_cursor->set_position (pointer_frame);
1629 edit_cursor_clock.set (pointer_frame);
1633 Editor::set_playhead_cursor (GdkEvent* event)
1635 nframes_t pointer_frame = event_frame (event);
1637 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1638 snap_to (pointer_frame);
1642 session->request_locate (pointer_frame, session->transport_rolling());
1647 Editor::start_fade_in_grab (ArdourCanvas::Item* item, GdkEvent* event)
1649 drag_info.item = item;
1650 drag_info.motion_callback = &Editor::fade_in_drag_motion_callback;
1651 drag_info.finished_callback = &Editor::fade_in_drag_finished_callback;
1655 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
1656 fatal << _("programming error: fade in canvas item has no regionview data pointer!") << endmsg;
1660 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1662 drag_info.pointer_frame_offset = drag_info.grab_frame - ((nframes_t) arv->audio_region()->fade_in().back()->when + arv->region()->position());
1666 Editor::fade_in_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1668 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1670 nframes_t fade_length;
1672 if ((int32_t)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1673 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1679 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1683 if (pos < (arv->region()->position() + 64)) {
1684 fade_length = 64; // this should be a minimum defined somewhere
1685 } else if (pos > arv->region()->last_frame()) {
1686 fade_length = arv->region()->length();
1688 fade_length = pos - arv->region()->position();
1690 /* mapover the region selection */
1692 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1694 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1700 tmp->reset_fade_in_shape_width (fade_length);
1703 show_verbose_duration_cursor (arv->region()->position(), arv->region()->position() + fade_length, 10);
1705 drag_info.first_move = false;
1709 Editor::fade_in_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1711 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1713 nframes_t fade_length;
1715 if (drag_info.first_move) return;
1717 if ((int32_t)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1718 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1723 if (pos < (arv->region()->position() + 64)) {
1724 fade_length = 64; // this should be a minimum defined somewhere
1725 } else if (pos > arv->region()->last_frame()) {
1726 fade_length = arv->region()->length();
1728 fade_length = pos - arv->region()->position();
1731 begin_reversible_command (_("change fade in length"));
1733 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1735 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1741 AutomationList& alist = tmp->audio_region()->fade_in();
1742 XMLNode &before = alist.get_state();
1744 tmp->audio_region()->set_fade_in_length (fade_length);
1746 XMLNode &after = alist.get_state();
1747 session->add_command(new MementoCommand<AutomationList>(alist, &before, &after));
1750 commit_reversible_command ();
1754 Editor::start_fade_out_grab (ArdourCanvas::Item* item, GdkEvent* event)
1756 drag_info.item = item;
1757 drag_info.motion_callback = &Editor::fade_out_drag_motion_callback;
1758 drag_info.finished_callback = &Editor::fade_out_drag_finished_callback;
1762 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
1763 fatal << _("programming error: fade out canvas item has no regionview data pointer!") << endmsg;
1767 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1769 drag_info.pointer_frame_offset = drag_info.grab_frame - (arv->region()->length() - (nframes_t) arv->audio_region()->fade_out().back()->when + arv->region()->position());
1773 Editor::fade_out_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1775 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1777 nframes_t fade_length;
1779 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1780 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1786 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1790 if (pos > (arv->region()->last_frame() - 64)) {
1791 fade_length = 64; // this should really be a minimum fade defined somewhere
1793 else if (pos < arv->region()->position()) {
1794 fade_length = arv->region()->length();
1797 fade_length = arv->region()->last_frame() - pos;
1800 /* mapover the region selection */
1802 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1804 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1810 tmp->reset_fade_out_shape_width (fade_length);
1813 show_verbose_duration_cursor (arv->region()->last_frame() - fade_length, arv->region()->last_frame(), 10);
1815 drag_info.first_move = false;
1819 Editor::fade_out_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1821 if (drag_info.first_move) return;
1823 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1825 nframes_t fade_length;
1827 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1828 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1834 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1838 if (pos > (arv->region()->last_frame() - 64)) {
1839 fade_length = 64; // this should really be a minimum fade defined somewhere
1841 else if (pos < arv->region()->position()) {
1842 fade_length = arv->region()->length();
1845 fade_length = arv->region()->last_frame() - pos;
1848 begin_reversible_command (_("change fade out length"));
1850 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1852 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1858 AutomationList& alist = tmp->audio_region()->fade_out();
1859 XMLNode &before = alist.get_state();
1861 tmp->audio_region()->set_fade_out_length (fade_length);
1863 XMLNode &after = alist.get_state();
1864 session->add_command(new MementoCommand<AutomationList>(alist, &before, &after));
1867 commit_reversible_command ();
1871 Editor::start_cursor_grab (ArdourCanvas::Item* item, GdkEvent* event)
1873 drag_info.item = item;
1874 drag_info.motion_callback = &Editor::cursor_drag_motion_callback;
1875 drag_info.finished_callback = &Editor::cursor_drag_finished_callback;
1879 if ((drag_info.data = (item->get_data ("cursor"))) == 0) {
1880 fatal << _("programming error: cursor canvas item has no cursor data pointer!") << endmsg;
1884 Cursor* cursor = (Cursor *) drag_info.data;
1886 if (cursor == playhead_cursor) {
1887 _dragging_playhead = true;
1889 if (session && drag_info.was_rolling) {
1890 session->request_stop ();
1893 if (session && session->is_auditioning()) {
1894 session->cancel_audition ();
1898 drag_info.pointer_frame_offset = drag_info.grab_frame - cursor->current_frame;
1900 show_verbose_time_cursor (cursor->current_frame, 10);
1904 Editor::cursor_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1906 Cursor* cursor = (Cursor *) drag_info.data;
1907 nframes_t adjusted_frame;
1909 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1910 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1916 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1917 if (cursor != edit_cursor || snap_type != SnapToEditCursor) {
1918 snap_to (adjusted_frame);
1922 if (adjusted_frame == drag_info.last_pointer_frame) return;
1924 cursor->set_position (adjusted_frame);
1926 if (cursor == edit_cursor) {
1927 edit_cursor_clock.set (cursor->current_frame);
1929 UpdateAllTransportClocks (cursor->current_frame);
1932 show_verbose_time_cursor (cursor->current_frame, 10);
1934 drag_info.last_pointer_frame = adjusted_frame;
1935 drag_info.first_move = false;
1939 Editor::cursor_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1941 if (drag_info.first_move) return;
1943 cursor_drag_motion_callback (item, event);
1945 _dragging_playhead = false;
1947 if (item == &playhead_cursor->canvas_item) {
1949 session->request_locate (playhead_cursor->current_frame, drag_info.was_rolling);
1951 } else if (item == &edit_cursor->canvas_item) {
1952 edit_cursor->set_position (edit_cursor->current_frame);
1953 edit_cursor_clock.set (edit_cursor->current_frame);
1958 Editor::update_marker_drag_item (Location *location)
1960 double x1 = frame_to_pixel (location->start());
1961 double x2 = frame_to_pixel (location->end());
1963 if (location->is_mark()) {
1964 marker_drag_line_points.front().set_x(x1);
1965 marker_drag_line_points.back().set_x(x1);
1966 marker_drag_line->property_points() = marker_drag_line_points;
1969 range_marker_drag_rect->property_x1() = x1;
1970 range_marker_drag_rect->property_x2() = x2;
1975 Editor::start_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
1979 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1980 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
1986 Location *location = find_location_from_marker (marker, is_start);
1988 drag_info.item = item;
1989 drag_info.data = marker;
1990 drag_info.motion_callback = &Editor::marker_drag_motion_callback;
1991 drag_info.finished_callback = &Editor::marker_drag_finished_callback;
1995 drag_info.copied_location = new Location (*location);
1996 drag_info.pointer_frame_offset = drag_info.grab_frame - (is_start ? location->start() : location->end());
1998 update_marker_drag_item (location);
2000 if (location->is_mark()) {
2001 marker_drag_line->show();
2002 marker_drag_line->raise_to_top();
2005 range_marker_drag_rect->show();
2006 range_marker_drag_rect->raise_to_top();
2009 if (is_start) show_verbose_time_cursor (location->start(), 10);
2010 else show_verbose_time_cursor (location->end(), 10);
2014 Editor::marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2017 Marker* marker = (Marker *) drag_info.data;
2018 Location *real_location;
2019 Location *copy_location;
2021 bool move_both = false;
2025 if (drag_info.pointer_frame_offset <= (long) drag_info.current_pointer_frame) {
2026 newframe = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2032 nframes_t next = newframe;
2034 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2035 snap_to (newframe, 0, true);
2038 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
2042 /* call this to find out if its the start or end */
2044 real_location = find_location_from_marker (marker, is_start);
2046 /* use the copy that we're "dragging" around */
2048 copy_location = drag_info.copied_location;
2050 f_delta = copy_location->end() - copy_location->start();
2052 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
2056 if (copy_location->is_mark()) {
2059 copy_location->set_start (newframe);
2063 if (is_start) { // start-of-range marker
2066 copy_location->set_start (newframe);
2067 copy_location->set_end (newframe + f_delta);
2068 } else if (newframe < copy_location->end()) {
2069 copy_location->set_start (newframe);
2071 snap_to (next, 1, true);
2072 copy_location->set_end (next);
2073 copy_location->set_start (newframe);
2076 } else { // end marker
2079 copy_location->set_end (newframe);
2080 copy_location->set_start (newframe - f_delta);
2081 } else if (newframe > copy_location->start()) {
2082 copy_location->set_end (newframe);
2084 } else if (newframe > 0) {
2085 snap_to (next, -1, true);
2086 copy_location->set_start (next);
2087 copy_location->set_end (newframe);
2092 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
2093 drag_info.first_move = false;
2095 update_marker_drag_item (copy_location);
2097 LocationMarkers* lm = find_location_markers (real_location);
2098 lm->set_position (copy_location->start(), copy_location->end());
2100 show_verbose_time_cursor (newframe, 10);
2104 Editor::marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2106 if (drag_info.first_move) {
2107 marker_drag_motion_callback (item, event);
2111 Marker* marker = (Marker *) drag_info.data;
2115 begin_reversible_command ( _("move marker") );
2116 XMLNode &before = session->locations()->get_state();
2118 Location * location = find_location_from_marker (marker, is_start);
2121 if (location->is_mark()) {
2122 location->set_start (drag_info.copied_location->start());
2124 location->set (drag_info.copied_location->start(), drag_info.copied_location->end());
2128 XMLNode &after = session->locations()->get_state();
2129 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
2130 commit_reversible_command ();
2132 marker_drag_line->hide();
2133 range_marker_drag_rect->hide();
2137 Editor::start_meter_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2140 MeterMarker* meter_marker;
2142 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2143 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2147 meter_marker = dynamic_cast<MeterMarker*> (marker);
2149 MetricSection& section (meter_marker->meter());
2151 if (!section.movable()) {
2155 drag_info.item = item;
2156 drag_info.data = marker;
2157 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2158 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2162 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2164 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2168 Editor::start_meter_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2171 MeterMarker* meter_marker;
2173 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2174 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2178 meter_marker = dynamic_cast<MeterMarker*> (marker);
2180 // create a dummy marker for visual representation of moving the copy.
2181 // The actual copying is not done before we reach the finish callback.
2183 snprintf (name, sizeof(name), "%g/%g", meter_marker->meter().beats_per_bar(), meter_marker->meter().note_divisor ());
2184 MeterMarker* new_marker = new MeterMarker(*this, *meter_group, color_map[cMeterMarker], name,
2185 *new MeterSection(meter_marker->meter()));
2187 drag_info.item = &new_marker->the_item();
2188 drag_info.copy = true;
2189 drag_info.data = new_marker;
2190 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2191 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2195 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2197 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2201 Editor::meter_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2203 MeterMarker* marker = (MeterMarker *) drag_info.data;
2204 nframes_t adjusted_frame;
2206 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2207 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2213 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2214 snap_to (adjusted_frame);
2217 if (adjusted_frame == drag_info.last_pointer_frame) return;
2219 marker->set_position (adjusted_frame);
2222 drag_info.last_pointer_frame = adjusted_frame;
2223 drag_info.first_move = false;
2225 show_verbose_time_cursor (adjusted_frame, 10);
2229 Editor::meter_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2231 if (drag_info.first_move) return;
2233 meter_marker_drag_motion_callback (drag_info.item, event);
2235 MeterMarker* marker = (MeterMarker *) drag_info.data;
2238 TempoMap& map (session->tempo_map());
2239 map.bbt_time (drag_info.last_pointer_frame, when);
2241 if (drag_info.copy == true) {
2242 begin_reversible_command (_("copy meter mark"));
2243 XMLNode &before = map.get_state();
2244 map.add_meter (marker->meter(), when);
2245 XMLNode &after = map.get_state();
2246 session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2247 commit_reversible_command ();
2249 // delete the dummy marker we used for visual representation of copying.
2250 // a new visual marker will show up automatically.
2253 begin_reversible_command (_("move meter mark"));
2254 XMLNode &before = map.get_state();
2255 map.move_meter (marker->meter(), when);
2256 XMLNode &after = map.get_state();
2257 session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2258 commit_reversible_command ();
2263 Editor::start_tempo_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2266 TempoMarker* tempo_marker;
2268 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2269 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2273 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2274 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2278 MetricSection& section (tempo_marker->tempo());
2280 if (!section.movable()) {
2284 drag_info.item = item;
2285 drag_info.data = marker;
2286 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2287 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2291 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2292 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2296 Editor::start_tempo_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2299 TempoMarker* tempo_marker;
2301 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2302 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2306 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2307 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2311 // create a dummy marker for visual representation of moving the copy.
2312 // The actual copying is not done before we reach the finish callback.
2314 snprintf (name, sizeof (name), "%.2f", tempo_marker->tempo().beats_per_minute());
2315 TempoMarker* new_marker = new TempoMarker(*this, *tempo_group, color_map[cTempoMarker], name,
2316 *new TempoSection(tempo_marker->tempo()));
2318 drag_info.item = &new_marker->the_item();
2319 drag_info.copy = true;
2320 drag_info.data = new_marker;
2321 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2322 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2326 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2328 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2332 Editor::tempo_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2334 TempoMarker* marker = (TempoMarker *) drag_info.data;
2335 nframes_t adjusted_frame;
2337 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2338 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2344 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2345 snap_to (adjusted_frame);
2348 if (adjusted_frame == drag_info.last_pointer_frame) return;
2350 /* OK, we've moved far enough to make it worth actually move the thing. */
2352 marker->set_position (adjusted_frame);
2354 show_verbose_time_cursor (adjusted_frame, 10);
2356 drag_info.last_pointer_frame = adjusted_frame;
2357 drag_info.first_move = false;
2361 Editor::tempo_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2363 if (drag_info.first_move) return;
2365 tempo_marker_drag_motion_callback (drag_info.item, event);
2367 TempoMarker* marker = (TempoMarker *) drag_info.data;
2370 TempoMap& map (session->tempo_map());
2371 map.bbt_time (drag_info.last_pointer_frame, when);
2373 if (drag_info.copy == true) {
2374 begin_reversible_command (_("copy tempo mark"));
2375 XMLNode &before = map.get_state();
2376 map.add_tempo (marker->tempo(), when);
2377 XMLNode &after = map.get_state();
2378 session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2379 commit_reversible_command ();
2381 // delete the dummy marker we used for visual representation of copying.
2382 // a new visual marker will show up automatically.
2385 begin_reversible_command (_("move tempo mark"));
2386 XMLNode &before = map.get_state();
2387 map.move_tempo (marker->tempo(), when);
2388 XMLNode &after = map.get_state();
2389 session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2390 commit_reversible_command ();
2395 Editor::remove_gain_control_point (ArdourCanvas::Item*item, GdkEvent* event)
2397 ControlPoint* control_point;
2399 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2400 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2404 // We shouldn't remove the first or last gain point
2405 if (control_point->line.is_last_point(*control_point) ||
2406 control_point->line.is_first_point(*control_point)) {
2410 control_point->line.remove_point (*control_point);
2414 Editor::remove_control_point (ArdourCanvas::Item*item, GdkEvent* event)
2416 ControlPoint* control_point;
2418 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2419 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2423 control_point->line.remove_point (*control_point);
2427 Editor::start_control_point_grab (ArdourCanvas::Item* item, GdkEvent* event)
2429 ControlPoint* control_point;
2431 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2432 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2436 drag_info.item = item;
2437 drag_info.data = control_point;
2438 drag_info.motion_callback = &Editor::control_point_drag_motion_callback;
2439 drag_info.finished_callback = &Editor::control_point_drag_finished_callback;
2441 start_grab (event, fader_cursor);
2443 control_point->line.start_drag (control_point, drag_info.grab_frame, 0);
2445 float fraction = 1.0 - ((control_point->get_y() - control_point->line.y_position()) / control_point->line.height());
2446 set_verbose_canvas_cursor (control_point->line.get_verbose_cursor_string (fraction),
2447 drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
2449 show_verbose_canvas_cursor ();
2453 Editor::control_point_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2455 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2457 double cx = drag_info.current_pointer_x;
2458 double cy = drag_info.current_pointer_y;
2460 drag_info.cumulative_x_drag = cx - drag_info.grab_x ;
2461 drag_info.cumulative_y_drag = cy - drag_info.grab_y ;
2463 if (drag_info.x_constrained) {
2464 cx = drag_info.grab_x;
2466 if (drag_info.y_constrained) {
2467 cy = drag_info.grab_y;
2470 cp->line.parent_group().w2i (cx, cy);
2474 cy = min ((double) (cp->line.y_position() + cp->line.height()), cy);
2476 //translate cx to frames
2477 nframes_t cx_frames = unit_to_frame (cx);
2479 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier()) && !drag_info.x_constrained) {
2480 snap_to (cx_frames);
2483 float const fraction = 1.0 - ((cy - cp->line.y_position()) / cp->line.height());
2487 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
2493 cp->line.point_drag (*cp, cx_frames , fraction, push);
2495 set_verbose_canvas_cursor_text (cp->line.get_verbose_cursor_string (fraction));
2497 drag_info.first_move = false;
2501 Editor::control_point_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2503 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2505 if (drag_info.first_move) {
2509 if ((event->type == GDK_BUTTON_RELEASE) && (event->button.button == 1) && Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
2510 reset_point_selection ();
2514 control_point_drag_motion_callback (item, event);
2516 cp->line.end_drag (cp);
2520 Editor::start_line_grab_from_regionview (ArdourCanvas::Item* item, GdkEvent* event)
2522 switch (mouse_mode) {
2524 assert(dynamic_cast<AudioRegionView*>(clicked_regionview));
2525 start_line_grab (dynamic_cast<AudioRegionView*>(clicked_regionview)->get_gain_line(), event);
2533 Editor::start_line_grab_from_line (ArdourCanvas::Item* item, GdkEvent* event)
2537 if ((al = reinterpret_cast<AutomationLine*> (item->get_data ("line"))) == 0) {
2538 fatal << _("programming error: line canvas item has no line pointer!") << endmsg;
2542 start_line_grab (al, event);
2546 Editor::start_line_grab (AutomationLine* line, GdkEvent* event)
2550 nframes_t frame_within_region;
2552 /* need to get x coordinate in terms of parent (TimeAxisItemView)
2556 cx = event->button.x;
2557 cy = event->button.y;
2558 line->parent_group().w2i (cx, cy);
2559 frame_within_region = (nframes_t) floor (cx * frames_per_unit);
2561 if (!line->control_points_adjacent (frame_within_region, current_line_drag_info.before,
2562 current_line_drag_info.after)) {
2563 /* no adjacent points */
2567 drag_info.item = &line->grab_item();
2568 drag_info.data = line;
2569 drag_info.motion_callback = &Editor::line_drag_motion_callback;
2570 drag_info.finished_callback = &Editor::line_drag_finished_callback;
2572 start_grab (event, fader_cursor);
2574 double const fraction = 1.0 - ((cy - line->y_position()) / line->height());
2576 line->start_drag (0, drag_info.grab_frame, fraction);
2578 set_verbose_canvas_cursor (line->get_verbose_cursor_string (fraction),
2579 drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
2580 show_verbose_canvas_cursor ();
2584 Editor::line_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2586 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
2587 double cx = drag_info.current_pointer_x;
2588 double cy = drag_info.current_pointer_y;
2590 line->parent_group().w2i (cx, cy);
2592 double const fraction = 1.0 - ((cy - line->y_position()) / line->height());
2596 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
2602 line->line_drag (current_line_drag_info.before, current_line_drag_info.after, fraction, push);
2604 set_verbose_canvas_cursor_text (line->get_verbose_cursor_string (fraction));
2608 Editor::line_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2610 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
2611 line_drag_motion_callback (item, event);
2616 Editor::start_region_grab (ArdourCanvas::Item* item, GdkEvent* event)
2618 if (selection->regions.empty() || clicked_regionview == 0) {
2622 drag_info.copy = false;
2623 drag_info.item = item;
2624 drag_info.data = clicked_regionview;
2625 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2626 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2631 TimeAxisView* tvp = clicked_axisview;
2632 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2634 if (tv && tv->is_track()) {
2635 speed = tv->get_diskstream()->speed();
2638 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2639 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2640 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2641 // we want a move threshold
2642 drag_info.want_move_threshold = true;
2644 show_verbose_time_cursor (drag_info.last_frame_position, 10);
2646 begin_reversible_command (_("move region(s)"));
2650 Editor::start_region_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2652 if (selection->regions.empty() || clicked_regionview == 0) {
2656 drag_info.copy = true;
2657 drag_info.item = item;
2658 drag_info.data = clicked_regionview;
2662 TimeAxisView* tv = &clicked_regionview->get_time_axis_view();
2663 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
2666 if (rtv && rtv->is_track()) {
2667 speed = rtv->get_diskstream()->speed();
2670 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2671 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2672 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2673 // we want a move threshold
2674 drag_info.want_move_threshold = true;
2675 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2676 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2677 show_verbose_time_cursor (drag_info.last_frame_position, 10);
2681 Editor::start_region_brush_grab (ArdourCanvas::Item* item, GdkEvent* event)
2683 if (selection->regions.empty() || clicked_regionview == 0) {
2687 drag_info.copy = false;
2688 drag_info.item = item;
2689 drag_info.data = clicked_regionview;
2690 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2691 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2696 TimeAxisView* tvp = clicked_axisview;
2697 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2699 if (tv && tv->is_track()) {
2700 speed = tv->get_diskstream()->speed();
2703 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2704 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2705 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2706 // we want a move threshold
2707 drag_info.want_move_threshold = true;
2708 drag_info.brushing = true;
2710 begin_reversible_command (_("Drag region brush"));
2714 Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2718 RegionView* rv = reinterpret_cast<RegionView*> (drag_info.data);
2719 nframes_t pending_region_position = 0;
2720 int32_t pointer_y_span = 0, canvas_pointer_y_span = 0, original_pointer_order;
2721 int32_t visible_y_high = 0, visible_y_low = 512; //high meaning higher numbered.. not the height on the screen
2722 bool clamp_y_axis = false;
2723 vector<int32_t> height_list(512) ;
2724 vector<int32_t>::iterator j;
2726 if (drag_info.copy && drag_info.move_threshold_passed && drag_info.want_move_threshold) {
2728 drag_info.want_move_threshold = false; // don't copy again
2730 /* duplicate the region(s) */
2732 vector<RegionView*> new_regionviews;
2734 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
2740 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
2741 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
2744 nrv = new AudioRegionView (*arv);
2746 nrv = new MidiRegionView (*mrv);
2751 nrv->get_canvas_group()->show ();
2753 new_regionviews.push_back (nrv);
2756 if (new_regionviews.empty()) {
2760 /* reset selection to new regionviews */
2762 selection->set (new_regionviews);
2764 /* reset drag_info data to reflect the fact that we are dragging the copies */
2766 drag_info.data = new_regionviews.front();
2768 swap_grab (new_regionviews.front()->get_canvas_group (), 0, event->motion.time);
2771 /* Which trackview is this ? */
2773 TimeAxisView* tvp = trackview_by_y_position (drag_info.current_pointer_y);
2774 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2776 /* The region motion is only processed if the pointer is over
2780 if (!tv || !tv->is_track()) {
2781 /* To make sure we hide the verbose canvas cursor when the mouse is
2782 not held over a track.
2784 hide_verbose_canvas_cursor ();
2788 original_pointer_order = drag_info.last_trackview->order;
2790 /************************************************************
2792 ************************************************************/
2794 if (drag_info.brushing) {
2795 clamp_y_axis = true;
2800 if ((pointer_y_span = (drag_info.last_trackview->order - tv->order)) != 0) {
2802 int32_t children = 0, numtracks = 0;
2803 // XXX hard coding track limit, oh my, so very very bad
2804 bitset <1024> tracks (0x00);
2805 /* get a bitmask representing the visible tracks */
2807 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2808 TimeAxisView *tracklist_timeview;
2809 tracklist_timeview = (*i);
2810 RouteTimeAxisView* rtv2 = dynamic_cast<RouteTimeAxisView*>(tracklist_timeview);
2811 list<TimeAxisView*> children_list;
2813 /* zeroes are audio tracks. ones are other types. */
2815 if (!rtv2->hidden()) {
2817 if (visible_y_high < rtv2->order) {
2818 visible_y_high = rtv2->order;
2820 if (visible_y_low > rtv2->order) {
2821 visible_y_low = rtv2->order;
2824 if (!rtv2->is_track()) {
2825 tracks = tracks |= (0x01 << rtv2->order);
2828 height_list[rtv2->order] = (*i)->height;
2830 if ((children_list = rtv2->get_child_list()).size() > 0) {
2831 for (list<TimeAxisView*>::iterator j = children_list.begin(); j != children_list.end(); ++j) {
2832 tracks = tracks |= (0x01 << (rtv2->order + children));
2833 height_list[rtv2->order + children] = (*j)->height;
2841 /* find the actual span according to the canvas */
2843 canvas_pointer_y_span = pointer_y_span;
2844 if (drag_info.last_trackview->order >= tv->order) {
2846 for (y = tv->order; y < drag_info.last_trackview->order; y++) {
2847 if (height_list[y] == 0 ) {
2848 canvas_pointer_y_span--;
2853 for (y = drag_info.last_trackview->order;y <= tv->order; y++) {
2854 if ( height_list[y] == 0 ) {
2855 canvas_pointer_y_span++;
2860 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
2861 RegionView* rv2 = (*i);
2862 double ix1, ix2, iy1, iy2;
2865 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
2866 rv2->get_canvas_group()->i2w (ix1, iy1);
2867 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
2868 RouteTimeAxisView* rtv2 = dynamic_cast<RouteTimeAxisView*>(tvp2);
2870 if (rtv2->order != original_pointer_order) {
2871 /* this isn't the pointer track */
2873 if (canvas_pointer_y_span > 0) {
2875 /* moving up the canvas */
2876 if ((rtv2->order - canvas_pointer_y_span) >= visible_y_low) {
2878 int32_t visible_tracks = 0;
2879 while (visible_tracks < canvas_pointer_y_span ) {
2882 while (height_list[rtv2->order - (visible_tracks - n)] == 0) {
2883 /* we're passing through a hidden track */
2888 if (tracks[rtv2->order - (canvas_pointer_y_span - n)] != 0x00) {
2889 clamp_y_axis = true;
2893 clamp_y_axis = true;
2896 } else if (canvas_pointer_y_span < 0) {
2898 /*moving down the canvas*/
2900 if ((rtv2->order - (canvas_pointer_y_span - n)) <= visible_y_high) { // we will overflow
2903 int32_t visible_tracks = 0;
2905 while (visible_tracks > canvas_pointer_y_span ) {
2908 while (height_list[rtv2->order - (visible_tracks - n)] == 0) {
2912 if ( tracks[rtv2->order - ( canvas_pointer_y_span - n)] != 0x00) {
2913 clamp_y_axis = true;
2918 clamp_y_axis = true;
2924 /* this is the pointer's track */
2925 if ((rtv2->order - pointer_y_span) > visible_y_high) { // we will overflow
2926 clamp_y_axis = true;
2927 } else if ((rtv2->order - pointer_y_span) < visible_y_low) { // we will underflow
2928 clamp_y_axis = true;
2936 } else if (drag_info.last_trackview == tv) {
2937 clamp_y_axis = true;
2941 if (!clamp_y_axis) {
2942 drag_info.last_trackview = tv;
2945 /************************************************************
2947 ************************************************************/
2949 /* compute the amount of pointer motion in frames, and where
2950 the region would be if we moved it by that much.
2953 if (drag_info.move_threshold_passed) {
2955 if ((int32_t)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2957 nframes_t sync_frame;
2958 nframes_t sync_offset;
2961 pending_region_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2963 sync_offset = rv->region()->sync_offset (sync_dir);
2964 sync_frame = rv->region()->adjust_to_sync (pending_region_position);
2966 /* we snap if the snap modifier is not enabled.
2969 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2970 snap_to (sync_frame);
2973 if (sync_frame - sync_offset <= sync_frame) {
2974 pending_region_position = sync_frame - (sync_dir*sync_offset);
2976 pending_region_position = 0;
2980 pending_region_position = 0;
2983 if (pending_region_position > max_frames - rv->region()->length()) {
2984 pending_region_position = drag_info.last_frame_position;
2987 // printf ("3: pending_region_position= %lu %lu\n", pending_region_position, drag_info.last_frame_position );
2989 if (pending_region_position != drag_info.last_frame_position && !drag_info.x_constrained) {
2991 /* now compute the canvas unit distance we need to move the regiondrag_info.last_trackview->order
2992 to make it appear at the new location.
2995 if (pending_region_position > drag_info.last_frame_position) {
2996 x_delta = ((double) (pending_region_position - drag_info.last_frame_position) / frames_per_unit);
2998 x_delta = -((double) (drag_info.last_frame_position - pending_region_position) / frames_per_unit);
3001 drag_info.last_frame_position = pending_region_position;
3008 /* threshold not passed */
3013 /*************************************************************
3015 ************************************************************/
3017 if (x_delta == 0 && (pointer_y_span == 0)) {
3018 /* haven't reached next snap point, and we're not switching
3019 trackviews. nothing to do.
3025 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3027 RegionView* rv2 = (*i);
3029 // If any regionview is at zero, we need to know so we can stop further leftward motion.
3031 double ix1, ix2, iy1, iy2;
3032 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3033 rv2->get_canvas_group()->i2w (ix1, iy1);
3042 /*************************************************************
3044 ************************************************************/
3048 if (drag_info.first_move) {
3049 if (drag_info.move_threshold_passed) {
3060 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
3061 const list<RegionView*>& layered_regions = selection->regions.by_layer();
3063 for (list<RegionView*>::const_iterator i = layered_regions.begin(); i != layered_regions.end(); ++i) {
3065 RegionView* rv = (*i);
3066 double ix1, ix2, iy1, iy2;
3067 int32_t temp_pointer_y_span = pointer_y_span;
3069 /* get item BBox, which will be relative to parent. so we have
3070 to query on a child, then convert to world coordinates using
3074 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3075 rv->get_canvas_group()->i2w (ix1, iy1);
3076 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3077 RouteTimeAxisView* canvas_rtv = dynamic_cast<RouteTimeAxisView*>(tvp2);
3078 RouteTimeAxisView* temp_rtv;
3080 if ((pointer_y_span != 0) && !clamp_y_axis) {
3083 for (j = height_list.begin(); j!= height_list.end(); j++) {
3084 if (x == canvas_rtv->order) {
3085 /* we found the track the region is on */
3086 if (x != original_pointer_order) {
3087 /*this isn't from the same track we're dragging from */
3088 temp_pointer_y_span = canvas_pointer_y_span;
3090 while (temp_pointer_y_span > 0) {
3091 /* we're moving up canvas-wise,
3092 so we need to find the next track height
3094 if (j != height_list.begin()) {
3097 if (x != original_pointer_order) {
3098 /* we're not from the dragged track, so ignore hidden tracks. */
3100 temp_pointer_y_span++;
3104 temp_pointer_y_span--;
3106 while (temp_pointer_y_span < 0) {
3108 if (x != original_pointer_order) {
3110 temp_pointer_y_span--;
3114 if (j != height_list.end()) {
3117 temp_pointer_y_span++;
3119 /* find out where we'll be when we move and set height accordingly */
3121 tvp2 = trackview_by_y_position (iy1 + y_delta);
3122 temp_rtv = dynamic_cast<RouteTimeAxisView*>(tvp2);
3123 rv->set_y_position_and_height (0, temp_rtv->height);
3125 /* if you un-comment the following, the region colours will follow the track colours whilst dragging,
3126 personally, i think this can confuse things, but never mind.
3129 //const GdkColor& col (temp_rtv->view->get_region_color());
3130 //rv->set_color (const_cast<GdkColor&>(col));
3137 /* prevent the regionview from being moved to before
3138 the zero position on the canvas.
3143 if (-x_delta > ix1) {
3146 } else if ((x_delta > 0) &&(rv->region()->last_frame() > max_frames - x_delta)) {
3147 x_delta = max_frames - rv->region()->last_frame();
3150 if (drag_info.first_move) {
3152 /* hide any dependent views */
3154 rv->get_time_axis_view().hide_dependent_views (*rv);
3156 /* this is subtle. raising the regionview itself won't help,
3157 because raise_to_top() just puts the item on the top of
3158 its parent's stack. so, we need to put the trackview canvas_display group
3159 on the top, since its parent is the whole canvas.
3162 rv->get_canvas_group()->raise_to_top();
3163 rv->get_time_axis_view().canvas_display->raise_to_top();
3164 cursor_group->raise_to_top();
3166 rv->fake_set_opaque (true);
3169 if (drag_info.brushing) {
3170 mouse_brush_insert_region (rv, pending_region_position);
3172 rv->move (x_delta, y_delta);
3175 } /* foreach region */
3179 if (drag_info.first_move && drag_info.move_threshold_passed) {
3180 cursor_group->raise_to_top();
3181 drag_info.first_move = false;
3184 if (x_delta != 0 && !drag_info.brushing) {
3185 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3190 Editor::region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3193 RegionView* rv = reinterpret_cast<RegionView *> (drag_info.data);
3194 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
3195 bool nocommit = true;
3197 RouteTimeAxisView* rtv;
3198 bool regionview_y_movement;
3199 bool regionview_x_movement;
3200 vector<RegionView*> copies;
3202 /* first_move is set to false if the regionview has been moved in the
3206 if (drag_info.first_move && !(drag_info.copy && drag_info.x_constrained)) {
3213 /* The regionview has been moved at some stage during the grab so we need
3214 to account for any mouse movement between this event and the last one.
3217 region_drag_motion_callback (item, event);
3219 if (drag_info.brushing) {
3220 /* all changes were made during motion event handlers */
3222 if (drag_info.copy) {
3223 for (list<RegionView*>::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
3224 copies.push_back (*i);
3231 /* adjust for track speed */
3234 rtv = dynamic_cast<RouteTimeAxisView*> (drag_info.last_trackview);
3235 if (rtv && rtv->get_diskstream()) {
3236 speed = rtv->get_diskstream()->speed();
3239 regionview_x_movement = (drag_info.last_frame_position != (nframes_t) (rv->region()->position()/speed));
3240 regionview_y_movement = (drag_info.last_trackview != &rv->get_time_axis_view());
3242 //printf ("last_frame: %s position is %lu %g\n", rv->get_time_axis_view().name().c_str(), drag_info.last_frame_position, speed);
3243 //printf ("last_rackview: %s \n", drag_info.last_trackview->name().c_str());
3247 if (drag_info.copy) {
3248 if (drag_info.x_constrained) {
3249 op_string = _("fixed time region copy");
3251 op_string = _("region copy");
3254 if (drag_info.x_constrained) {
3255 op_string = _("fixed time region drag");
3257 op_string = _("region drag");
3261 begin_reversible_command (op_string);
3263 if (regionview_y_movement) {
3265 /* moved to a different audio track. */
3267 vector<RegionView*> new_selection;
3269 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ) {
3271 RegionView* rv = (*i);
3273 double ix1, ix2, iy1, iy2;
3275 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3276 rv->get_canvas_group()->i2w (ix1, iy1);
3277 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3278 RouteTimeAxisView* rtv2 = dynamic_cast<RouteTimeAxisView*>(tvp2);
3280 boost::shared_ptr<Playlist> from_playlist = rv->region()->playlist();
3281 boost::shared_ptr<Playlist> to_playlist = rtv2->playlist();
3283 where = (nframes_t) (unit_to_frame (ix1) * speed);
3284 boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region()));
3286 /* undo the previous hide_dependent_views so that xfades don't
3287 disappear on copying regions
3290 rv->get_time_axis_view().reveal_dependent_views (*rv);
3292 if (!drag_info.copy) {
3294 /* the region that used to be in the old playlist is not
3295 moved to the new one - we make a copy of it. as a result,
3296 any existing editor for the region should no longer be
3300 rv->hide_region_editor();
3301 rv->fake_set_opaque (false);
3303 session->add_command (new MementoCommand<Playlist>(*from_playlist, &from_playlist->get_state(), 0));
3304 from_playlist->remove_region ((rv->region()));
3305 session->add_command (new MementoCommand<Playlist>(*from_playlist, 0, &from_playlist->get_state()));
3309 /* the regionview we dragged around is a temporary copy, queue it for deletion */
3311 copies.push_back (rv);
3314 latest_regionview = 0;
3316 sigc::connection c = rtv2->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3317 session->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
3318 to_playlist->add_region (new_region, where);
3319 session->add_command (new MementoCommand<Playlist>(*to_playlist, 0, &to_playlist->get_state()));
3322 if (latest_regionview) {
3323 new_selection.push_back (latest_regionview);
3326 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
3327 was selected in all of them, then removing it from the playlist will have removed all
3328 trace of it from the selection (i.e. there were N regions selected, we removed 1,
3329 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
3330 corresponding regionview, and the selection is now empty).
3332 this could have invalidated any and all iterators into the region selection.
3334 the heuristic we use here is: if the region selection is empty, break out of the loop
3335 here. if the region selection is not empty, then restart the loop because we know that
3336 we must have removed at least the region(view) we've just been working on as well as any
3337 that we processed on previous iterations.
3339 EXCEPT .... if we are doing a copy drag, then the selection hasn't been modified and
3340 we can just iterate.
3343 if (drag_info.copy) {
3346 if (selection->regions.empty()) {
3349 i = selection->regions.by_layer().begin();
3354 selection->set (new_selection);
3358 /* motion within a single track */
3360 list<RegionView*> regions = selection->regions.by_layer();
3362 if (drag_info.copy) {
3363 selection->clear_regions();
3366 for (list<RegionView*>::iterator i = regions.begin(); i != regions.end(); ++i) {
3370 if (!rv->region()->can_move()) {
3374 if (regionview_x_movement) {
3375 double ownspeed = 1.0;
3376 rtv = dynamic_cast<RouteTimeAxisView*> (&(rv->get_time_axis_view()));
3378 if (rtv && rtv->get_diskstream()) {
3379 ownspeed = rtv->get_diskstream()->speed();
3382 /* base the new region position on the current position of the regionview.*/
3384 double ix1, ix2, iy1, iy2;
3386 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3387 rv->get_canvas_group()->i2w (ix1, iy1);
3388 where = (nframes_t) (unit_to_frame (ix1) * ownspeed);
3392 where = rv->region()->position();
3395 boost::shared_ptr<Playlist> to_playlist = rv->region()->playlist();
3397 assert (to_playlist);
3401 session->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
3403 if (drag_info.copy) {
3405 boost::shared_ptr<Region> newregion;
3406 boost::shared_ptr<Region> ar;
3408 if ((ar = boost::dynamic_pointer_cast<AudioRegion>(rv->region())) != 0) {
3409 newregion = RegionFactory::create (ar);
3411 /* XXX MIDI HERE drobilla */
3417 latest_regionview = 0;
3418 sigc::connection c = rtv->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3419 to_playlist->add_region (newregion, (nframes_t) (where * rtv->get_diskstream()->speed()));
3422 if (latest_regionview) {
3423 rtv->reveal_dependent_views (*latest_regionview);
3424 selection->add (latest_regionview);
3429 /* just change the model */
3431 rv->region()->set_position (where, (void*) this);
3437 session->add_command (new MementoCommand<Playlist>(*to_playlist, 0, &to_playlist->get_state()));
3439 if (drag_info.copy) {
3440 copies.push_back (rv);
3448 commit_reversible_command ();
3451 for (vector<RegionView*>::iterator x = copies.begin(); x != copies.end(); ++x) {
3457 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
3459 /* Either add to or set the set the region selection, unless
3460 this is an alignment click (control used)
3463 if (Keyboard::modifier_state_contains (event->state, Keyboard::Control)) {
3464 TimeAxisView* tv = &rv.get_time_axis_view();
3465 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
3467 if (rtv && rtv->is_track()) {
3468 speed = rtv->get_diskstream()->speed();
3471 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Alt))) {
3473 align_region (rv.region(), SyncPoint, (nframes_t) (edit_cursor->current_frame * speed));
3475 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Shift))) {
3477 align_region (rv.region(), End, (nframes_t) (edit_cursor->current_frame * speed));
3481 align_region (rv.region(), Start, (nframes_t) (edit_cursor->current_frame * speed));
3487 Editor::show_verbose_time_cursor (nframes_t frame, double offset, double xpos, double ypos)
3493 nframes_t frame_rate;
3500 switch (Profile->get_small_screen() ? ARDOUR_UI::instance()->primary_clock.mode () : ARDOUR_UI::instance()->secondary_clock.mode ()) {
3501 case AudioClock::BBT:
3502 session->bbt_time (frame, bbt);
3503 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, bbt.bars, bbt.beats, bbt.ticks);
3506 case AudioClock::SMPTE:
3507 session->smpte_time (frame, smpte);
3508 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
3511 case AudioClock::MinSec:
3512 /* XXX this is copied from show_verbose_duration_cursor() */
3513 frame_rate = session->frame_rate();
3514 hours = frame / (frame_rate * 3600);
3515 frame = frame % (frame_rate * 3600);
3516 mins = frame / (frame_rate * 60);
3517 frame = frame % (frame_rate * 60);
3518 secs = (float) frame / (float) frame_rate;
3519 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
3523 snprintf (buf, sizeof(buf), "%u", frame);
3527 if (xpos >= 0 && ypos >=0) {
3528 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
3531 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
3533 show_verbose_canvas_cursor ();
3537 Editor::show_verbose_duration_cursor (nframes_t start, nframes_t end, double offset, double xpos, double ypos)
3544 nframes_t distance, frame_rate;
3546 Meter meter_at_start(session->tempo_map().meter_at(start));
3552 switch (ARDOUR_UI::instance()->secondary_clock.mode ()) {
3553 case AudioClock::BBT:
3554 session->bbt_time (start, sbbt);
3555 session->bbt_time (end, ebbt);
3558 /* XXX this computation won't work well if the
3559 user makes a selection that spans any meter changes.
3562 ebbt.bars -= sbbt.bars;
3563 if (ebbt.beats >= sbbt.beats) {
3564 ebbt.beats -= sbbt.beats;
3567 ebbt.beats = int(meter_at_start.beats_per_bar()) + ebbt.beats - sbbt.beats;
3569 if (ebbt.ticks >= sbbt.ticks) {
3570 ebbt.ticks -= sbbt.ticks;
3573 ebbt.ticks = int(Meter::ticks_per_beat) + ebbt.ticks - sbbt.ticks;
3576 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, ebbt.bars, ebbt.beats, ebbt.ticks);
3579 case AudioClock::SMPTE:
3580 session->smpte_duration (end - start, smpte);
3581 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
3584 case AudioClock::MinSec:
3585 /* XXX this stuff should be elsewhere.. */
3586 distance = end - start;
3587 frame_rate = session->frame_rate();
3588 hours = distance / (frame_rate * 3600);
3589 distance = distance % (frame_rate * 3600);
3590 mins = distance / (frame_rate * 60);
3591 distance = distance % (frame_rate * 60);
3592 secs = (float) distance / (float) frame_rate;
3593 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
3597 snprintf (buf, sizeof(buf), "%u", end - start);
3601 if (xpos >= 0 && ypos >=0) {
3602 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
3605 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
3607 show_verbose_canvas_cursor ();
3611 Editor::collect_new_region_view (RegionView* rv)
3613 latest_regionview = rv;
3617 Editor::start_selection_grab (ArdourCanvas::Item* item, GdkEvent* event)
3619 if (clicked_regionview == 0) {
3623 /* lets try to create new Region for the selection */
3625 vector<boost::shared_ptr<AudioRegion> > new_regions;
3626 create_region_from_selection (new_regions);
3628 if (new_regions.empty()) {
3632 /* XXX fix me one day to use all new regions */
3634 boost::shared_ptr<Region> region (new_regions.front());
3636 /* add it to the current stream/playlist.
3638 tricky: the streamview for the track will add a new regionview. we will
3639 catch the signal it sends when it creates the regionview to
3640 set the regionview we want to then drag.
3643 latest_regionview = 0;
3644 sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3646 /* A selection grab currently creates two undo/redo operations, one for
3647 creating the new region and another for moving it.
3650 begin_reversible_command (_("selection grab"));
3652 boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
3654 XMLNode *before = &(playlist->get_state());
3655 clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
3656 XMLNode *after = &(playlist->get_state());
3657 session->add_command(new MementoCommand<Playlist>(*playlist, before, after));
3659 commit_reversible_command ();
3663 if (latest_regionview == 0) {
3664 /* something went wrong */
3668 /* we need to deselect all other regionviews, and select this one
3669 i'm ignoring undo stuff, because the region creation will take care of it */
3670 selection->set (latest_regionview);
3672 drag_info.item = latest_regionview->get_canvas_group();
3673 drag_info.data = latest_regionview;
3674 drag_info.motion_callback = &Editor::region_drag_motion_callback;
3675 drag_info.finished_callback = &Editor::region_drag_finished_callback;
3679 drag_info.last_trackview = clicked_axisview;
3680 drag_info.last_frame_position = latest_regionview->region()->position();
3681 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
3683 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3687 Editor::cancel_selection ()
3689 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
3690 (*i)->hide_selection ();
3692 begin_reversible_command (_("cancel selection"));
3693 selection->clear ();
3694 clicked_selection = 0;
3695 commit_reversible_command ();
3699 Editor::start_selection_op (ArdourCanvas::Item* item, GdkEvent* event, SelectionOp op)
3701 nframes_t start = 0;
3708 drag_info.item = item;
3709 drag_info.motion_callback = &Editor::drag_selection;
3710 drag_info.finished_callback = &Editor::end_selection_op;
3715 case CreateSelection:
3716 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
3717 drag_info.copy = true;
3719 drag_info.copy = false;
3721 start_grab (event, selector_cursor);
3724 case SelectionStartTrim:
3725 if (clicked_axisview) {
3726 clicked_axisview->order_selection_trims (item, true);
3728 start_grab (event, trimmer_cursor);
3729 start = selection->time[clicked_selection].start;
3730 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
3733 case SelectionEndTrim:
3734 if (clicked_axisview) {
3735 clicked_axisview->order_selection_trims (item, false);
3737 start_grab (event, trimmer_cursor);
3738 end = selection->time[clicked_selection].end;
3739 drag_info.pointer_frame_offset = drag_info.grab_frame - end;
3743 start = selection->time[clicked_selection].start;
3745 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
3749 if (selection_op == SelectionMove) {
3750 show_verbose_time_cursor(start, 10);
3752 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
3757 Editor::drag_selection (ArdourCanvas::Item* item, GdkEvent* event)
3759 nframes_t start = 0;
3762 nframes_t pending_position;
3764 if ((int32_t) drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
3765 pending_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
3768 pending_position = 0;
3771 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3772 snap_to (pending_position);
3775 /* only alter selection if the current frame is
3776 different from the last frame position (adjusted)
3779 if (pending_position == drag_info.last_pointer_frame) return;
3781 switch (selection_op) {
3782 case CreateSelection:
3784 if (drag_info.first_move) {
3785 snap_to (drag_info.grab_frame);
3788 if (pending_position < drag_info.grab_frame) {
3789 start = pending_position;
3790 end = drag_info.grab_frame;
3792 end = pending_position;
3793 start = drag_info.grab_frame;
3796 /* first drag: Either add to the selection
3797 or create a new selection->
3800 if (drag_info.first_move) {
3802 begin_reversible_command (_("range selection"));
3804 if (drag_info.copy) {
3805 /* adding to the selection */
3806 clicked_selection = selection->add (start, end);
3807 drag_info.copy = false;
3809 /* new selection-> */
3810 clicked_selection = selection->set (clicked_axisview, start, end);
3815 case SelectionStartTrim:
3817 if (drag_info.first_move) {
3818 begin_reversible_command (_("trim selection start"));
3821 start = selection->time[clicked_selection].start;
3822 end = selection->time[clicked_selection].end;
3824 if (pending_position > end) {
3827 start = pending_position;
3831 case SelectionEndTrim:
3833 if (drag_info.first_move) {
3834 begin_reversible_command (_("trim selection end"));
3837 start = selection->time[clicked_selection].start;
3838 end = selection->time[clicked_selection].end;
3840 if (pending_position < start) {
3843 end = pending_position;
3850 if (drag_info.first_move) {
3851 begin_reversible_command (_("move selection"));
3854 start = selection->time[clicked_selection].start;
3855 end = selection->time[clicked_selection].end;
3857 length = end - start;
3859 start = pending_position;
3862 end = start + length;
3867 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
3868 start_canvas_autoscroll (1);
3872 selection->replace (clicked_selection, start, end);
3875 drag_info.last_pointer_frame = pending_position;
3876 drag_info.first_move = false;
3878 if (selection_op == SelectionMove) {
3879 show_verbose_time_cursor(start, 10);
3881 show_verbose_time_cursor(pending_position, 10);
3886 Editor::end_selection_op (ArdourCanvas::Item* item, GdkEvent* event)
3888 if (!drag_info.first_move) {
3889 drag_selection (item, event);
3890 /* XXX this is not object-oriented programming at all. ick */
3891 if (selection->time.consolidate()) {
3892 selection->TimeChanged ();
3894 commit_reversible_command ();
3896 /* just a click, no pointer movement.*/
3898 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
3900 selection->clear_time();
3905 /* XXX what happens if its a music selection? */
3906 session->set_audio_range (selection->time);
3907 stop_canvas_autoscroll ();
3911 Editor::start_trim (ArdourCanvas::Item* item, GdkEvent* event)
3914 TimeAxisView* tvp = clicked_axisview;
3915 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
3917 if (tv && tv->is_track()) {
3918 speed = tv->get_diskstream()->speed();
3921 nframes_t region_start = (nframes_t) (clicked_regionview->region()->position() / speed);
3922 nframes_t region_end = (nframes_t) (clicked_regionview->region()->last_frame() / speed);
3923 nframes_t region_length = (nframes_t) (clicked_regionview->region()->length() / speed);
3925 //drag_info.item = clicked_regionview->get_name_highlight();
3926 drag_info.item = item;
3927 drag_info.motion_callback = &Editor::trim_motion_callback;
3928 drag_info.finished_callback = &Editor::trim_finished_callback;
3930 start_grab (event, trimmer_cursor);
3932 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
3933 trim_op = ContentsTrim;
3935 /* These will get overridden for a point trim.*/
3936 if (drag_info.current_pointer_frame < (region_start + region_length/2)) {
3937 /* closer to start */
3938 trim_op = StartTrim;
3939 } else if (drag_info.current_pointer_frame > (region_end - region_length/2)) {
3947 show_verbose_time_cursor(region_start, 10);
3950 show_verbose_time_cursor(region_end, 10);
3953 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
3959 Editor::trim_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
3961 RegionView* rv = clicked_regionview;
3962 nframes_t frame_delta = 0;
3963 bool left_direction;
3964 bool obey_snap = !Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier());
3966 /* snap modifier works differently here..
3967 its' current state has to be passed to the
3968 various trim functions in order to work properly
3972 TimeAxisView* tvp = clicked_axisview;
3973 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
3974 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
3976 if (tv && tv->is_track()) {
3977 speed = tv->get_diskstream()->speed();
3980 if (drag_info.last_pointer_frame > drag_info.current_pointer_frame) {
3981 left_direction = true;
3983 left_direction = false;
3987 snap_to (drag_info.current_pointer_frame);
3990 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
3994 if (drag_info.first_move) {
4000 trim_type = "Region start trim";
4003 trim_type = "Region end trim";
4006 trim_type = "Region content trim";
4010 begin_reversible_command (trim_type);
4012 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4013 (*i)->fake_set_opaque(false);
4014 (*i)->region()->freeze ();
4016 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
4018 arv->temporarily_hide_envelope ();
4020 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4021 insert_result = motion_frozen_playlists.insert (pl);
4022 if (insert_result.second) {
4023 session->add_command(new MementoCommand<Playlist>(*pl, &pl->get_state(), 0));
4028 if (left_direction) {
4029 frame_delta = (drag_info.last_pointer_frame - drag_info.current_pointer_frame);
4031 frame_delta = (drag_info.current_pointer_frame - drag_info.last_pointer_frame);
4036 if ((left_direction == false) && (drag_info.current_pointer_frame <= rv->region()->first_frame()/speed)) {
4039 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4040 single_start_trim (**i, frame_delta, left_direction, obey_snap);
4046 if ((left_direction == true) && (drag_info.current_pointer_frame > (nframes_t) (rv->region()->last_frame()/speed))) {
4049 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4050 single_end_trim (**i, frame_delta, left_direction, obey_snap);
4057 bool swap_direction = false;
4059 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
4060 swap_direction = true;
4063 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4064 i != selection->regions.by_layer().end(); ++i)
4066 single_contents_trim (**i, frame_delta, left_direction, swap_direction, obey_snap);
4074 show_verbose_time_cursor((nframes_t) (rv->region()->position()/speed), 10);
4077 show_verbose_time_cursor((nframes_t) (rv->region()->last_frame()/speed), 10);
4080 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4084 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4085 drag_info.first_move = false;
4089 Editor::single_contents_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool swap_direction, bool obey_snap)
4091 boost::shared_ptr<Region> region (rv.region());
4093 if (region->locked()) {
4097 nframes_t new_bound;
4100 TimeAxisView* tvp = clicked_axisview;
4101 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4103 if (tv && tv->is_track()) {
4104 speed = tv->get_diskstream()->speed();
4107 if (left_direction) {
4108 if (swap_direction) {
4109 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4111 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4114 if (swap_direction) {
4115 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4117 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4122 snap_to (new_bound);
4124 region->trim_start ((nframes_t) (new_bound * speed), this);
4125 rv.region_changed (StartChanged);
4129 Editor::single_start_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool obey_snap)
4131 boost::shared_ptr<Region> region (rv.region());
4133 if (region->locked()) {
4137 nframes_t new_bound;
4140 TimeAxisView* tvp = clicked_axisview;
4141 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4143 if (tv && tv->is_track()) {
4144 speed = tv->get_diskstream()->speed();
4147 if (left_direction) {
4148 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4150 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4154 snap_to (new_bound, (left_direction ? 0 : 1));
4157 region->trim_front ((nframes_t) (new_bound * speed), this);
4159 rv.region_changed (Change (LengthChanged|PositionChanged|StartChanged));
4163 Editor::single_end_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool obey_snap)
4165 boost::shared_ptr<Region> region (rv.region());
4167 if (region->locked()) {
4171 nframes_t new_bound;
4174 TimeAxisView* tvp = clicked_axisview;
4175 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4177 if (tv && tv->is_track()) {
4178 speed = tv->get_diskstream()->speed();
4181 if (left_direction) {
4182 new_bound = (nframes_t) ((region->last_frame() + 1)/speed) - frame_delta;
4184 new_bound = (nframes_t) ((region->last_frame() + 1)/speed) + frame_delta;
4188 snap_to (new_bound);
4190 region->trim_end ((nframes_t) (new_bound * speed), this);
4191 rv.region_changed (LengthChanged);
4195 Editor::trim_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
4197 if (!drag_info.first_move) {
4198 trim_motion_callback (item, event);
4200 if (!clicked_regionview->get_selected()) {
4201 thaw_region_after_trim (*clicked_regionview);
4204 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4205 i != selection->regions.by_layer().end(); ++i)
4207 thaw_region_after_trim (**i);
4208 (*i)->fake_set_opaque (true);
4212 for (set<boost::shared_ptr<Playlist> >::iterator p = motion_frozen_playlists.begin(); p != motion_frozen_playlists.end(); ++p) {
4214 session->add_command (new MementoCommand<Playlist>(*(*p).get(), 0, &(*p)->get_state()));
4217 motion_frozen_playlists.clear ();
4219 commit_reversible_command();
4221 /* no mouse movement */
4227 Editor::point_trim (GdkEvent* event)
4229 RegionView* rv = clicked_regionview;
4230 nframes_t new_bound = drag_info.current_pointer_frame;
4232 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4233 snap_to (new_bound);
4236 /* Choose action dependant on which button was pressed */
4237 switch (event->button.button) {
4239 trim_op = StartTrim;
4240 begin_reversible_command (_("Start point trim"));
4242 if (rv->get_selected()) {
4244 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4245 i != selection->regions.by_layer().end(); ++i)
4247 if (!(*i)->region()->locked()) {
4248 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4249 XMLNode &before = pl->get_state();
4250 (*i)->region()->trim_front (new_bound, this);
4251 XMLNode &after = pl->get_state();
4252 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4258 if (!rv->region()->locked()) {
4259 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
4260 XMLNode &before = pl->get_state();
4261 rv->region()->trim_front (new_bound, this);
4262 XMLNode &after = pl->get_state();
4263 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4267 commit_reversible_command();
4272 begin_reversible_command (_("End point trim"));
4274 if (rv->get_selected()) {
4276 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
4278 if (!(*i)->region()->locked()) {
4279 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4280 XMLNode &before = pl->get_state();
4281 (*i)->region()->trim_end (new_bound, this);
4282 XMLNode &after = pl->get_state();
4283 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4289 if (!rv->region()->locked()) {
4290 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
4291 XMLNode &before = pl->get_state();
4292 rv->region()->trim_end (new_bound, this);
4293 XMLNode &after = pl->get_state();
4294 session->add_command (new MementoCommand<Playlist>(*pl.get(), &before, &after));
4298 commit_reversible_command();
4307 Editor::thaw_region_after_trim (RegionView& rv)
4309 boost::shared_ptr<Region> region (rv.region());
4311 if (region->locked()) {
4315 region->thaw (_("trimmed region"));
4316 XMLNode &after = region->playlist()->get_state();
4317 session->add_command (new MementoCommand<Playlist>(*(region->playlist()), 0, &after));
4319 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(&rv);
4321 arv->unhide_envelope ();
4325 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* event)
4330 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
4331 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
4335 Location* location = find_location_from_marker (marker, is_start);
4336 location->set_hidden (true, this);
4341 Editor::start_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event, RangeMarkerOp op)
4347 drag_info.item = item;
4348 drag_info.motion_callback = &Editor::drag_range_markerbar_op;
4349 drag_info.finished_callback = &Editor::end_range_markerbar_op;
4351 range_marker_op = op;
4353 if (!temp_location) {
4354 temp_location = new Location;
4358 case CreateRangeMarker:
4359 case CreateTransportMarker:
4361 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
4362 drag_info.copy = true;
4364 drag_info.copy = false;
4366 start_grab (event, selector_cursor);
4370 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4375 Editor::drag_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4377 nframes_t start = 0;
4379 ArdourCanvas::SimpleRect *crect = (range_marker_op == CreateRangeMarker) ? range_bar_drag_rect: transport_bar_drag_rect;
4381 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4382 snap_to (drag_info.current_pointer_frame);
4385 /* only alter selection if the current frame is
4386 different from the last frame position.
4389 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4391 switch (range_marker_op) {
4392 case CreateRangeMarker:
4393 case CreateTransportMarker:
4394 if (drag_info.first_move) {
4395 snap_to (drag_info.grab_frame);
4398 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4399 start = drag_info.current_pointer_frame;
4400 end = drag_info.grab_frame;
4402 end = drag_info.current_pointer_frame;
4403 start = drag_info.grab_frame;
4406 /* first drag: Either add to the selection
4407 or create a new selection.
4410 if (drag_info.first_move) {
4412 temp_location->set (start, end);
4416 update_marker_drag_item (temp_location);
4417 range_marker_drag_rect->show();
4418 range_marker_drag_rect->raise_to_top();
4424 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
4425 start_canvas_autoscroll (1);
4429 temp_location->set (start, end);
4431 double x1 = frame_to_pixel (start);
4432 double x2 = frame_to_pixel (end);
4433 crect->property_x1() = x1;
4434 crect->property_x2() = x2;
4436 update_marker_drag_item (temp_location);
4439 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4440 drag_info.first_move = false;
4442 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4447 Editor::end_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4449 Location * newloc = 0;
4452 if (!drag_info.first_move) {
4453 drag_range_markerbar_op (item, event);
4455 switch (range_marker_op) {
4456 case CreateRangeMarker:
4458 begin_reversible_command (_("new range marker"));
4459 XMLNode &before = session->locations()->get_state();
4460 session->locations()->next_available_name(rangename,"unnamed");
4461 newloc = new Location(temp_location->start(), temp_location->end(), rangename, Location::IsRangeMarker);
4462 session->locations()->add (newloc, true);
4463 XMLNode &after = session->locations()->get_state();
4464 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
4465 commit_reversible_command ();
4467 range_bar_drag_rect->hide();
4468 range_marker_drag_rect->hide();
4472 case CreateTransportMarker:
4473 // popup menu to pick loop or punch
4474 new_transport_marker_context_menu (&event->button, item);
4479 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
4481 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
4486 start = session->locations()->first_mark_before (drag_info.grab_frame);
4487 end = session->locations()->first_mark_after (drag_info.grab_frame);
4489 if (end == max_frames) {
4490 end = session->current_end_frame ();
4494 start = session->current_start_frame ();
4497 switch (mouse_mode) {
4499 /* find the two markers on either side and then make the selection from it */
4500 select_all_within (start, end, 0.0f, FLT_MAX, track_views, Selection::Set);
4504 /* find the two markers on either side of the click and make the range out of it */
4505 selection->set (0, start, end);
4514 stop_canvas_autoscroll ();
4520 Editor::start_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4522 drag_info.item = item;
4523 drag_info.motion_callback = &Editor::drag_mouse_zoom;
4524 drag_info.finished_callback = &Editor::end_mouse_zoom;
4526 start_grab (event, zoom_cursor);
4528 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4532 Editor::drag_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4537 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4538 snap_to (drag_info.current_pointer_frame);
4540 if (drag_info.first_move) {
4541 snap_to (drag_info.grab_frame);
4545 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4547 /* base start and end on initial click position */
4548 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4549 start = drag_info.current_pointer_frame;
4550 end = drag_info.grab_frame;
4552 end = drag_info.current_pointer_frame;
4553 start = drag_info.grab_frame;
4558 if (drag_info.first_move) {
4560 zoom_rect->raise_to_top();
4563 reposition_zoom_rect(start, end);
4565 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4566 drag_info.first_move = false;
4568 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4573 Editor::end_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4575 if (!drag_info.first_move) {
4576 drag_mouse_zoom (item, event);
4578 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
4579 temporal_zoom_by_frame (drag_info.grab_frame, drag_info.last_pointer_frame, "mouse zoom");
4581 temporal_zoom_by_frame (drag_info.last_pointer_frame, drag_info.grab_frame, "mouse zoom");
4584 temporal_zoom_to_frame (false, drag_info.grab_frame);
4586 temporal_zoom_step (false);
4587 center_screen (drag_info.grab_frame);
4595 Editor::reposition_zoom_rect (nframes_t start, nframes_t end)
4597 double x1 = frame_to_pixel (start);
4598 double x2 = frame_to_pixel (end);
4599 double y2 = full_canvas_height - 1.0;
4601 zoom_rect->property_x1() = x1;
4602 zoom_rect->property_y1() = 1.0;
4603 zoom_rect->property_x2() = x2;
4604 zoom_rect->property_y2() = y2;
4608 Editor::start_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4610 drag_info.item = item;
4611 drag_info.motion_callback = &Editor::drag_rubberband_select;
4612 drag_info.finished_callback = &Editor::end_rubberband_select;
4614 start_grab (event, cross_hair_cursor);
4616 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4620 Editor::drag_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4627 /* use a bigger drag threshold than the default */
4629 if (abs ((int) (drag_info.current_pointer_frame - drag_info.grab_frame)) < 8) {
4633 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4634 if (drag_info.first_move) {
4635 snap_to (drag_info.grab_frame);
4637 snap_to (drag_info.current_pointer_frame);
4640 /* base start and end on initial click position */
4642 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4643 start = drag_info.current_pointer_frame;
4644 end = drag_info.grab_frame;
4646 end = drag_info.current_pointer_frame;
4647 start = drag_info.grab_frame;
4650 if (drag_info.current_pointer_y < drag_info.grab_y) {
4651 y1 = drag_info.current_pointer_y;
4652 y2 = drag_info.grab_y;
4654 y2 = drag_info.current_pointer_y;
4655 y1 = drag_info.grab_y;
4659 if (start != end || y1 != y2) {
4661 double x1 = frame_to_pixel (start);
4662 double x2 = frame_to_pixel (end);
4664 rubberband_rect->property_x1() = x1;
4665 rubberband_rect->property_y1() = y1;
4666 rubberband_rect->property_x2() = x2;
4667 rubberband_rect->property_y2() = y2;
4669 rubberband_rect->show();
4670 rubberband_rect->raise_to_top();
4672 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4673 drag_info.first_move = false;
4675 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4680 Editor::end_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4682 if (!drag_info.first_move) {
4684 drag_rubberband_select (item, event);
4687 if (drag_info.current_pointer_y < drag_info.grab_y) {
4688 y1 = drag_info.current_pointer_y;
4689 y2 = drag_info.grab_y;
4692 y2 = drag_info.current_pointer_y;
4693 y1 = drag_info.grab_y;
4697 Selection::Operation op = Keyboard::selection_type (event->button.state);
4700 begin_reversible_command (_("rubberband selection"));
4702 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
4703 commit = select_all_within (drag_info.grab_frame, drag_info.last_pointer_frame, y1, y2, track_views, op);
4705 commit = select_all_within (drag_info.last_pointer_frame, drag_info.grab_frame, y1, y2, track_views, op);
4709 commit_reversible_command ();
4713 selection->clear_tracks();
4714 selection->clear_regions();
4715 selection->clear_points ();
4716 selection->clear_lines ();
4719 rubberband_rect->hide();
4724 Editor::mouse_rename_region (ArdourCanvas::Item* item, GdkEvent* event)
4726 using namespace Gtkmm2ext;
4728 ArdourPrompter prompter (false);
4730 prompter.set_prompt (_("Name for region:"));
4731 prompter.set_initial_text (clicked_regionview->region()->name());
4732 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
4733 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
4734 prompter.show_all ();
4735 switch (prompter.run ()) {
4736 case Gtk::RESPONSE_ACCEPT:
4738 prompter.get_result(str);
4740 clicked_regionview->region()->set_name (str);
4748 Editor::start_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
4750 drag_info.item = item;
4751 drag_info.motion_callback = &Editor::time_fx_motion;
4752 drag_info.finished_callback = &Editor::end_time_fx;
4756 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4760 Editor::time_fx_motion (ArdourCanvas::Item *item, GdkEvent* event)
4762 RegionView* rv = clicked_regionview;
4764 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4765 snap_to (drag_info.current_pointer_frame);
4768 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
4772 if (drag_info.current_pointer_frame > rv->region()->position()) {
4773 rv->get_time_axis_view().show_timestretch (rv->region()->position(), drag_info.current_pointer_frame);
4776 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4777 drag_info.first_move = false;
4779 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4783 Editor::end_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
4785 clicked_regionview->get_time_axis_view().hide_timestretch ();
4787 if (drag_info.first_move) {
4791 nframes_t newlen = drag_info.last_pointer_frame - clicked_regionview->region()->position();
4792 float percentage = (float) ((double) newlen - (double) clicked_regionview->region()->length()) / ((double) newlen) * 100.0f;
4794 begin_reversible_command (_("timestretch"));
4796 if (run_timestretch (selection->regions, percentage) == 0) {
4797 session->commit_reversible_command ();
4802 Editor::mouse_brush_insert_region (RegionView* rv, nframes_t pos)
4804 /* no brushing without a useful snap setting */
4807 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
4810 switch (snap_mode) {
4812 return; /* can't work because it allows region to be placed anywhere */
4817 switch (snap_type) {
4820 case SnapToEditCursor:
4827 /* don't brush a copy over the original */
4829 if (pos == rv->region()->position()) {
4833 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&arv->get_time_axis_view());
4835 if (rtv == 0 || !rtv->is_track()) {
4839 boost::shared_ptr<Playlist> playlist = rtv->playlist();
4840 double speed = rtv->get_diskstream()->speed();
4842 XMLNode &before = playlist->get_state();
4843 playlist->add_region (boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (arv->audio_region())), (nframes_t) (pos * speed));
4844 XMLNode &after = playlist->get_state();
4845 session->add_command(new MementoCommand<Playlist>(*playlist.get(), &before, &after));
4847 // playlist is frozen, so we have to update manually
4849 playlist->Modified(); /* EMIT SIGNAL */
4853 Editor::track_height_step_timeout ()
4856 struct timeval delta;
4858 gettimeofday (&now, 0);
4859 timersub (&now, &last_track_height_step_timestamp, &delta);
4861 if (delta.tv_sec * 1000000 + delta.tv_usec > 250000) { /* milliseconds */
4862 current_stepping_trackview = 0;