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) {
1010 switch (item_type) {
1012 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
1014 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::Shift|Keyboard::Alt))) {
1017 // Button2 click is unused
1030 // x_style_paste (where, 1.0);
1050 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1056 switch (item_type) {
1057 case GainControlPointItem:
1058 if (mouse_mode == MouseGain) {
1059 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1060 cp->set_visible (true);
1064 at_y = cp->get_y ();
1065 cp->item->i2w (at_x, at_y);
1069 fraction = 1.0 - (cp->get_y() / cp->line.height());
1071 set_verbose_canvas_cursor (cp->line.get_verbose_cursor_string (fraction), at_x, at_y);
1072 show_verbose_canvas_cursor ();
1074 if (is_drawable()) {
1075 track_canvas.get_window()->set_cursor (*fader_cursor);
1080 case GainAutomationControlPointItem:
1081 case PanAutomationControlPointItem:
1082 case RedirectAutomationControlPointItem:
1083 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1084 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1085 cp->set_visible (true);
1089 at_y = cp->get_y ();
1090 cp->item->i2w (at_x, at_y);
1094 fraction = 1.0 - (cp->get_y() / cp->line.height());
1096 set_verbose_canvas_cursor (cp->line.get_verbose_cursor_string (fraction), at_x, at_y);
1097 show_verbose_canvas_cursor ();
1099 if (is_drawable()) {
1100 track_canvas.get_window()->set_cursor (*fader_cursor);
1106 if (mouse_mode == MouseGain) {
1107 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1109 line->property_fill_color_rgba() = color_map[cEnteredGainLine];
1110 if (is_drawable()) {
1111 track_canvas.get_window()->set_cursor (*fader_cursor);
1116 case GainAutomationLineItem:
1117 case RedirectAutomationLineItem:
1118 case PanAutomationLineItem:
1119 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1121 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1123 line->property_fill_color_rgba() = color_map[cEnteredAutomationLine];
1125 if (is_drawable()) {
1126 track_canvas.get_window()->set_cursor (*fader_cursor);
1131 case RegionViewNameHighlight:
1132 if (is_drawable() && mouse_mode == MouseObject) {
1133 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1137 case StartSelectionTrimItem:
1138 case EndSelectionTrimItem:
1141 case ImageFrameHandleStartItem:
1142 case ImageFrameHandleEndItem:
1143 case MarkerViewHandleStartItem:
1144 case MarkerViewHandleEndItem:
1147 if (is_drawable()) {
1148 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1152 case EditCursorItem:
1153 case PlayheadCursorItem:
1154 if (is_drawable()) {
1155 track_canvas.get_window()->set_cursor (*grabber_cursor);
1159 case RegionViewName:
1161 /* when the name is not an active item, the entire name highlight is for trimming */
1163 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1164 if (mouse_mode == MouseObject && is_drawable()) {
1165 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1171 case AutomationTrackItem:
1172 if (is_drawable()) {
1173 Gdk::Cursor *cursor;
1174 switch (mouse_mode) {
1176 cursor = selector_cursor;
1179 cursor = zoom_cursor;
1182 cursor = cross_hair_cursor;
1186 track_canvas.get_window()->set_cursor (*cursor);
1188 AutomationTimeAxisView* atv;
1189 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1190 clear_entered_track = false;
1191 set_entered_track (atv);
1197 case RangeMarkerBarItem:
1198 case TransportMarkerBarItem:
1201 if (is_drawable()) {
1202 time_canvas.get_window()->set_cursor (*timebar_cursor);
1207 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1210 marker->set_color_rgba (color_map[cEnteredMarker]);
1212 case MeterMarkerItem:
1213 case TempoMarkerItem:
1214 if (is_drawable()) {
1215 time_canvas.get_window()->set_cursor (*timebar_cursor);
1218 case FadeInHandleItem:
1219 case FadeOutHandleItem:
1220 if (mouse_mode == MouseObject) {
1221 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1223 rect->property_fill_color_rgba() = 0;
1224 rect->property_outline_pixels() = 1;
1233 /* second pass to handle entered track status in a comprehensible way.
1236 switch (item_type) {
1238 case GainAutomationLineItem:
1239 case RedirectAutomationLineItem:
1240 case PanAutomationLineItem:
1241 case GainControlPointItem:
1242 case GainAutomationControlPointItem:
1243 case PanAutomationControlPointItem:
1244 case RedirectAutomationControlPointItem:
1245 /* these do not affect the current entered track state */
1246 clear_entered_track = false;
1249 case AutomationTrackItem:
1250 /* handled above already */
1254 set_entered_track (0);
1262 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1271 switch (item_type) {
1272 case GainControlPointItem:
1273 case GainAutomationControlPointItem:
1274 case PanAutomationControlPointItem:
1275 case RedirectAutomationControlPointItem:
1276 cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
1277 if (cp->line.npoints() > 1) {
1278 if (!cp->selected) {
1279 cp->set_visible (false);
1283 if (is_drawable()) {
1284 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1287 hide_verbose_canvas_cursor ();
1290 case RegionViewNameHighlight:
1291 case StartSelectionTrimItem:
1292 case EndSelectionTrimItem:
1293 case EditCursorItem:
1294 case PlayheadCursorItem:
1297 case ImageFrameHandleStartItem:
1298 case ImageFrameHandleEndItem:
1299 case MarkerViewHandleStartItem:
1300 case MarkerViewHandleEndItem:
1303 if (is_drawable()) {
1304 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1309 case GainAutomationLineItem:
1310 case RedirectAutomationLineItem:
1311 case PanAutomationLineItem:
1312 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1314 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1316 line->property_fill_color_rgba() = al->get_line_color();
1318 if (is_drawable()) {
1319 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1323 case RegionViewName:
1324 /* see enter_handler() for notes */
1325 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1326 if (is_drawable() && mouse_mode == MouseObject) {
1327 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1332 case RangeMarkerBarItem:
1333 case TransportMarkerBarItem:
1337 if (is_drawable()) {
1338 time_canvas.get_window()->set_cursor (*timebar_cursor);
1343 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1346 loc = find_location_from_marker (marker, is_start);
1347 if (loc) location_flags_changed (loc, this);
1349 case MeterMarkerItem:
1350 case TempoMarkerItem:
1352 if (is_drawable()) {
1353 time_canvas.get_window()->set_cursor (*timebar_cursor);
1358 case FadeInHandleItem:
1359 case FadeOutHandleItem:
1360 rv = static_cast<RegionView*>(item->get_data ("regionview"));
1362 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1364 rect->property_fill_color_rgba() = rv->get_fill_color();
1365 rect->property_outline_pixels() = 0;
1370 case AutomationTrackItem:
1371 if (is_drawable()) {
1372 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1373 clear_entered_track = true;
1374 Glib::signal_idle().connect (mem_fun(*this, &Editor::left_automation_track));
1386 Editor::left_automation_track ()
1388 if (clear_entered_track) {
1389 set_entered_track (0);
1390 clear_entered_track = false;
1396 Editor::motion_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type, bool from_autoscroll)
1400 /* We call this so that MOTION_NOTIFY events continue to be
1401 delivered to the canvas. We need to do this because we set
1402 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
1403 the density of the events, at the expense of a round-trip
1404 to the server. Given that this will mostly occur on cases
1405 where DISPLAY = :0.0, and given the cost of what the motion
1406 event might do, its a good tradeoff.
1409 track_canvas.get_pointer (x, y);
1411 if (current_stepping_trackview) {
1412 /* don't keep the persistent stepped trackview if the mouse moves */
1413 current_stepping_trackview = 0;
1414 step_timeout.disconnect ();
1417 if (session && session->actively_recording()) {
1418 /* Sorry. no dragging stuff around while we record */
1422 drag_info.item_type = item_type;
1423 drag_info.current_pointer_frame = event_frame (event, &drag_info.current_pointer_x,
1424 &drag_info.current_pointer_y);
1426 if (!from_autoscroll && drag_info.item) {
1427 /* item != 0 is the best test i can think of for dragging.
1429 if (!drag_info.move_threshold_passed) {
1431 bool x_threshold_passed = (abs ((int) (drag_info.current_pointer_x - drag_info.grab_x)) > 4);
1432 bool y_threshold_passed = (abs ((int) (drag_info.current_pointer_y - drag_info.grab_y)) > 4);
1434 drag_info.move_threshold_passed = (x_threshold_passed || y_threshold_passed);
1436 // and change the initial grab loc/frame if this drag info wants us to
1438 if (drag_info.want_move_threshold && drag_info.move_threshold_passed) {
1439 drag_info.grab_frame = drag_info.current_pointer_frame;
1440 drag_info.grab_x = drag_info.current_pointer_x;
1441 drag_info.grab_y = drag_info.current_pointer_y;
1442 drag_info.last_pointer_frame = drag_info.grab_frame;
1443 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
1448 switch (item_type) {
1449 case PlayheadCursorItem:
1450 case EditCursorItem:
1452 case GainControlPointItem:
1453 case RedirectAutomationControlPointItem:
1454 case GainAutomationControlPointItem:
1455 case PanAutomationControlPointItem:
1456 case TempoMarkerItem:
1457 case MeterMarkerItem:
1458 case RegionViewNameHighlight:
1459 case StartSelectionTrimItem:
1460 case EndSelectionTrimItem:
1463 case RedirectAutomationLineItem:
1464 case GainAutomationLineItem:
1465 case PanAutomationLineItem:
1466 case FadeInHandleItem:
1467 case FadeOutHandleItem:
1470 case ImageFrameHandleStartItem:
1471 case ImageFrameHandleEndItem:
1472 case MarkerViewHandleStartItem:
1473 case MarkerViewHandleEndItem:
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);
1490 switch (mouse_mode) {
1495 if (drag_info.item && (event->motion.state & GDK_BUTTON1_MASK ||
1496 (event->motion.state & GDK_BUTTON2_MASK))) {
1497 if (!from_autoscroll) {
1498 maybe_autoscroll (event);
1500 (this->*(drag_info.motion_callback)) (item, event);
1511 track_canvas_motion (event);
1512 // drag_info.last_pointer_frame = drag_info.current_pointer_frame;
1520 Editor::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
1522 if (drag_info.item == 0) {
1523 fatal << _("programming error: start_grab called without drag item") << endmsg;
1529 cursor = grabber_cursor;
1532 // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
1534 if (event->button.button == 2) {
1535 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Alt)) {
1536 drag_info.y_constrained = true;
1537 drag_info.x_constrained = false;
1539 drag_info.y_constrained = false;
1540 drag_info.x_constrained = true;
1543 drag_info.x_constrained = false;
1544 drag_info.y_constrained = false;
1547 drag_info.grab_frame = event_frame(event, &drag_info.grab_x, &drag_info.grab_y);
1548 drag_info.last_pointer_frame = drag_info.grab_frame;
1549 drag_info.current_pointer_frame = drag_info.grab_frame;
1550 drag_info.current_pointer_x = drag_info.grab_x;
1551 drag_info.current_pointer_y = drag_info.grab_y;
1552 drag_info.cumulative_x_drag = 0;
1553 drag_info.cumulative_y_drag = 0;
1554 drag_info.first_move = true;
1555 drag_info.move_threshold_passed = false;
1556 drag_info.want_move_threshold = false;
1557 drag_info.pointer_frame_offset = 0;
1558 drag_info.brushing = false;
1559 drag_info.copied_location = 0;
1561 drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK,
1563 event->button.time);
1565 if (session && session->transport_rolling()) {
1566 drag_info.was_rolling = true;
1568 drag_info.was_rolling = false;
1571 switch (snap_type) {
1572 case SnapToRegionStart:
1573 case SnapToRegionEnd:
1574 case SnapToRegionSync:
1575 case SnapToRegionBoundary:
1576 build_region_boundary_cache ();
1584 Editor::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t time)
1586 drag_info.item->ungrab (0);
1587 drag_info.item = new_item;
1590 cursor = grabber_cursor;
1593 drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK, *cursor, time);
1597 Editor::end_grab (ArdourCanvas::Item* item, GdkEvent* event)
1599 bool did_drag = false;
1601 stop_canvas_autoscroll ();
1603 if (drag_info.item == 0) {
1607 drag_info.item->ungrab (event->button.time);
1609 if (drag_info.finished_callback) {
1610 (this->*(drag_info.finished_callback)) (item, event);
1613 did_drag = !drag_info.first_move;
1615 hide_verbose_canvas_cursor();
1618 drag_info.copy = false;
1619 drag_info.motion_callback = 0;
1620 drag_info.finished_callback = 0;
1621 drag_info.last_trackview = 0;
1622 drag_info.last_frame_position = 0;
1623 drag_info.grab_frame = 0;
1624 drag_info.last_pointer_frame = 0;
1625 drag_info.current_pointer_frame = 0;
1626 drag_info.brushing = false;
1628 if (drag_info.copied_location) {
1629 delete drag_info.copied_location;
1630 drag_info.copied_location = 0;
1637 Editor::set_edit_cursor (GdkEvent* event)
1639 nframes_t pointer_frame = event_frame (event);
1641 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1642 if (snap_type != SnapToEditCursor) {
1643 snap_to (pointer_frame);
1647 edit_cursor->set_position (pointer_frame);
1648 edit_cursor_clock.set (pointer_frame);
1652 Editor::set_playhead_cursor (GdkEvent* event)
1654 nframes_t pointer_frame = event_frame (event);
1656 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1657 snap_to (pointer_frame);
1661 session->request_locate (pointer_frame, session->transport_rolling());
1666 Editor::start_fade_in_grab (ArdourCanvas::Item* item, GdkEvent* event)
1668 drag_info.item = item;
1669 drag_info.motion_callback = &Editor::fade_in_drag_motion_callback;
1670 drag_info.finished_callback = &Editor::fade_in_drag_finished_callback;
1674 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
1675 fatal << _("programming error: fade in canvas item has no regionview data pointer!") << endmsg;
1679 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1681 drag_info.pointer_frame_offset = drag_info.grab_frame - ((nframes_t) arv->audio_region()->fade_in().back()->when + arv->region()->position());
1685 Editor::fade_in_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1687 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1689 nframes_t fade_length;
1691 if ((int32_t)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1692 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1698 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1702 if (pos < (arv->region()->position() + 64)) {
1703 fade_length = 64; // this should be a minimum defined somewhere
1704 } else if (pos > arv->region()->last_frame()) {
1705 fade_length = arv->region()->length();
1707 fade_length = pos - arv->region()->position();
1709 /* mapover the region selection */
1711 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1713 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1719 tmp->reset_fade_in_shape_width (fade_length);
1722 show_verbose_duration_cursor (arv->region()->position(), arv->region()->position() + fade_length, 10);
1724 drag_info.first_move = false;
1728 Editor::fade_in_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1730 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1732 nframes_t fade_length;
1734 if (drag_info.first_move) return;
1736 if ((int32_t)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1737 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1742 if (pos < (arv->region()->position() + 64)) {
1743 fade_length = 64; // this should be a minimum defined somewhere
1744 } else if (pos > arv->region()->last_frame()) {
1745 fade_length = arv->region()->length();
1747 fade_length = pos - arv->region()->position();
1750 begin_reversible_command (_("change fade in length"));
1752 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1754 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1760 AutomationList& alist = tmp->audio_region()->fade_in();
1761 XMLNode &before = alist.get_state();
1763 tmp->audio_region()->set_fade_in_length (fade_length);
1765 XMLNode &after = alist.get_state();
1766 session->add_command(new MementoCommand<AutomationList>(alist, &before, &after));
1769 commit_reversible_command ();
1773 Editor::start_fade_out_grab (ArdourCanvas::Item* item, GdkEvent* event)
1775 drag_info.item = item;
1776 drag_info.motion_callback = &Editor::fade_out_drag_motion_callback;
1777 drag_info.finished_callback = &Editor::fade_out_drag_finished_callback;
1781 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
1782 fatal << _("programming error: fade out canvas item has no regionview data pointer!") << endmsg;
1786 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1788 drag_info.pointer_frame_offset = drag_info.grab_frame - (arv->region()->length() - (nframes_t) arv->audio_region()->fade_out().back()->when + arv->region()->position());
1792 Editor::fade_out_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1794 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1796 nframes_t fade_length;
1798 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1799 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1805 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1809 if (pos > (arv->region()->last_frame() - 64)) {
1810 fade_length = 64; // this should really be a minimum fade defined somewhere
1812 else if (pos < arv->region()->position()) {
1813 fade_length = arv->region()->length();
1816 fade_length = arv->region()->last_frame() - pos;
1819 /* mapover the region selection */
1821 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1823 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1829 tmp->reset_fade_out_shape_width (fade_length);
1832 show_verbose_duration_cursor (arv->region()->last_frame() - fade_length, arv->region()->last_frame(), 10);
1834 drag_info.first_move = false;
1838 Editor::fade_out_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1840 if (drag_info.first_move) return;
1842 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1844 nframes_t fade_length;
1846 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1847 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1853 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1857 if (pos > (arv->region()->last_frame() - 64)) {
1858 fade_length = 64; // this should really be a minimum fade defined somewhere
1860 else if (pos < arv->region()->position()) {
1861 fade_length = arv->region()->length();
1864 fade_length = arv->region()->last_frame() - pos;
1867 begin_reversible_command (_("change fade out length"));
1869 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1871 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1877 AutomationList& alist = tmp->audio_region()->fade_out();
1878 XMLNode &before = alist.get_state();
1880 tmp->audio_region()->set_fade_out_length (fade_length);
1882 XMLNode &after = alist.get_state();
1883 session->add_command(new MementoCommand<AutomationList>(alist, &before, &after));
1886 commit_reversible_command ();
1890 Editor::start_cursor_grab (ArdourCanvas::Item* item, GdkEvent* event)
1892 drag_info.item = item;
1893 drag_info.motion_callback = &Editor::cursor_drag_motion_callback;
1894 drag_info.finished_callback = &Editor::cursor_drag_finished_callback;
1898 if ((drag_info.data = (item->get_data ("cursor"))) == 0) {
1899 fatal << _("programming error: cursor canvas item has no cursor data pointer!") << endmsg;
1903 Cursor* cursor = (Cursor *) drag_info.data;
1905 if (cursor == playhead_cursor) {
1906 _dragging_playhead = true;
1908 if (session && drag_info.was_rolling) {
1909 session->request_stop ();
1912 if (session && session->is_auditioning()) {
1913 session->cancel_audition ();
1917 drag_info.pointer_frame_offset = drag_info.grab_frame - cursor->current_frame;
1919 show_verbose_time_cursor (cursor->current_frame, 10);
1923 Editor::cursor_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1925 Cursor* cursor = (Cursor *) drag_info.data;
1926 nframes_t adjusted_frame;
1928 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1929 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1935 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1936 if (cursor != edit_cursor || snap_type != SnapToEditCursor) {
1937 snap_to (adjusted_frame);
1941 if (adjusted_frame == drag_info.last_pointer_frame) return;
1943 cursor->set_position (adjusted_frame);
1945 if (cursor == edit_cursor) {
1946 edit_cursor_clock.set (cursor->current_frame);
1948 UpdateAllTransportClocks (cursor->current_frame);
1951 show_verbose_time_cursor (cursor->current_frame, 10);
1953 drag_info.last_pointer_frame = adjusted_frame;
1954 drag_info.first_move = false;
1958 Editor::cursor_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1960 if (drag_info.first_move) return;
1962 cursor_drag_motion_callback (item, event);
1964 _dragging_playhead = false;
1966 if (item == &playhead_cursor->canvas_item) {
1968 session->request_locate (playhead_cursor->current_frame, drag_info.was_rolling);
1970 } else if (item == &edit_cursor->canvas_item) {
1971 edit_cursor->set_position (edit_cursor->current_frame);
1972 edit_cursor_clock.set (edit_cursor->current_frame);
1977 Editor::update_marker_drag_item (Location *location)
1979 double x1 = frame_to_pixel (location->start());
1980 double x2 = frame_to_pixel (location->end());
1982 if (location->is_mark()) {
1983 marker_drag_line_points.front().set_x(x1);
1984 marker_drag_line_points.back().set_x(x1);
1985 marker_drag_line->property_points() = marker_drag_line_points;
1988 range_marker_drag_rect->property_x1() = x1;
1989 range_marker_drag_rect->property_x2() = x2;
1994 Editor::start_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
1998 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1999 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2005 Location *location = find_location_from_marker (marker, is_start);
2007 drag_info.item = item;
2008 drag_info.data = marker;
2009 drag_info.motion_callback = &Editor::marker_drag_motion_callback;
2010 drag_info.finished_callback = &Editor::marker_drag_finished_callback;
2014 drag_info.copied_location = new Location (*location);
2015 drag_info.pointer_frame_offset = drag_info.grab_frame - (is_start ? location->start() : location->end());
2017 update_marker_drag_item (location);
2019 if (location->is_mark()) {
2020 marker_drag_line->show();
2021 marker_drag_line->raise_to_top();
2024 range_marker_drag_rect->show();
2025 range_marker_drag_rect->raise_to_top();
2028 if (is_start) show_verbose_time_cursor (location->start(), 10);
2029 else show_verbose_time_cursor (location->end(), 10);
2033 Editor::marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2036 Marker* marker = (Marker *) drag_info.data;
2037 Location *real_location;
2038 Location *copy_location;
2040 bool move_both = false;
2044 if (drag_info.pointer_frame_offset <= (long) drag_info.current_pointer_frame) {
2045 newframe = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2051 nframes_t next = newframe;
2053 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2054 snap_to (newframe, 0, true);
2057 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
2061 /* call this to find out if its the start or end */
2063 real_location = find_location_from_marker (marker, is_start);
2065 /* use the copy that we're "dragging" around */
2067 copy_location = drag_info.copied_location;
2069 f_delta = copy_location->end() - copy_location->start();
2071 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
2075 if (copy_location->is_mark()) {
2078 copy_location->set_start (newframe);
2082 if (is_start) { // start-of-range marker
2085 copy_location->set_start (newframe);
2086 copy_location->set_end (newframe + f_delta);
2087 } else if (newframe < copy_location->end()) {
2088 copy_location->set_start (newframe);
2090 snap_to (next, 1, true);
2091 copy_location->set_end (next);
2092 copy_location->set_start (newframe);
2095 } else { // end marker
2098 copy_location->set_end (newframe);
2099 copy_location->set_start (newframe - f_delta);
2100 } else if (newframe > copy_location->start()) {
2101 copy_location->set_end (newframe);
2103 } else if (newframe > 0) {
2104 snap_to (next, -1, true);
2105 copy_location->set_start (next);
2106 copy_location->set_end (newframe);
2111 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
2112 drag_info.first_move = false;
2114 update_marker_drag_item (copy_location);
2116 LocationMarkers* lm = find_location_markers (real_location);
2117 lm->set_position (copy_location->start(), copy_location->end());
2119 show_verbose_time_cursor (newframe, 10);
2123 Editor::marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2125 if (drag_info.first_move) {
2126 marker_drag_motion_callback (item, event);
2130 Marker* marker = (Marker *) drag_info.data;
2134 begin_reversible_command ( _("move marker") );
2135 XMLNode &before = session->locations()->get_state();
2137 Location * location = find_location_from_marker (marker, is_start);
2140 if (location->is_mark()) {
2141 location->set_start (drag_info.copied_location->start());
2143 location->set (drag_info.copied_location->start(), drag_info.copied_location->end());
2147 XMLNode &after = session->locations()->get_state();
2148 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
2149 commit_reversible_command ();
2151 marker_drag_line->hide();
2152 range_marker_drag_rect->hide();
2156 Editor::start_meter_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2159 MeterMarker* meter_marker;
2161 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2162 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2166 meter_marker = dynamic_cast<MeterMarker*> (marker);
2168 MetricSection& section (meter_marker->meter());
2170 if (!section.movable()) {
2174 drag_info.item = item;
2175 drag_info.data = marker;
2176 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2177 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2181 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2183 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2187 Editor::start_meter_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2190 MeterMarker* meter_marker;
2192 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2193 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2197 meter_marker = dynamic_cast<MeterMarker*> (marker);
2199 // create a dummy marker for visual representation of moving the copy.
2200 // The actual copying is not done before we reach the finish callback.
2202 snprintf (name, sizeof(name), "%g/%g", meter_marker->meter().beats_per_bar(), meter_marker->meter().note_divisor ());
2203 MeterMarker* new_marker = new MeterMarker(*this, *meter_group, color_map[cMeterMarker], name,
2204 *new MeterSection(meter_marker->meter()));
2206 drag_info.item = &new_marker->the_item();
2207 drag_info.copy = true;
2208 drag_info.data = new_marker;
2209 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2210 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2214 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2216 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2220 Editor::meter_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2222 MeterMarker* marker = (MeterMarker *) drag_info.data;
2223 nframes_t adjusted_frame;
2225 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2226 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2232 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2233 snap_to (adjusted_frame);
2236 if (adjusted_frame == drag_info.last_pointer_frame) return;
2238 marker->set_position (adjusted_frame);
2241 drag_info.last_pointer_frame = adjusted_frame;
2242 drag_info.first_move = false;
2244 show_verbose_time_cursor (adjusted_frame, 10);
2248 Editor::meter_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2250 if (drag_info.first_move) return;
2252 meter_marker_drag_motion_callback (drag_info.item, event);
2254 MeterMarker* marker = (MeterMarker *) drag_info.data;
2257 TempoMap& map (session->tempo_map());
2258 map.bbt_time (drag_info.last_pointer_frame, when);
2260 if (drag_info.copy == true) {
2261 begin_reversible_command (_("copy meter mark"));
2262 XMLNode &before = map.get_state();
2263 map.add_meter (marker->meter(), when);
2264 XMLNode &after = map.get_state();
2265 session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2266 commit_reversible_command ();
2268 // delete the dummy marker we used for visual representation of copying.
2269 // a new visual marker will show up automatically.
2272 begin_reversible_command (_("move meter mark"));
2273 XMLNode &before = map.get_state();
2274 map.move_meter (marker->meter(), when);
2275 XMLNode &after = map.get_state();
2276 session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2277 commit_reversible_command ();
2282 Editor::start_tempo_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2285 TempoMarker* tempo_marker;
2287 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2288 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2292 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2293 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2297 MetricSection& section (tempo_marker->tempo());
2299 if (!section.movable()) {
2303 drag_info.item = item;
2304 drag_info.data = marker;
2305 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2306 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2310 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2311 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2315 Editor::start_tempo_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2318 TempoMarker* tempo_marker;
2320 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2321 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2325 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2326 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2330 // create a dummy marker for visual representation of moving the copy.
2331 // The actual copying is not done before we reach the finish callback.
2333 snprintf (name, sizeof (name), "%.2f", tempo_marker->tempo().beats_per_minute());
2334 TempoMarker* new_marker = new TempoMarker(*this, *tempo_group, color_map[cTempoMarker], name,
2335 *new TempoSection(tempo_marker->tempo()));
2337 drag_info.item = &new_marker->the_item();
2338 drag_info.copy = true;
2339 drag_info.data = new_marker;
2340 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2341 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2345 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2347 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2351 Editor::tempo_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2353 TempoMarker* marker = (TempoMarker *) drag_info.data;
2354 nframes_t adjusted_frame;
2356 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2357 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2363 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2364 snap_to (adjusted_frame);
2367 if (adjusted_frame == drag_info.last_pointer_frame) return;
2369 /* OK, we've moved far enough to make it worth actually move the thing. */
2371 marker->set_position (adjusted_frame);
2373 show_verbose_time_cursor (adjusted_frame, 10);
2375 drag_info.last_pointer_frame = adjusted_frame;
2376 drag_info.first_move = false;
2380 Editor::tempo_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2382 if (drag_info.first_move) return;
2384 tempo_marker_drag_motion_callback (drag_info.item, event);
2386 TempoMarker* marker = (TempoMarker *) drag_info.data;
2389 TempoMap& map (session->tempo_map());
2390 map.bbt_time (drag_info.last_pointer_frame, when);
2392 if (drag_info.copy == true) {
2393 begin_reversible_command (_("copy tempo mark"));
2394 XMLNode &before = map.get_state();
2395 map.add_tempo (marker->tempo(), when);
2396 XMLNode &after = map.get_state();
2397 session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2398 commit_reversible_command ();
2400 // delete the dummy marker we used for visual representation of copying.
2401 // a new visual marker will show up automatically.
2404 begin_reversible_command (_("move tempo mark"));
2405 XMLNode &before = map.get_state();
2406 map.move_tempo (marker->tempo(), when);
2407 XMLNode &after = map.get_state();
2408 session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2409 commit_reversible_command ();
2414 Editor::remove_gain_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 // We shouldn't remove the first or last gain point
2424 if (control_point->line.is_last_point(*control_point) ||
2425 control_point->line.is_first_point(*control_point)) {
2429 control_point->line.remove_point (*control_point);
2433 Editor::remove_control_point (ArdourCanvas::Item*item, GdkEvent* event)
2435 ControlPoint* control_point;
2437 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2438 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2442 control_point->line.remove_point (*control_point);
2446 Editor::start_control_point_grab (ArdourCanvas::Item* item, GdkEvent* event)
2448 ControlPoint* control_point;
2450 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2451 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2455 drag_info.item = item;
2456 drag_info.data = control_point;
2457 drag_info.motion_callback = &Editor::control_point_drag_motion_callback;
2458 drag_info.finished_callback = &Editor::control_point_drag_finished_callback;
2460 start_grab (event, fader_cursor);
2462 control_point->line.start_drag (control_point, drag_info.grab_frame, 0);
2464 float fraction = 1.0 - (control_point->get_y() / control_point->line.height());
2465 set_verbose_canvas_cursor (control_point->line.get_verbose_cursor_string (fraction),
2466 drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
2468 show_verbose_canvas_cursor ();
2472 Editor::control_point_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2474 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2476 double cx = drag_info.current_pointer_x;
2477 double cy = drag_info.current_pointer_y;
2479 drag_info.cumulative_x_drag = cx - drag_info.grab_x ;
2480 drag_info.cumulative_y_drag = cy - drag_info.grab_y ;
2482 if (drag_info.x_constrained) {
2483 cx = drag_info.grab_x;
2485 if (drag_info.y_constrained) {
2486 cy = drag_info.grab_y;
2489 cp->line.parent_group().w2i (cx, cy);
2493 cy = min ((double) cp->line.height(), cy);
2495 //translate cx to frames
2496 nframes_t cx_frames = unit_to_frame (cx);
2498 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier()) && !drag_info.x_constrained) {
2499 snap_to (cx_frames);
2502 float fraction = 1.0 - (cy / cp->line.height());
2506 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
2512 cp->line.point_drag (*cp, cx_frames , fraction, push);
2514 set_verbose_canvas_cursor_text (cp->line.get_verbose_cursor_string (fraction));
2516 drag_info.first_move = false;
2520 Editor::control_point_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2522 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2524 if (drag_info.first_move) {
2528 if ((event->type == GDK_BUTTON_RELEASE) && (event->button.button == 1) && Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
2529 reset_point_selection ();
2533 control_point_drag_motion_callback (item, event);
2535 cp->line.end_drag (cp);
2539 Editor::start_line_grab_from_regionview (ArdourCanvas::Item* item, GdkEvent* event)
2541 switch (mouse_mode) {
2543 assert(dynamic_cast<AudioRegionView*>(clicked_regionview));
2544 start_line_grab (dynamic_cast<AudioRegionView*>(clicked_regionview)->get_gain_line(), event);
2552 Editor::start_line_grab_from_line (ArdourCanvas::Item* item, GdkEvent* event)
2556 if ((al = reinterpret_cast<AutomationLine*> (item->get_data ("line"))) == 0) {
2557 fatal << _("programming error: line canvas item has no line pointer!") << endmsg;
2561 start_line_grab (al, event);
2565 Editor::start_line_grab (AutomationLine* line, GdkEvent* event)
2569 nframes_t frame_within_region;
2571 /* need to get x coordinate in terms of parent (TimeAxisItemView)
2575 cx = event->button.x;
2576 cy = event->button.y;
2577 line->parent_group().w2i (cx, cy);
2578 frame_within_region = (nframes_t) floor (cx * frames_per_unit);
2580 if (!line->control_points_adjacent (frame_within_region, current_line_drag_info.before,
2581 current_line_drag_info.after)) {
2582 /* no adjacent points */
2586 drag_info.item = &line->grab_item();
2587 drag_info.data = line;
2588 drag_info.motion_callback = &Editor::line_drag_motion_callback;
2589 drag_info.finished_callback = &Editor::line_drag_finished_callback;
2591 start_grab (event, fader_cursor);
2593 double fraction = 1.0 - (cy / line->height());
2595 line->start_drag (0, drag_info.grab_frame, fraction);
2597 set_verbose_canvas_cursor (line->get_verbose_cursor_string (fraction),
2598 drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
2599 show_verbose_canvas_cursor ();
2603 Editor::line_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2605 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
2606 double cx = drag_info.current_pointer_x;
2607 double cy = drag_info.current_pointer_y;
2609 line->parent_group().w2i (cx, cy);
2612 fraction = 1.0 - (cy / line->height());
2616 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
2622 line->line_drag (current_line_drag_info.before, current_line_drag_info.after, fraction, push);
2624 set_verbose_canvas_cursor_text (line->get_verbose_cursor_string (fraction));
2628 Editor::line_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2630 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
2631 line_drag_motion_callback (item, event);
2636 Editor::start_region_grab (ArdourCanvas::Item* item, GdkEvent* event)
2638 if (selection->regions.empty() || clicked_regionview == 0) {
2642 drag_info.copy = false;
2643 drag_info.item = item;
2644 drag_info.data = clicked_regionview;
2645 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2646 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2651 TimeAxisView* tvp = clicked_axisview;
2652 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2654 if (tv && tv->is_track()) {
2655 speed = tv->get_diskstream()->speed();
2658 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2659 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2660 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2661 // we want a move threshold
2662 drag_info.want_move_threshold = true;
2664 show_verbose_time_cursor (drag_info.last_frame_position, 10);
2666 begin_reversible_command (_("move region(s)"));
2670 Editor::start_region_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2672 if (selection->regions.empty() || clicked_regionview == 0) {
2676 drag_info.copy = true;
2677 drag_info.item = item;
2678 drag_info.data = clicked_regionview;
2682 TimeAxisView* tv = &clicked_regionview->get_time_axis_view();
2683 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
2686 if (rtv && rtv->is_track()) {
2687 speed = rtv->get_diskstream()->speed();
2690 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2691 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2692 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2693 // we want a move threshold
2694 drag_info.want_move_threshold = true;
2695 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2696 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2697 show_verbose_time_cursor (drag_info.last_frame_position, 10);
2701 Editor::start_region_brush_grab (ArdourCanvas::Item* item, GdkEvent* event)
2703 if (selection->regions.empty() || clicked_regionview == 0) {
2707 drag_info.copy = false;
2708 drag_info.item = item;
2709 drag_info.data = clicked_regionview;
2710 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2711 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2716 TimeAxisView* tvp = clicked_axisview;
2717 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2719 if (tv && tv->is_track()) {
2720 speed = tv->get_diskstream()->speed();
2723 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2724 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2725 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2726 // we want a move threshold
2727 drag_info.want_move_threshold = true;
2728 drag_info.brushing = true;
2730 begin_reversible_command (_("Drag region brush"));
2734 Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2738 RegionView* rv = reinterpret_cast<RegionView*> (drag_info.data);
2739 nframes_t pending_region_position = 0;
2740 int32_t pointer_y_span = 0, canvas_pointer_y_span = 0, original_pointer_order;
2741 int32_t visible_y_high = 0, visible_y_low = 512; //high meaning higher numbered.. not the height on the screen
2742 bool clamp_y_axis = false;
2743 vector<int32_t> height_list(512) ;
2744 vector<int32_t>::iterator j;
2746 if (drag_info.copy && drag_info.move_threshold_passed && drag_info.want_move_threshold) {
2748 drag_info.want_move_threshold = false; // don't copy again
2750 /* duplicate the region(s) */
2752 vector<RegionView*> new_regionviews;
2754 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
2760 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
2761 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
2764 nrv = new AudioRegionView (*arv);
2766 nrv = new MidiRegionView (*mrv);
2771 nrv->get_canvas_group()->show ();
2773 new_regionviews.push_back (nrv);
2776 if (new_regionviews.empty()) {
2780 /* reset selection to new regionviews */
2782 selection->set (new_regionviews);
2784 /* reset drag_info data to reflect the fact that we are dragging the copies */
2786 drag_info.data = new_regionviews.front();
2788 swap_grab (new_regionviews.front()->get_canvas_group (), 0, event->motion.time);
2791 /* Which trackview is this ? */
2793 TimeAxisView* tvp = trackview_by_y_position (drag_info.current_pointer_y);
2794 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2796 /* The region motion is only processed if the pointer is over
2800 if (!tv || !tv->is_track()) {
2801 /* To make sure we hide the verbose canvas cursor when the mouse is
2802 not held over a track.
2804 hide_verbose_canvas_cursor ();
2808 original_pointer_order = drag_info.last_trackview->order;
2810 /************************************************************
2812 ************************************************************/
2814 if (drag_info.brushing) {
2815 clamp_y_axis = true;
2820 if ((pointer_y_span = (drag_info.last_trackview->order - tv->order)) != 0) {
2822 int32_t children = 0, numtracks = 0;
2823 // XXX hard coding track limit, oh my, so very very bad
2824 bitset <1024> tracks (0x00);
2825 /* get a bitmask representing the visible tracks */
2827 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2828 TimeAxisView *tracklist_timeview;
2829 tracklist_timeview = (*i);
2830 RouteTimeAxisView* rtv2 = dynamic_cast<RouteTimeAxisView*>(tracklist_timeview);
2831 list<TimeAxisView*> children_list;
2833 /* zeroes are audio tracks. ones are other types. */
2835 if (!rtv2->hidden()) {
2837 if (visible_y_high < rtv2->order) {
2838 visible_y_high = rtv2->order;
2840 if (visible_y_low > rtv2->order) {
2841 visible_y_low = rtv2->order;
2844 if (!rtv2->is_track()) {
2845 tracks = tracks |= (0x01 << rtv2->order);
2848 height_list[rtv2->order] = (*i)->height;
2850 if ((children_list = rtv2->get_child_list()).size() > 0) {
2851 for (list<TimeAxisView*>::iterator j = children_list.begin(); j != children_list.end(); ++j) {
2852 tracks = tracks |= (0x01 << (rtv2->order + children));
2853 height_list[rtv2->order + children] = (*j)->height;
2861 /* find the actual span according to the canvas */
2863 canvas_pointer_y_span = pointer_y_span;
2864 if (drag_info.last_trackview->order >= tv->order) {
2866 for (y = tv->order; y < drag_info.last_trackview->order; y++) {
2867 if (height_list[y] == 0 ) {
2868 canvas_pointer_y_span--;
2873 for (y = drag_info.last_trackview->order;y <= tv->order; y++) {
2874 if ( height_list[y] == 0 ) {
2875 canvas_pointer_y_span++;
2880 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
2881 RegionView* rv2 = (*i);
2882 double ix1, ix2, iy1, iy2;
2885 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
2886 rv2->get_canvas_group()->i2w (ix1, iy1);
2887 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
2888 RouteTimeAxisView* rtv2 = dynamic_cast<RouteTimeAxisView*>(tvp2);
2890 if (rtv2->order != original_pointer_order) {
2891 /* this isn't the pointer track */
2893 if (canvas_pointer_y_span > 0) {
2895 /* moving up the canvas */
2896 if ((rtv2->order - canvas_pointer_y_span) >= visible_y_low) {
2898 int32_t visible_tracks = 0;
2899 while (visible_tracks < canvas_pointer_y_span ) {
2902 while (height_list[rtv2->order - (visible_tracks - n)] == 0) {
2903 /* we're passing through a hidden track */
2908 if (tracks[rtv2->order - (canvas_pointer_y_span - n)] != 0x00) {
2909 clamp_y_axis = true;
2913 clamp_y_axis = true;
2916 } else if (canvas_pointer_y_span < 0) {
2918 /*moving down the canvas*/
2920 if ((rtv2->order - (canvas_pointer_y_span - n)) <= visible_y_high) { // we will overflow
2923 int32_t visible_tracks = 0;
2925 while (visible_tracks > canvas_pointer_y_span ) {
2928 while (height_list[rtv2->order - (visible_tracks - n)] == 0) {
2932 if ( tracks[rtv2->order - ( canvas_pointer_y_span - n)] != 0x00) {
2933 clamp_y_axis = true;
2938 clamp_y_axis = true;
2944 /* this is the pointer's track */
2945 if ((rtv2->order - pointer_y_span) > visible_y_high) { // we will overflow
2946 clamp_y_axis = true;
2947 } else if ((rtv2->order - pointer_y_span) < visible_y_low) { // we will underflow
2948 clamp_y_axis = true;
2956 } else if (drag_info.last_trackview == tv) {
2957 clamp_y_axis = true;
2961 if (!clamp_y_axis) {
2962 drag_info.last_trackview = tv;
2965 /************************************************************
2967 ************************************************************/
2969 /* compute the amount of pointer motion in frames, and where
2970 the region would be if we moved it by that much.
2973 if (drag_info.move_threshold_passed) {
2975 if ((int32_t)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2977 nframes_t sync_frame;
2978 nframes_t sync_offset;
2981 pending_region_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2983 sync_offset = rv->region()->sync_offset (sync_dir);
2984 sync_frame = rv->region()->adjust_to_sync (pending_region_position);
2986 /* we snap if the snap modifier is not enabled.
2989 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2990 snap_to (sync_frame);
2993 if (sync_frame - sync_offset <= sync_frame) {
2994 pending_region_position = sync_frame - (sync_dir*sync_offset);
2996 pending_region_position = 0;
3000 pending_region_position = 0;
3003 if (pending_region_position > max_frames - rv->region()->length()) {
3004 pending_region_position = drag_info.last_frame_position;
3007 // printf ("3: pending_region_position= %lu %lu\n", pending_region_position, drag_info.last_frame_position );
3009 if (pending_region_position != drag_info.last_frame_position && !drag_info.x_constrained) {
3011 /* now compute the canvas unit distance we need to move the regiondrag_info.last_trackview->order
3012 to make it appear at the new location.
3015 if (pending_region_position > drag_info.last_frame_position) {
3016 x_delta = ((double) (pending_region_position - drag_info.last_frame_position) / frames_per_unit);
3018 x_delta = -((double) (drag_info.last_frame_position - pending_region_position) / frames_per_unit);
3021 drag_info.last_frame_position = pending_region_position;
3028 /* threshold not passed */
3033 /*************************************************************
3035 ************************************************************/
3037 if (x_delta == 0 && (pointer_y_span == 0)) {
3038 /* haven't reached next snap point, and we're not switching
3039 trackviews. nothing to do.
3045 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3047 RegionView* rv2 = (*i);
3049 // If any regionview is at zero, we need to know so we can stop further leftward motion.
3051 double ix1, ix2, iy1, iy2;
3052 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3053 rv2->get_canvas_group()->i2w (ix1, iy1);
3062 /*************************************************************
3064 ************************************************************/
3068 if (drag_info.first_move) {
3069 if (drag_info.move_threshold_passed) {
3080 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
3081 const list<RegionView*>& layered_regions = selection->regions.by_layer();
3083 for (list<RegionView*>::const_iterator i = layered_regions.begin(); i != layered_regions.end(); ++i) {
3085 RegionView* rv = (*i);
3086 double ix1, ix2, iy1, iy2;
3087 int32_t temp_pointer_y_span = pointer_y_span;
3089 /* get item BBox, which will be relative to parent. so we have
3090 to query on a child, then convert to world coordinates using
3094 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3095 rv->get_canvas_group()->i2w (ix1, iy1);
3096 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3097 RouteTimeAxisView* canvas_rtv = dynamic_cast<RouteTimeAxisView*>(tvp2);
3098 RouteTimeAxisView* temp_rtv;
3100 if ((pointer_y_span != 0) && !clamp_y_axis) {
3103 for (j = height_list.begin(); j!= height_list.end(); j++) {
3104 if (x == canvas_rtv->order) {
3105 /* we found the track the region is on */
3106 if (x != original_pointer_order) {
3107 /*this isn't from the same track we're dragging from */
3108 temp_pointer_y_span = canvas_pointer_y_span;
3110 while (temp_pointer_y_span > 0) {
3111 /* we're moving up canvas-wise,
3112 so we need to find the next track height
3114 if (j != height_list.begin()) {
3117 if (x != original_pointer_order) {
3118 /* we're not from the dragged track, so ignore hidden tracks. */
3120 temp_pointer_y_span++;
3124 temp_pointer_y_span--;
3126 while (temp_pointer_y_span < 0) {
3128 if (x != original_pointer_order) {
3130 temp_pointer_y_span--;
3134 if (j != height_list.end()) {
3137 temp_pointer_y_span++;
3139 /* find out where we'll be when we move and set height accordingly */
3141 tvp2 = trackview_by_y_position (iy1 + y_delta);
3142 temp_rtv = dynamic_cast<RouteTimeAxisView*>(tvp2);
3143 rv->set_height (temp_rtv->height);
3145 /* if you un-comment the following, the region colours will follow the track colours whilst dragging,
3146 personally, i think this can confuse things, but never mind.
3149 //const GdkColor& col (temp_rtv->view->get_region_color());
3150 //rv->set_color (const_cast<GdkColor&>(col));
3157 /* prevent the regionview from being moved to before
3158 the zero position on the canvas.
3163 if (-x_delta > ix1) {
3166 } else if ((x_delta > 0) &&(rv->region()->last_frame() > max_frames - x_delta)) {
3167 x_delta = max_frames - rv->region()->last_frame();
3170 if (drag_info.first_move) {
3172 /* hide any dependent views */
3174 rv->get_time_axis_view().hide_dependent_views (*rv);
3176 /* this is subtle. raising the regionview itself won't help,
3177 because raise_to_top() just puts the item on the top of
3178 its parent's stack. so, we need to put the trackview canvas_display group
3179 on the top, since its parent is the whole canvas.
3182 rv->get_canvas_group()->raise_to_top();
3183 rv->get_time_axis_view().canvas_display->raise_to_top();
3184 cursor_group->raise_to_top();
3186 rv->fake_set_opaque (true);
3189 if (drag_info.brushing) {
3190 mouse_brush_insert_region (rv, pending_region_position);
3192 rv->move (x_delta, y_delta);
3195 } /* foreach region */
3199 if (drag_info.first_move && drag_info.move_threshold_passed) {
3200 cursor_group->raise_to_top();
3201 drag_info.first_move = false;
3204 if (x_delta != 0 && !drag_info.brushing) {
3205 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3210 Editor::region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3213 RegionView* rv = reinterpret_cast<RegionView *> (drag_info.data);
3214 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
3215 bool nocommit = true;
3217 RouteTimeAxisView* rtv;
3218 bool regionview_y_movement;
3219 bool regionview_x_movement;
3220 vector<RegionView*> copies;
3222 /* first_move is set to false if the regionview has been moved in the
3226 if (drag_info.first_move && !(drag_info.copy && drag_info.x_constrained)) {
3233 /* The regionview has been moved at some stage during the grab so we need
3234 to account for any mouse movement between this event and the last one.
3237 region_drag_motion_callback (item, event);
3239 if (drag_info.brushing) {
3240 /* all changes were made during motion event handlers */
3242 if (drag_info.copy) {
3243 for (list<RegionView*>::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
3244 copies.push_back (*i);
3251 /* adjust for track speed */
3254 rtv = dynamic_cast<RouteTimeAxisView*> (drag_info.last_trackview);
3255 if (rtv && rtv->get_diskstream()) {
3256 speed = rtv->get_diskstream()->speed();
3259 regionview_x_movement = (drag_info.last_frame_position != (nframes_t) (rv->region()->position()/speed));
3260 regionview_y_movement = (drag_info.last_trackview != &rv->get_time_axis_view());
3262 //printf ("last_frame: %s position is %lu %g\n", rv->get_time_axis_view().name().c_str(), drag_info.last_frame_position, speed);
3263 //printf ("last_rackview: %s \n", drag_info.last_trackview->name().c_str());
3267 if (drag_info.copy) {
3268 if (drag_info.x_constrained) {
3269 op_string = _("fixed time region copy");
3271 op_string = _("region copy");
3274 if (drag_info.x_constrained) {
3275 op_string = _("fixed time region drag");
3277 op_string = _("region drag");
3281 begin_reversible_command (op_string);
3283 if (regionview_y_movement) {
3285 /* moved to a different audio track. */
3287 vector<RegionView*> new_selection;
3289 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ) {
3291 RegionView* rv = (*i);
3293 double ix1, ix2, iy1, iy2;
3295 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3296 rv->get_canvas_group()->i2w (ix1, iy1);
3297 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3298 RouteTimeAxisView* rtv2 = dynamic_cast<RouteTimeAxisView*>(tvp2);
3300 boost::shared_ptr<Playlist> from_playlist = rv->region()->playlist();
3301 boost::shared_ptr<Playlist> to_playlist = rtv2->playlist();
3303 where = (nframes_t) (unit_to_frame (ix1) * speed);
3304 boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region()));
3306 /* undo the previous hide_dependent_views so that xfades don't
3307 disappear on copying regions
3310 rv->get_time_axis_view().reveal_dependent_views (*rv);
3312 if (!drag_info.copy) {
3314 /* the region that used to be in the old playlist is not
3315 moved to the new one - we make a copy of it. as a result,
3316 any existing editor for the region should no longer be
3320 rv->hide_region_editor();
3321 rv->fake_set_opaque (false);
3323 session->add_command (new MementoCommand<Playlist>(*from_playlist, &from_playlist->get_state(), 0));
3324 from_playlist->remove_region ((rv->region()));
3325 session->add_command (new MementoCommand<Playlist>(*from_playlist, 0, &from_playlist->get_state()));
3329 /* the regionview we dragged around is a temporary copy, queue it for deletion */
3331 copies.push_back (rv);
3334 latest_regionview = 0;
3336 sigc::connection c = rtv2->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3337 session->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
3338 to_playlist->add_region (new_region, where);
3339 session->add_command (new MementoCommand<Playlist>(*to_playlist, 0, &to_playlist->get_state()));
3342 if (latest_regionview) {
3343 new_selection.push_back (latest_regionview);
3346 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
3347 was selected in all of them, then removing it from the playlist will have removed all
3348 trace of it from the selection (i.e. there were N regions selected, we removed 1,
3349 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
3350 corresponding regionview, and the selection is now empty).
3352 this could have invalidated any and all iterators into the region selection.
3354 the heuristic we use here is: if the region selection is empty, break out of the loop
3355 here. if the region selection is not empty, then restart the loop because we know that
3356 we must have removed at least the region(view) we've just been working on as well as any
3357 that we processed on previous iterations.
3359 EXCEPT .... if we are doing a copy drag, then the selection hasn't been modified and
3360 we can just iterate.
3363 if (drag_info.copy) {
3366 if (selection->regions.empty()) {
3369 i = selection->regions.by_layer().begin();
3374 selection->set (new_selection);
3378 /* motion within a single track */
3380 list<RegionView*> regions = selection->regions.by_layer();
3382 if (drag_info.copy) {
3383 selection->clear_regions();
3386 for (list<RegionView*>::iterator i = regions.begin(); i != regions.end(); ++i) {
3390 if (rv->region()->locked()) {
3395 if (regionview_x_movement) {
3396 double ownspeed = 1.0;
3397 rtv = dynamic_cast<RouteTimeAxisView*> (&(rv->get_time_axis_view()));
3399 if (rtv && rtv->get_diskstream()) {
3400 ownspeed = rtv->get_diskstream()->speed();
3403 /* base the new region position on the current position of the regionview.*/
3405 double ix1, ix2, iy1, iy2;
3407 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3408 rv->get_canvas_group()->i2w (ix1, iy1);
3409 where = (nframes_t) (unit_to_frame (ix1) * ownspeed);
3413 where = rv->region()->position();
3416 boost::shared_ptr<Playlist> to_playlist = rv->region()->playlist();
3418 assert (to_playlist);
3422 session->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
3424 if (drag_info.copy) {
3426 boost::shared_ptr<Region> newregion;
3427 boost::shared_ptr<Region> ar;
3429 if ((ar = boost::dynamic_pointer_cast<AudioRegion>(rv->region())) != 0) {
3430 newregion = RegionFactory::create (ar);
3432 /* XXX MIDI HERE drobilla */
3438 latest_regionview = 0;
3439 sigc::connection c = rtv->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3440 to_playlist->add_region (newregion, (nframes_t) (where * rtv->get_diskstream()->speed()));
3443 if (latest_regionview) {
3444 rtv->reveal_dependent_views (*latest_regionview);
3445 selection->add (latest_regionview);
3448 /* if the original region was locked, we don't care for the new one */
3450 newregion->set_locked (false);
3454 /* just change the model */
3456 rv->region()->set_position (where, (void*) this);
3462 session->add_command (new MementoCommand<Playlist>(*to_playlist, 0, &to_playlist->get_state()));
3464 if (drag_info.copy) {
3465 copies.push_back (rv);
3473 commit_reversible_command ();
3476 for (vector<RegionView*>::iterator x = copies.begin(); x != copies.end(); ++x) {
3482 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
3484 /* Either add to or set the set the region selection, unless
3485 this is an alignment click (control used)
3488 if (Keyboard::modifier_state_contains (event->state, Keyboard::Control)) {
3489 TimeAxisView* tv = &rv.get_time_axis_view();
3490 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
3492 if (rtv && rtv->is_track()) {
3493 speed = rtv->get_diskstream()->speed();
3496 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Alt))) {
3498 align_region (rv.region(), SyncPoint, (nframes_t) (edit_cursor->current_frame * speed));
3500 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Shift))) {
3502 align_region (rv.region(), End, (nframes_t) (edit_cursor->current_frame * speed));
3506 align_region (rv.region(), Start, (nframes_t) (edit_cursor->current_frame * speed));
3512 Editor::show_verbose_time_cursor (nframes_t frame, double offset, double xpos, double ypos)
3518 nframes_t frame_rate;
3525 switch (Profile->get_small_screen() ? ARDOUR_UI::instance()->primary_clock.mode () : ARDOUR_UI::instance()->secondary_clock.mode ()) {
3526 case AudioClock::BBT:
3527 session->bbt_time (frame, bbt);
3528 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, bbt.bars, bbt.beats, bbt.ticks);
3531 case AudioClock::SMPTE:
3532 session->smpte_time (frame, smpte);
3533 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
3536 case AudioClock::MinSec:
3537 /* XXX this is copied from show_verbose_duration_cursor() */
3538 frame_rate = session->frame_rate();
3539 hours = frame / (frame_rate * 3600);
3540 frame = frame % (frame_rate * 3600);
3541 mins = frame / (frame_rate * 60);
3542 frame = frame % (frame_rate * 60);
3543 secs = (float) frame / (float) frame_rate;
3544 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
3548 snprintf (buf, sizeof(buf), "%u", frame);
3552 if (xpos >= 0 && ypos >=0) {
3553 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
3556 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
3558 show_verbose_canvas_cursor ();
3562 Editor::show_verbose_duration_cursor (nframes_t start, nframes_t end, double offset, double xpos, double ypos)
3569 nframes_t distance, frame_rate;
3571 Meter meter_at_start(session->tempo_map().meter_at(start));
3577 switch (ARDOUR_UI::instance()->secondary_clock.mode ()) {
3578 case AudioClock::BBT:
3579 session->bbt_time (start, sbbt);
3580 session->bbt_time (end, ebbt);
3583 /* XXX this computation won't work well if the
3584 user makes a selection that spans any meter changes.
3587 ebbt.bars -= sbbt.bars;
3588 if (ebbt.beats >= sbbt.beats) {
3589 ebbt.beats -= sbbt.beats;
3592 ebbt.beats = int(meter_at_start.beats_per_bar()) + ebbt.beats - sbbt.beats;
3594 if (ebbt.ticks >= sbbt.ticks) {
3595 ebbt.ticks -= sbbt.ticks;
3598 ebbt.ticks = int(Meter::ticks_per_beat) + ebbt.ticks - sbbt.ticks;
3601 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, ebbt.bars, ebbt.beats, ebbt.ticks);
3604 case AudioClock::SMPTE:
3605 session->smpte_duration (end - start, smpte);
3606 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
3609 case AudioClock::MinSec:
3610 /* XXX this stuff should be elsewhere.. */
3611 distance = end - start;
3612 frame_rate = session->frame_rate();
3613 hours = distance / (frame_rate * 3600);
3614 distance = distance % (frame_rate * 3600);
3615 mins = distance / (frame_rate * 60);
3616 distance = distance % (frame_rate * 60);
3617 secs = (float) distance / (float) frame_rate;
3618 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
3622 snprintf (buf, sizeof(buf), "%u", end - start);
3626 if (xpos >= 0 && ypos >=0) {
3627 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
3630 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
3632 show_verbose_canvas_cursor ();
3636 Editor::collect_new_region_view (RegionView* rv)
3638 latest_regionview = rv;
3642 Editor::start_selection_grab (ArdourCanvas::Item* item, GdkEvent* event)
3644 if (clicked_regionview == 0) {
3648 /* lets try to create new Region for the selection */
3650 vector<boost::shared_ptr<AudioRegion> > new_regions;
3651 create_region_from_selection (new_regions);
3653 if (new_regions.empty()) {
3657 /* XXX fix me one day to use all new regions */
3659 boost::shared_ptr<Region> region (new_regions.front());
3661 /* add it to the current stream/playlist.
3663 tricky: the streamview for the track will add a new regionview. we will
3664 catch the signal it sends when it creates the regionview to
3665 set the regionview we want to then drag.
3668 latest_regionview = 0;
3669 sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3671 /* A selection grab currently creates two undo/redo operations, one for
3672 creating the new region and another for moving it.
3675 begin_reversible_command (_("selection grab"));
3677 boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
3679 XMLNode *before = &(playlist->get_state());
3680 clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
3681 XMLNode *after = &(playlist->get_state());
3682 session->add_command(new MementoCommand<Playlist>(*playlist, before, after));
3684 commit_reversible_command ();
3688 if (latest_regionview == 0) {
3689 /* something went wrong */
3693 /* we need to deselect all other regionviews, and select this one
3694 i'm ignoring undo stuff, because the region creation will take care of it */
3695 selection->set (latest_regionview);
3697 drag_info.item = latest_regionview->get_canvas_group();
3698 drag_info.data = latest_regionview;
3699 drag_info.motion_callback = &Editor::region_drag_motion_callback;
3700 drag_info.finished_callback = &Editor::region_drag_finished_callback;
3704 drag_info.last_trackview = clicked_axisview;
3705 drag_info.last_frame_position = latest_regionview->region()->position();
3706 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
3708 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3712 Editor::cancel_selection ()
3714 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
3715 (*i)->hide_selection ();
3717 begin_reversible_command (_("cancel selection"));
3718 selection->clear ();
3719 clicked_selection = 0;
3720 commit_reversible_command ();
3724 Editor::start_selection_op (ArdourCanvas::Item* item, GdkEvent* event, SelectionOp op)
3726 nframes_t start = 0;
3733 drag_info.item = item;
3734 drag_info.motion_callback = &Editor::drag_selection;
3735 drag_info.finished_callback = &Editor::end_selection_op;
3740 case CreateSelection:
3741 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
3742 drag_info.copy = true;
3744 drag_info.copy = false;
3746 start_grab (event, selector_cursor);
3749 case SelectionStartTrim:
3750 if (clicked_axisview) {
3751 clicked_axisview->order_selection_trims (item, true);
3753 start_grab (event, trimmer_cursor);
3754 start = selection->time[clicked_selection].start;
3755 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
3758 case SelectionEndTrim:
3759 if (clicked_axisview) {
3760 clicked_axisview->order_selection_trims (item, false);
3762 start_grab (event, trimmer_cursor);
3763 end = selection->time[clicked_selection].end;
3764 drag_info.pointer_frame_offset = drag_info.grab_frame - end;
3768 start = selection->time[clicked_selection].start;
3770 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
3774 if (selection_op == SelectionMove) {
3775 show_verbose_time_cursor(start, 10);
3777 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
3782 Editor::drag_selection (ArdourCanvas::Item* item, GdkEvent* event)
3784 nframes_t start = 0;
3787 nframes_t pending_position;
3789 if ((int32_t) drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
3790 pending_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
3793 pending_position = 0;
3796 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3797 snap_to (pending_position);
3800 /* only alter selection if the current frame is
3801 different from the last frame position (adjusted)
3804 if (pending_position == drag_info.last_pointer_frame) return;
3806 switch (selection_op) {
3807 case CreateSelection:
3809 if (drag_info.first_move) {
3810 snap_to (drag_info.grab_frame);
3813 if (pending_position < drag_info.grab_frame) {
3814 start = pending_position;
3815 end = drag_info.grab_frame;
3817 end = pending_position;
3818 start = drag_info.grab_frame;
3821 /* first drag: Either add to the selection
3822 or create a new selection->
3825 if (drag_info.first_move) {
3827 begin_reversible_command (_("range selection"));
3829 if (drag_info.copy) {
3830 /* adding to the selection */
3831 clicked_selection = selection->add (start, end);
3832 drag_info.copy = false;
3834 /* new selection-> */
3835 clicked_selection = selection->set (clicked_axisview, start, end);
3840 case SelectionStartTrim:
3842 if (drag_info.first_move) {
3843 begin_reversible_command (_("trim selection start"));
3846 start = selection->time[clicked_selection].start;
3847 end = selection->time[clicked_selection].end;
3849 if (pending_position > end) {
3852 start = pending_position;
3856 case SelectionEndTrim:
3858 if (drag_info.first_move) {
3859 begin_reversible_command (_("trim selection end"));
3862 start = selection->time[clicked_selection].start;
3863 end = selection->time[clicked_selection].end;
3865 if (pending_position < start) {
3868 end = pending_position;
3875 if (drag_info.first_move) {
3876 begin_reversible_command (_("move selection"));
3879 start = selection->time[clicked_selection].start;
3880 end = selection->time[clicked_selection].end;
3882 length = end - start;
3884 start = pending_position;
3887 end = start + length;
3892 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
3893 start_canvas_autoscroll (1);
3897 selection->replace (clicked_selection, start, end);
3900 drag_info.last_pointer_frame = pending_position;
3901 drag_info.first_move = false;
3903 if (selection_op == SelectionMove) {
3904 show_verbose_time_cursor(start, 10);
3906 show_verbose_time_cursor(pending_position, 10);
3911 Editor::end_selection_op (ArdourCanvas::Item* item, GdkEvent* event)
3913 if (!drag_info.first_move) {
3914 drag_selection (item, event);
3915 /* XXX this is not object-oriented programming at all. ick */
3916 if (selection->time.consolidate()) {
3917 selection->TimeChanged ();
3919 commit_reversible_command ();
3921 /* just a click, no pointer movement.*/
3923 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
3925 selection->clear_time();
3930 /* XXX what happens if its a music selection? */
3931 session->set_audio_range (selection->time);
3932 stop_canvas_autoscroll ();
3936 Editor::start_trim (ArdourCanvas::Item* item, GdkEvent* event)
3939 TimeAxisView* tvp = clicked_axisview;
3940 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
3942 if (tv && tv->is_track()) {
3943 speed = tv->get_diskstream()->speed();
3946 nframes_t region_start = (nframes_t) (clicked_regionview->region()->position() / speed);
3947 nframes_t region_end = (nframes_t) (clicked_regionview->region()->last_frame() / speed);
3948 nframes_t region_length = (nframes_t) (clicked_regionview->region()->length() / speed);
3950 //drag_info.item = clicked_regionview->get_name_highlight();
3951 drag_info.item = item;
3952 drag_info.motion_callback = &Editor::trim_motion_callback;
3953 drag_info.finished_callback = &Editor::trim_finished_callback;
3955 start_grab (event, trimmer_cursor);
3957 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
3958 trim_op = ContentsTrim;
3960 /* These will get overridden for a point trim.*/
3961 if (drag_info.current_pointer_frame < (region_start + region_length/2)) {
3962 /* closer to start */
3963 trim_op = StartTrim;
3964 } else if (drag_info.current_pointer_frame > (region_end - region_length/2)) {
3972 show_verbose_time_cursor(region_start, 10);
3975 show_verbose_time_cursor(region_end, 10);
3978 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
3984 Editor::trim_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
3986 RegionView* rv = clicked_regionview;
3987 nframes_t frame_delta = 0;
3988 bool left_direction;
3989 bool obey_snap = !Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier());
3991 /* snap modifier works differently here..
3992 its' current state has to be passed to the
3993 various trim functions in order to work properly
3997 TimeAxisView* tvp = clicked_axisview;
3998 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
3999 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
4001 if (tv && tv->is_track()) {
4002 speed = tv->get_diskstream()->speed();
4005 if (drag_info.last_pointer_frame > drag_info.current_pointer_frame) {
4006 left_direction = true;
4008 left_direction = false;
4012 snap_to (drag_info.current_pointer_frame);
4015 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
4019 if (drag_info.first_move) {
4025 trim_type = "Region start trim";
4028 trim_type = "Region end trim";
4031 trim_type = "Region content trim";
4035 begin_reversible_command (trim_type);
4037 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4038 (*i)->fake_set_opaque(false);
4039 (*i)->region()->freeze ();
4041 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
4043 arv->temporarily_hide_envelope ();
4045 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4046 insert_result = motion_frozen_playlists.insert (pl);
4047 if (insert_result.second) {
4048 session->add_command(new MementoCommand<Playlist>(*pl, &pl->get_state(), 0));
4053 if (left_direction) {
4054 frame_delta = (drag_info.last_pointer_frame - drag_info.current_pointer_frame);
4056 frame_delta = (drag_info.current_pointer_frame - drag_info.last_pointer_frame);
4061 if ((left_direction == false) && (drag_info.current_pointer_frame <= rv->region()->first_frame()/speed)) {
4064 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4065 single_start_trim (**i, frame_delta, left_direction, obey_snap);
4071 if ((left_direction == true) && (drag_info.current_pointer_frame > (nframes_t) (rv->region()->last_frame()/speed))) {
4074 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4075 single_end_trim (**i, frame_delta, left_direction, obey_snap);
4082 bool swap_direction = false;
4084 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
4085 swap_direction = true;
4088 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4089 i != selection->regions.by_layer().end(); ++i)
4091 single_contents_trim (**i, frame_delta, left_direction, swap_direction, obey_snap);
4099 show_verbose_time_cursor((nframes_t) (rv->region()->position()/speed), 10);
4102 show_verbose_time_cursor((nframes_t) (rv->region()->last_frame()/speed), 10);
4105 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4109 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4110 drag_info.first_move = false;
4114 Editor::single_contents_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool swap_direction, bool obey_snap)
4116 boost::shared_ptr<Region> region (rv.region());
4118 if (region->locked()) {
4122 nframes_t new_bound;
4125 TimeAxisView* tvp = clicked_axisview;
4126 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4128 if (tv && tv->is_track()) {
4129 speed = tv->get_diskstream()->speed();
4132 if (left_direction) {
4133 if (swap_direction) {
4134 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4136 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4139 if (swap_direction) {
4140 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4142 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4147 snap_to (new_bound);
4149 region->trim_start ((nframes_t) (new_bound * speed), this);
4150 rv.region_changed (StartChanged);
4154 Editor::single_start_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool obey_snap)
4156 boost::shared_ptr<Region> region (rv.region());
4158 if (region->locked()) {
4162 nframes_t new_bound;
4165 TimeAxisView* tvp = clicked_axisview;
4166 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4168 if (tv && tv->is_track()) {
4169 speed = tv->get_diskstream()->speed();
4172 if (left_direction) {
4173 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4175 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4179 snap_to (new_bound, (left_direction ? 0 : 1));
4182 region->trim_front ((nframes_t) (new_bound * speed), this);
4184 rv.region_changed (Change (LengthChanged|PositionChanged|StartChanged));
4188 Editor::single_end_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool obey_snap)
4190 boost::shared_ptr<Region> region (rv.region());
4192 if (region->locked()) {
4196 nframes_t new_bound;
4199 TimeAxisView* tvp = clicked_axisview;
4200 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4202 if (tv && tv->is_track()) {
4203 speed = tv->get_diskstream()->speed();
4206 if (left_direction) {
4207 new_bound = (nframes_t) ((region->last_frame() + 1)/speed) - frame_delta;
4209 new_bound = (nframes_t) ((region->last_frame() + 1)/speed) + frame_delta;
4213 snap_to (new_bound);
4215 region->trim_end ((nframes_t) (new_bound * speed), this);
4216 rv.region_changed (LengthChanged);
4220 Editor::trim_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
4222 if (!drag_info.first_move) {
4223 trim_motion_callback (item, event);
4225 if (!clicked_regionview->get_selected()) {
4226 thaw_region_after_trim (*clicked_regionview);
4229 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4230 i != selection->regions.by_layer().end(); ++i)
4232 thaw_region_after_trim (**i);
4233 (*i)->fake_set_opaque (true);
4237 for (set<boost::shared_ptr<Playlist> >::iterator p = motion_frozen_playlists.begin(); p != motion_frozen_playlists.end(); ++p) {
4239 session->add_command (new MementoCommand<Playlist>(*(*p).get(), 0, &(*p)->get_state()));
4242 motion_frozen_playlists.clear ();
4244 commit_reversible_command();
4246 /* no mouse movement */
4252 Editor::point_trim (GdkEvent* event)
4254 RegionView* rv = clicked_regionview;
4255 nframes_t new_bound = drag_info.current_pointer_frame;
4257 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4258 snap_to (new_bound);
4261 /* Choose action dependant on which button was pressed */
4262 switch (event->button.button) {
4264 trim_op = StartTrim;
4265 begin_reversible_command (_("Start point trim"));
4267 if (rv->get_selected()) {
4269 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4270 i != selection->regions.by_layer().end(); ++i)
4272 if (!(*i)->region()->locked()) {
4273 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4274 XMLNode &before = pl->get_state();
4275 (*i)->region()->trim_front (new_bound, this);
4276 XMLNode &after = pl->get_state();
4277 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4283 if (!rv->region()->locked()) {
4284 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
4285 XMLNode &before = pl->get_state();
4286 rv->region()->trim_front (new_bound, this);
4287 XMLNode &after = pl->get_state();
4288 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4292 commit_reversible_command();
4297 begin_reversible_command (_("End point trim"));
4299 if (rv->get_selected()) {
4301 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
4303 if (!(*i)->region()->locked()) {
4304 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4305 XMLNode &before = pl->get_state();
4306 (*i)->region()->trim_end (new_bound, this);
4307 XMLNode &after = pl->get_state();
4308 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4314 if (!rv->region()->locked()) {
4315 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
4316 XMLNode &before = pl->get_state();
4317 rv->region()->trim_end (new_bound, this);
4318 XMLNode &after = pl->get_state();
4319 session->add_command (new MementoCommand<Playlist>(*pl.get(), &before, &after));
4323 commit_reversible_command();
4332 Editor::thaw_region_after_trim (RegionView& rv)
4334 boost::shared_ptr<Region> region (rv.region());
4336 if (region->locked()) {
4340 region->thaw (_("trimmed region"));
4341 XMLNode &after = region->playlist()->get_state();
4342 session->add_command (new MementoCommand<Playlist>(*(region->playlist()), 0, &after));
4344 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(&rv);
4346 arv->unhide_envelope ();
4350 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* event)
4355 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
4356 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
4360 Location* location = find_location_from_marker (marker, is_start);
4361 location->set_hidden (true, this);
4366 Editor::start_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event, RangeMarkerOp op)
4372 drag_info.item = item;
4373 drag_info.motion_callback = &Editor::drag_range_markerbar_op;
4374 drag_info.finished_callback = &Editor::end_range_markerbar_op;
4376 range_marker_op = op;
4378 if (!temp_location) {
4379 temp_location = new Location;
4383 case CreateRangeMarker:
4384 case CreateTransportMarker:
4386 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
4387 drag_info.copy = true;
4389 drag_info.copy = false;
4391 start_grab (event, selector_cursor);
4395 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4400 Editor::drag_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4402 nframes_t start = 0;
4404 ArdourCanvas::SimpleRect *crect = (range_marker_op == CreateRangeMarker) ? range_bar_drag_rect: transport_bar_drag_rect;
4406 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4407 snap_to (drag_info.current_pointer_frame);
4410 /* only alter selection if the current frame is
4411 different from the last frame position.
4414 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4416 switch (range_marker_op) {
4417 case CreateRangeMarker:
4418 case CreateTransportMarker:
4419 if (drag_info.first_move) {
4420 snap_to (drag_info.grab_frame);
4423 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4424 start = drag_info.current_pointer_frame;
4425 end = drag_info.grab_frame;
4427 end = drag_info.current_pointer_frame;
4428 start = drag_info.grab_frame;
4431 /* first drag: Either add to the selection
4432 or create a new selection.
4435 if (drag_info.first_move) {
4437 temp_location->set (start, end);
4441 update_marker_drag_item (temp_location);
4442 range_marker_drag_rect->show();
4443 range_marker_drag_rect->raise_to_top();
4449 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
4450 start_canvas_autoscroll (1);
4454 temp_location->set (start, end);
4456 double x1 = frame_to_pixel (start);
4457 double x2 = frame_to_pixel (end);
4458 crect->property_x1() = x1;
4459 crect->property_x2() = x2;
4461 update_marker_drag_item (temp_location);
4464 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4465 drag_info.first_move = false;
4467 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4472 Editor::end_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4474 Location * newloc = 0;
4477 if (!drag_info.first_move) {
4478 drag_range_markerbar_op (item, event);
4480 switch (range_marker_op) {
4481 case CreateRangeMarker:
4483 begin_reversible_command (_("new range marker"));
4484 XMLNode &before = session->locations()->get_state();
4485 session->locations()->next_available_name(rangename,"unnamed");
4486 newloc = new Location(temp_location->start(), temp_location->end(), rangename, Location::IsRangeMarker);
4487 session->locations()->add (newloc, true);
4488 XMLNode &after = session->locations()->get_state();
4489 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
4490 commit_reversible_command ();
4492 range_bar_drag_rect->hide();
4493 range_marker_drag_rect->hide();
4497 case CreateTransportMarker:
4498 // popup menu to pick loop or punch
4499 new_transport_marker_context_menu (&event->button, item);
4504 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
4506 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
4511 start = session->locations()->first_mark_before (drag_info.grab_frame);
4512 end = session->locations()->first_mark_after (drag_info.grab_frame);
4514 if (end == max_frames) {
4515 end = session->current_end_frame ();
4519 start = session->current_start_frame ();
4522 switch (mouse_mode) {
4524 /* find the two markers on either side and then make the selection from it */
4525 select_all_within (start, end, 0.0f, FLT_MAX, track_views, Selection::Set);
4529 /* find the two markers on either side of the click and make the range out of it */
4530 selection->set (0, start, end);
4539 stop_canvas_autoscroll ();
4545 Editor::start_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4547 drag_info.item = item;
4548 drag_info.motion_callback = &Editor::drag_mouse_zoom;
4549 drag_info.finished_callback = &Editor::end_mouse_zoom;
4551 start_grab (event, zoom_cursor);
4553 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4557 Editor::drag_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4562 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4563 snap_to (drag_info.current_pointer_frame);
4565 if (drag_info.first_move) {
4566 snap_to (drag_info.grab_frame);
4570 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4572 /* base start and end on initial click position */
4573 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4574 start = drag_info.current_pointer_frame;
4575 end = drag_info.grab_frame;
4577 end = drag_info.current_pointer_frame;
4578 start = drag_info.grab_frame;
4583 if (drag_info.first_move) {
4585 zoom_rect->raise_to_top();
4588 reposition_zoom_rect(start, end);
4590 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4591 drag_info.first_move = false;
4593 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4598 Editor::end_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4600 if (!drag_info.first_move) {
4601 drag_mouse_zoom (item, event);
4603 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
4604 temporal_zoom_by_frame (drag_info.grab_frame, drag_info.last_pointer_frame, "mouse zoom");
4606 temporal_zoom_by_frame (drag_info.last_pointer_frame, drag_info.grab_frame, "mouse zoom");
4609 temporal_zoom_to_frame (false, drag_info.grab_frame);
4611 temporal_zoom_step (false);
4612 center_screen (drag_info.grab_frame);
4620 Editor::reposition_zoom_rect (nframes_t start, nframes_t end)
4622 double x1 = frame_to_pixel (start);
4623 double x2 = frame_to_pixel (end);
4624 double y2 = full_canvas_height - 1.0;
4626 zoom_rect->property_x1() = x1;
4627 zoom_rect->property_y1() = 1.0;
4628 zoom_rect->property_x2() = x2;
4629 zoom_rect->property_y2() = y2;
4633 Editor::start_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4635 drag_info.item = item;
4636 drag_info.motion_callback = &Editor::drag_rubberband_select;
4637 drag_info.finished_callback = &Editor::end_rubberband_select;
4639 start_grab (event, cross_hair_cursor);
4641 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4645 Editor::drag_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4652 /* use a bigger drag threshold than the default */
4654 if (abs ((int) (drag_info.current_pointer_frame - drag_info.grab_frame)) < 8) {
4658 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4659 if (drag_info.first_move) {
4660 snap_to (drag_info.grab_frame);
4662 snap_to (drag_info.current_pointer_frame);
4665 /* base start and end on initial click position */
4667 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4668 start = drag_info.current_pointer_frame;
4669 end = drag_info.grab_frame;
4671 end = drag_info.current_pointer_frame;
4672 start = drag_info.grab_frame;
4675 if (drag_info.current_pointer_y < drag_info.grab_y) {
4676 y1 = drag_info.current_pointer_y;
4677 y2 = drag_info.grab_y;
4679 y2 = drag_info.current_pointer_y;
4680 y1 = drag_info.grab_y;
4684 if (start != end || y1 != y2) {
4686 double x1 = frame_to_pixel (start);
4687 double x2 = frame_to_pixel (end);
4689 rubberband_rect->property_x1() = x1;
4690 rubberband_rect->property_y1() = y1;
4691 rubberband_rect->property_x2() = x2;
4692 rubberband_rect->property_y2() = y2;
4694 rubberband_rect->show();
4695 rubberband_rect->raise_to_top();
4697 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4698 drag_info.first_move = false;
4700 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4705 Editor::end_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4707 if (!drag_info.first_move) {
4709 drag_rubberband_select (item, event);
4712 if (drag_info.current_pointer_y < drag_info.grab_y) {
4713 y1 = drag_info.current_pointer_y;
4714 y2 = drag_info.grab_y;
4717 y2 = drag_info.current_pointer_y;
4718 y1 = drag_info.grab_y;
4722 Selection::Operation op = Keyboard::selection_type (event->button.state);
4725 begin_reversible_command (_("rubberband selection"));
4727 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
4728 commit = select_all_within (drag_info.grab_frame, drag_info.last_pointer_frame, y1, y2, track_views, op);
4730 commit = select_all_within (drag_info.last_pointer_frame, drag_info.grab_frame, y1, y2, track_views, op);
4734 commit_reversible_command ();
4738 selection->clear_tracks();
4739 selection->clear_regions();
4740 selection->clear_points ();
4741 selection->clear_lines ();
4744 rubberband_rect->hide();
4749 Editor::mouse_rename_region (ArdourCanvas::Item* item, GdkEvent* event)
4751 using namespace Gtkmm2ext;
4753 ArdourPrompter prompter (false);
4755 prompter.set_prompt (_("Name for region:"));
4756 prompter.set_initial_text (clicked_regionview->region()->name());
4757 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
4758 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
4759 prompter.show_all ();
4760 switch (prompter.run ()) {
4761 case Gtk::RESPONSE_ACCEPT:
4763 prompter.get_result(str);
4765 clicked_regionview->region()->set_name (str);
4773 Editor::start_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
4775 drag_info.item = item;
4776 drag_info.motion_callback = &Editor::time_fx_motion;
4777 drag_info.finished_callback = &Editor::end_time_fx;
4781 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4785 Editor::time_fx_motion (ArdourCanvas::Item *item, GdkEvent* event)
4787 RegionView* rv = clicked_regionview;
4789 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4790 snap_to (drag_info.current_pointer_frame);
4793 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
4797 if (drag_info.current_pointer_frame > rv->region()->position()) {
4798 rv->get_time_axis_view().show_timestretch (rv->region()->position(), drag_info.current_pointer_frame);
4801 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4802 drag_info.first_move = false;
4804 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4808 Editor::end_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
4810 clicked_regionview->get_time_axis_view().hide_timestretch ();
4812 if (drag_info.first_move) {
4816 nframes_t newlen = drag_info.last_pointer_frame - clicked_regionview->region()->position();
4817 float percentage = (float) ((double) newlen - (double) clicked_regionview->region()->length()) / ((double) newlen) * 100.0f;
4819 begin_reversible_command (_("timestretch"));
4821 if (run_timestretch (selection->regions, percentage) == 0) {
4822 session->commit_reversible_command ();
4827 Editor::mouse_brush_insert_region (RegionView* rv, nframes_t pos)
4829 /* no brushing without a useful snap setting */
4832 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
4835 switch (snap_mode) {
4837 return; /* can't work because it allows region to be placed anywhere */
4842 switch (snap_type) {
4845 case SnapToEditCursor:
4852 /* don't brush a copy over the original */
4854 if (pos == rv->region()->position()) {
4858 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&arv->get_time_axis_view());
4860 if (rtv == 0 || !rtv->is_track()) {
4864 boost::shared_ptr<Playlist> playlist = rtv->playlist();
4865 double speed = rtv->get_diskstream()->speed();
4867 XMLNode &before = playlist->get_state();
4868 playlist->add_region (boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (arv->audio_region())), (nframes_t) (pos * speed));
4869 XMLNode &after = playlist->get_state();
4870 session->add_command(new MementoCommand<Playlist>(*playlist.get(), &before, &after));
4872 // playlist is frozen, so we have to update manually
4874 playlist->Modified(); /* EMIT SIGNAL */
4878 Editor::track_height_step_timeout ()
4881 struct timeval delta;
4883 gettimeofday (&now, 0);
4884 timersub (&now, &last_track_height_step_timestamp, &delta);
4886 if (delta.tv_sec * 1000000 + delta.tv_usec > 250000) { /* milliseconds */
4887 current_stepping_trackview = 0;